高并发网站如何解决数据库主键自增的时候出现重复?
时间:2021-07-01 10:21:17
帮助过:23人阅读
这种问题应该在各种流量极大的微博,论坛,贴吧等地方都会碰到的吧!
回复内容:
全局id生成器。
我们日订单也有一万,说多不多说少不少,当然比起贴吧微博不在一个量级。
改天介绍一下
--------------------------2015/5/27
1 设置主键自增为何不可取
这样的话,数据库本身是单点,不可拆库,因为id会重复。
2 依赖数据库自增机制达到全局ID唯一
使用如下语句:
REPLACE INTO Tickets64 (stub) VALUES ('a');
SELECT LAST_INSERT_ID();
这样可以保证全局ID唯一,但这个Tickets64表依旧是个单点。
3 依赖数据库自增机制达到全局ID唯一并消除单点
在2的基础上,部署两个(多个)数据库实例,
设置自增步长为2(多个则为实例数),即auto-increment-increment = 2
设置auto-increment-offset分别为1,2.....
这样第一台数据库服务器的自增id为 1 3 5 7 9
第二台为2 4 6 8 10
4 解决每次请求全局ID都读库写库压力过大的问题
比如第一次启动业务服务,会请求一个唯一id为3559
如果是2、3的方法,则id为3559,这样每次都请求数据库,对数据库压力比较大
可以用3559 * 65536(举个例子,并不一定是65536)+ 内存自增变量来作为id
当内存自增变量到达65535时,从数据库重新获取一个自增id
这样即使有多台业务服务器,id也不会重复:
第一台 3559 * 65536 + 1,2,3.....65535
第二台 3560 * 65536 + 1,2,3.....65535
然后第一台到65535了,换一个数据库自增id,这时候可能是3561 * 65536 + 1,2,3....
我们目前采用4
Sharding & IDs at Instagram
里面提到instagram是怎么生成uuid的,微信朋友圈也是用类似的方法。
里面提到了这个服务 twitter/snowflake · GitHub用于生成uuid.
很多方法,举几个栗子:
1、用UUID而非自增字段。这个主要的阻碍在于编程麻烦一点,很多框架不支持,程序员的智力理解不了的也遇到过。另外UUID不可迷信,其实也有出现过UUID撞车的。
2、全局用一个唯一id生成服务,Google公开的架构文档,有介绍他们用一个单独的oracle节点生成全局唯一自增id。买个 Oracle 就为了一个 sequnece ,这脑洞也确实有点大。有钱任性。
3、用一些协商算法,保证每个节点的自增id跟别的节点不冲突。缺点是很难找到一个扩展性良好的算法。
说到GUID占用空间比较大,我也觉得的确应该找个方法弄一下,于是用Go语言写了个函数放到Github上了,etworker/idxgen · GitHub。
目的就是将每个字符的可选范围从GUID的[0-9a-z]只有16个,变成[0-9a-zA-Z_-]共64个。
权衡之后,选择了8位长度,虽然空间比GUID的小很多,但是实测效果还行,测试时候跑了几个1千万的生成,在我的破笔记本上耗时约30s,偶尔会有1次重复。如果担心唯一性的,就自己实现里面的IsUniqueIdx()函数就可以了。
对了,我把这个东西塞进Docker,部署到云雀里面了,地址是http://idxgen-etworker.alaudacn.me:49662/,刷新一次就出来一个新的ID,不妨试试。
Snowflake
主要思路就是“怎么在短时间内生成多个ID不重复”。
利用数据库写入时的自增能实现不重复,但是由于并发量过大会造成延时;
将自增的量直接写在内存或缓存中能加快生成速度,但是可能会掉电丢失,因此可以定时写入数据库中或者文件中(写入时还要对比递增中的和库中的,取大者);
为了保险起见,防止上述情况失效,再在ID中加一个时间,现在一般也就精确到微秒吧,也就是时间(微秒级)+自增因子;
如果为了应付更大访问,或者容灾,或者分布式,再加一个机器标志位,也就是机器标志+时间(微秒级)+自增因子;
我们现在用这个方案来生成1000个/s左右并发量的ID,算是一个比较稳定安全的方案。
当然,各个位置的长度要考虑数据的量,保存的时间等。如果像淘宝这种,肯定是用户ID+购物记录ID+时间等各个组合成一个购买记录的ID的。毕竟数据量太大了
Ticket Servers: Distributed Unique Primary Keys on the Cheap
如果自增主键都能出现重复,只能说你的代码有个天大的bug。
我们基本都用guid,guid算法的实现各个版本略有不同,不过应该不用担心撞车的问题。guid就是比较占地方。
擦,解决不了就慢慢来,也不至于把所有数据库都删了啊!