已经完成的用户的基本实现,那接下来我们要进行下订单方面的。
订单也是要等我要做的时候,才发现挺复杂的,我初期的设想是不管我是通过 UID 或者订单 ID 来进行查询,都能达到 O(1),但设计的时候就开始困扰了。用户表我们知道 UID 可以直接解析出数据存在哪个表,但 UID 和订单 ID 两个都要能直接解析的时候,要怎么处理呢?
雪花算法也很难直接处理,群友推荐我可以把解析分成两块,这样子从全部表查降低成了几个表查,不过这块的实现我也没有验证过。
后面我的想法还是按照用户的 ID 生成方式,但是提取则不是按照顺序了,取最前面 余数 = UID % 订单表数
的数,但这里有存在判断是挺麻烦的,我不清楚能不能通过 Lua 来循环处理,但预生成的数量要多少合适呢?会不会影响到表与表之间数量差距较大然后无法取到值呢?
本来已经放弃了的,无意看到腾讯技术工程新发的 分布式唯一 ID 生成方案浅谈,让我想到可以在获取的时候才进行自增,因为 Redis 是原子性的,所以不会存在 ID 重复的情况。
特意查了下,Redis 的 int 可以存储 2^63 - 1
, 9223372036854775807 这么多。 – What happens when Int64 maxvalue is exceded with Redis INCR
当然,这块也可以用 MySQL 来实现,修改每个表的 AUTO_INCREMENT 起始值,还有每次自增的量。
首先我们要先写个脚本来初始化我们的订单发号器,我这里分表后面的值是转换成了十六进制的,这个要注意下,我就把自己坑了。
app/command/OrderIdInit.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
| <?php declare (strict_types = 1);
namespace app\command;
use think\console\Command; use think\console\Input; use think\console\input\Argument; use think\console\input\Option; use think\console\Output;
class OrderIdInit extends Command { protected $redis;
protected $cache_key = 'order:generate_id';
protected $lock_key = 'order:init_lock';
public function __construct() { $this->redis = get_redis();
parent::__construct(); }
protected function configure() { $this->setName('order:id_init') ->addOption('number', null, Option::VALUE_REQUIRED, '表数', 32) ->setDescription('订单 ID 生成初始化'); }
protected function execute(Input $input, Output $output) { $number = (int) $input->getOption('number');
$lock = $this->redis->executeRaw([ 'SET', $this->lock_key, 1, 'EX', 10 * 60, 'NX', ]);
if($lock !== 'OK') { $output->writeln('获取锁失败');
return; }
try { for($i = 0; $i < $number; $i++) { $v = sprintf('%x', $i);
$this->redis->executeRaw([ 'HSETNX', $this->cache_key, $v, $i, ]); } } catch (\Exception $e) { $output->error($e->getMessage()); } finally { $this->redis->del($this->lock_key); } } }
|
开始插入订单表
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| public function store(Request $request) { $uid = $request->uid;
$table_name = table_name('order', $uid);
$table_index = explode('_', $table_name)[1];
$id = get_redis()->executeRaw([ 'HINCRBY', 'order:generate_id', $table_index, 32, // 分表数 ]);
if(!$id) { return '获取 ID 异常'; }
Db::table($table_name)->insert([ 'id' => $id, 'uid' => $uid, 'amount' => random_int(1, 99999), ]); }
|
根据订单号查询
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public function show(Request $request) { $uid = $request->uid;
$id = $request->id;
$table_name = table_name('order', $uid);
$data = Db::table($table_name) ->where('id', $id) ->where('uid', $uid) ->find(); }
|
查询当前用户全部订单
1 2 3 4 5 6 7 8 9 10 11
| public function index(Request $request) { $uid = $request->uid;
$table_name = table_name('order', $uid);
$data = Db::table($table_name) ->where('uid', $uid) ->select(); }
|