批量更新时更新分布式ID

很久没更新了,惭愧,今日刚好又学了一新招,特此记录。

以前写过一篇文章,《分布式环境下创建唯一ID》。在那个时候,最终定下来的ID组成结构是:

time    |     sequence   |    index

   32                19                   13

头32位是时间戳;

中间19位是递增序号,每秒重置;

最后13位是固定的序号;


由于原来只考虑了INSERT,UPDATE因为有批量操作所以并没有让UPDATE时更新这一唯一ID,否则同样where出来的结果集去更新该ID,肯定报duplicate primary key。

为了让批量UPDATE时,为每一条更新的row,自动生成新的唯一ID,在现有架构下,只能从数据库找办法了。


假定Test表表结构如下:

FV | F1 | F2

其中FV即是全schema唯一ID。该语句找到F1字段值为2的批量更新FV和F2。

执行后,看到where结果集中,F2全部变成相同的时间戳值。而FV完全没有重复。

取更新时间即为

SELECT FROM_UNIXTIME(FV>>32) FROM Test;


于是了这样一条语句:

UPDATE Test t, (SELECT @count:=0)b 
SET FV=(UNIX_TIMESTAMP(CURRENT_TIMESTAMP)<<32 | (@count:=@count+1)<<13 | 97) , F2=(UNIX_TIMESTAMP(CURRENT_TIMESTAMP)<<32) 
WHERE t.F1='2';

这里@count的值,每一次执行SQL时,会重置为0,所以基本上不用担心溢出(19位应该够用了,不会一次性更新这么多)。

序号的话,不同的写入节点分配不同的序号,每次生产SQL的时候,动态拼接就好了。

但是想想,还是不对,同一个节点下两个不同线程如果刚好执行一个相同where语句,那么彼此@count不共享,都是从0开始,依然会造成FV相同。


还好mysql有个UUID_SHORT()的方法,通过以下方式生成唯一键

  (server_id & 255) << 56
+ (server_startup_time_in_seconds << 24)
+ incremented_variable++;
server_id是数据库instance的编号,不能超过255。

这个UUID_SHORT()可以产生一个64位的全库唯一ID,刚好可以用,考虑到它的低位都是递增的,所以并不需要取完整的64位,只需要取后面部分的值就行了,即

UPDATE Test SET FV=(UNIX_TIMESTAMP(CURRENT_TIMESTAMP)<<32 | right(UUID_SHORT(),8)) where F1='2'

这里取字符串后面8位的值,拼接到低32位,十进制8位最大值99999999,26位即可满足,所以也不会溢出。

这个方案连低13位的序号都省了。完全可以满足分布式的需求。


另外简单做了下压力测试,结果还可以。不过仍需长期观察下有没有其他影响。




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值