今天我们讨论了4个表唯一Id的办法:(数据库MSSQL)
1、自增Id,因为数据库分表所以自增Id不适合
2、GUID,是一个好方案,但是以前的代码需要改动太多,无可读性(eg:订单Id),也决定不适合
3、设置一个MaxId表,各个表的MaxId都存在MaxId表中,并结合分布式缓存提高效率,但是若缓存失效,就大片错误,也决定不使用
4、自己弄编码规则,这是最后采取的方案。符合业务后续长远的发展(可能默写业务需要自己的编码规则等等)。我这边写两个编码规则方案:
3位服务器编码+15位年月日时分秒毫秒+3位表编码+4位随机码 (这样就完全单机完成编码任务)---共25位
3位服务器编码+15位年月日时分秒毫秒+3位表编码+4流水码 (这样流水码就需要结合数据库和缓存)---共25位
各位公司都是怎么解决这个问题的啊。
我想听听你们关于方案2、3、4的看法。(最关键的是希望你们能提供一个好的编码规则,和理由简单说明)
用的是什么数据库?
mssql
@滴答的雨: mysql和oracle 这些会存在差异吗?也请dudu帮说下差异吧(简单的概述就好)
@滴答的雨:
SQL Server 2012已经解决了这个问题——Sequence
参考博文:
SQL Server2012中的SequenceNumber尝试
@dudu:
1、我看看,真是好特性啊
2、我们现在还在用mssql2008……
@滴答的雨: 我们现在用的也是SQL Server 2008
@dudu: 不是2012的特性吗?(哎,这个措施貌似我以前在mysql用过,不过不懂mysql语法,还以为mysql中建立自增长就是用额外的序列对象……)
@滴答的雨:
Sequence是SQL Server 2012的新特性。
我们用SQL Server 2008,没有遇到你这样的场景。
如果遇到这样的场景,我们会考虑用表分区或Guid。
@滴答的雨: 另外还想解决的一个问题是,我想在插入前就知道编码,因为比如订单表,有多个子表想在插入前就有编码以免再返回主键进行子表插入了。
@滴答的雨: 其实我们之前用第三个方案,还没加缓存,所以导致并发时查询id超时了,加入缓存就会考虑缓存丢失的问题。
这是我之前想到的方案
Id最大值取法:存储过程中启用事物取maxId,并更新数据库中MaxId+1。。这样串行解决了MaxId重复的问题。但引起一个问题,太多需要重MaxId表中取MaxId的操作造成阻塞而报超时错误。
我的建议:这个超时问题只能用缓存来解决,获取一次所有MaxId后放入缓存中,然后后续的操作都再缓存中拿并更新缓存为MaxId+1;定时执行一次更新语法。。。但也可能出现缓存丢失的现象,比如使用memcached对于频繁使用的key出现丢失应该是整个缓存都丢了(eg:服务器停电等意外情况)。这个获取不到key或许需要先执行每个表的select max(id) 同步后,网站才能开始运作。。。(不过目前我们还没正式使用memecached缓存,还是使用的单机cache缓存类)
但是我听有人提出“编码规则”我觉得这个方案挺好的(我第一家公司就是使用这方案的,不过那时太年轻,傻傻分不清楚……)
另外不用guid也考虑到我们需要将订单id给用户,并且常常需要根据id查数据。guid做为聚集索引感觉没什么大做用哎。
@滴答的雨:
我觉得第三个方案把问题复杂化了
另外,让人奇怪的是已经分表了,却还没使用memcached。
@dudu: 实际上现在还没分表!
@滴答的雨: 没有分表,为什么说自增ID不适合?
@dudu:
以后,电子商务麻,以后流量上去了自然要分表。应该是下一步要做的。
另外:我看了sequence,这个貌似是隶属于数据库的对象,如果数据库集群分表后应该会和自增id出现一样的问题吧。
@滴答的雨: 你确认数据库集群会带来这个问题?
@dudu:
我是这样理解集群分表的(我没玩过集群和分表):就是一个表结构有多份存储,多份存在在不同数据库,这样就不能用自增id,而sequence是隶属于某一个数据库的,这样我多份存储在不同数据库就会有不同的sequence,就可能相同了
@滴答的雨:
我觉得你面临的是分表的问题(Sharding),与集群无关。
@dudu: 是啊,理解错误。我以为是差不多的概念。但其实刚才回复的是分表的概念。刚去咨询了DBA了解了一下。。。那现在分表的话就在不同数据库了,那sequence就不适合了哇。。对了?
另外:
1、dudu你们出现缓存丢失或爆掉吗?都是因为什么原因啊?
2、还是目前选择编码规则方案,有什么保证唯一的好方法吗?
@滴答的雨:
是的,sequence解决不了sharding的问题
1、我们在缓存出问题时,数据库服务器是能撑住的。
2、我推荐的解决方法是Guid
@dudu: 好的。GUID。也有组员说用这个,再看上面的决定吧
@滴答的雨: dudu.你看楼下的方法生成唯一吗?我现在写了个测试测他的不过并发生成的是唯一的
@滴答的雨: 楼下的就是Guid的解决方案,只不过是在C#代码中生成Guid
@dudu: 他ToInt64了,这样之后还唯一不。数字比GUID提供给客户更加专业。因为有组员说不同的GUID调用ToInt64会有可能生成相同值
@滴答的雨: 那可以在插入数据之前,先通过ID在数据库中查询一下,如果存在相同的ID,就重新生成
@dudu: 这样会查一次数据,我们现在比如订单表,会在高并发时我们的数据库服务器会超时
@滴答的雨: 如果有索引的话,不应该会超时
@dudu: 估计是表太大了,现在又没分表。。。应该下一步就要分表了。。。
奥。现在我们采取的是方案3,使用maxId表。所以每个表都到这边来,超时的是这个表。
@滴答的雨: 不明白maxid表为什么会超时,难道所有表所有记录的ID都会存入这个表?
@dudu: 是啊,目前是这样处理的呢。。。我写了篇博客,dudu有空来看看,是方案二:如何在高并发分布式系统中生成全局唯一Id
1 /// <summary> 2 /// 根据GUID获取19位的唯一数字序列 3 /// </summary> 4 /// <returns></returns> 5 public static long GuidToLongID() 6 { 7 byte[] buffer = Guid.NewGuid().ToByteArray(); 8 return BitConverter.ToInt64(buffer, 0); 9 }
看看这样的行不,好像是19位的
确实不错,我发公司群讨论下……你用过这个方案吗?不过也是无序的吧……
@滴答的雨: 组员说,这guid,ToInt64后就不唯一了。
@滴答的雨:
是无序的,得看看你们是否必须要有序的。
有一个教训是,像大规模的应用,这样的业务还不是不要依赖数据库生成了,否则很容易引起数据库的故障
@滴答的雨:
@黑色的羽翼: 确实,我写了个测试程序测试,目前还都是并发生成了唯一key。。。我再继续跑下。。。你是不是用过这个,并且验证过是唯一啊
我们是自己写个服务生成int64长度的数字. 所有业务都调用这个服务来生成id,
就是我说的第4种方案,自定义编码规则。你们的规则是什么呢?
@滴答的雨: 和你们那种不一样, 我们的就是123456往下排. 比如一个业务需要100个id,它就向这个服务调用类似GetRecordID(100)的,就会取得一个起始id比如101, 下次再调用这个服务时返回的就会是201了. 这个服务本身是串行的,所以不用担心并发问题. MaxID会存入一个文本文件里. 其实更类似于第三种, 不过没有采用数据库做持久化,
@assiwe: 明白了,但如果业务大并发的去取GetRecordId,是不是造成速度下降或超时。现在我们采用第三种方案,高访问量的时候出现访问数据库超时呢
@滴答的雨: 不会, 这个服务实际上就是读取再保存一个超级小(只有一个int64大小的数字)的文本文件. 成本很低的.