首页 > 随笔> 关于订单号的生成

关于订单号的生成

2015-08-14 13:03:02 发表评论 阅读评论 1817 人阅读    

最近面试某公司的时候被问到这个,一开始想法是以流水叠加的方式,借助redis的incr生成一个流水订单号,可以增加前缀。如:2015081400001,后面五位数进行递增。


面试官:如果不想让别人根据订单号来统计流水呢。

这时候第一想法是每日定时生成一批订单,然后存放在redis/mysql,使用的时候进行获取即可。但是仍然无法解决随机获取的问题,随机key的话又要考虑互斥问题。所以想到使用incr的进行random 1-5之间的递增。然后以index去获取order id。


面试官:如果要保证这批订单号都能使用上呢。

对于最后的这个问题当时并没有答得出来。

之后问了下面试官,答题是跟我的思路类似,也是使用incr做index,当时没有想明白。因为按照好我的方式话保证不了随机获取,而且所有的订单号都能获取到。

==================================================================================================

晚上回来之后突然想到,其实思路没问题的,只是纠结在存在redis/mysql是顺序生成的订单号,所以获取的时候也只能顺序获取。mysql倒是有rand()函数,但是性能不好,而且会导致索引失效。 但其实可以生成订单号的时候程序可以进行打乱这批订单号再存储!这样就可以解决这个问题了。

直接上代码

<?php
// 每日生成的最大订单流水号, 可以根据实际应用进行调整
define('NUM', 1000);

// 链接redis
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);

$order = null;
$pre = date('Ymd');

// 生成订单流水数组
$arr = range(0, NUM);

// 数组乱序
shuffle($arr);

$redis->delete('order');

// 插入订单号进入redis
foreach ($arr as $i) {
    $order = $pre . str_pad($i, 5, 0, STR_PAD_LEFT);
    $redis->rPush('order', $order);
}


// 获取订单号
$orders = [];
for($i = 0; $i < 10000; $i++) {
    $orders[] = $redis->lPop('order');
}
var_dump($orders);



因个人爱好,我又用Golang实现了一遍该功能,对比发现,PHP果然是世界上最好的语言。
package main

import (
	"fmt"
	"math/rand"
	"strconv"
	"time"

	"github.com/garyburd/redigo/redis"
)

const (
	// day order max number
	NUM = 10000
)

func main() {
	pre := time.Now().Format("20060102")

	c, err := redis.Dial("tcp", "127.0.0.1:6379")
	if err != nil {
		panic(err)
	}
	defer c.Close()

	c.Do("SELECT", 3)

	var order [NUM]string
	for i := 0; i < NUM; i++ {
		// generate order id
		order[i] = fmt.Sprintf("%s%s", pre, strPad(strconv.Itoa(i), 5, "0"))
	}

	// random numbers 0 - NUM
	r1 := rand.New(rand.NewSource(time.Now().UnixNano()))

	// shuffle array
	for k, v := range order {
		k2 := r1.Intn(NUM)
		order[k] = order[k2]
		order[k2] = v
	}

	c.Send("MULTI")
	c.Send("DEL", "goOrder")
	for _, v := range order {
		c.Send("RPUSH", "goOrder", v)
	}
	c.Do("EXEC")
}

// add pre zore
func strPad(s string, length int, p string) string {
	if len(s) >= length {
		return s
	}

	padLen := length - len(s)
	for i := 0; i < padLen; i++ {
		s = fmt.Sprintf("%s%s", p, s)
	}

	return s
}


分类: 随笔 标签: 订单号生成
comments powered by Disqus