首页 新闻 会员 周边

关于并发表唯一Id的问题,各位有什么好方法。都进来看看啊……

2
悬赏园豆:100 [已解决问题] 解决于 2013-08-21 23:07

今天我们讨论了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的看法。(最关键的是希望你们能提供一个好的编码规则,和理由简单说明)

滴答的雨的主页 滴答的雨 | 老鸟四级 | 园豆:3660
提问于:2013-08-15 14:44
< >
分享
最佳答案
0

用的是什么数据库?

收获园豆:60
dudu | 高人七级 |园豆:31007 | 2013-08-15 14:51

mssql

滴答的雨 | 园豆:3660 (老鸟四级) | 2013-08-15 14:53

@滴答的雨: mysql和oracle   这些会存在差异吗?也请dudu帮说下差异吧(简单的概述就好)

滴答的雨 | 园豆:3660 (老鸟四级) | 2013-08-15 14:54

@dudu:

1、我看看,真是好特性啊

2、我们现在还在用mssql2008……

滴答的雨 | 园豆:3660 (老鸟四级) | 2013-08-15 15:06

@滴答的雨: 我们现在用的也是SQL Server 2008

dudu | 园豆:31007 (高人七级) | 2013-08-15 15:08

@dudu: 不是2012的特性吗?(哎,这个措施貌似我以前在mysql用过,不过不懂mysql语法,还以为mysql中建立自增长就是用额外的序列对象……)

滴答的雨 | 园豆:3660 (老鸟四级) | 2013-08-15 15:12

@滴答的雨: 

Sequence是SQL Server 2012的新特性。

我们用SQL Server 2008,没有遇到你这样的场景。

如果遇到这样的场景,我们会考虑用表分区或Guid。

dudu | 园豆:31007 (高人七级) | 2013-08-15 15:17

@滴答的雨: 另外还想解决的一个问题是,我想在插入前就知道编码,因为比如订单表,有多个子表想在插入前就有编码以免再返回主键进行子表插入了。

滴答的雨 | 园豆:3660 (老鸟四级) | 2013-08-15 15:18

@滴答的雨: 其实我们之前用第三个方案,还没加缓存,所以导致并发时查询id超时了,加入缓存就会考虑缓存丢失的问题。

这是我之前想到的方案

Id最大值取法:存储过程中启用事物取maxId,并更新数据库中MaxId+1。。这样串行解决了MaxId重复的问题。但引起一个问题,太多需要重MaxId表中取MaxId的操作造成阻塞而报超时错误。

我的建议:这个超时问题只能用缓存来解决,获取一次所有MaxId后放入缓存中,然后后续的操作都再缓存中拿并更新缓存为MaxId+1;定时执行一次更新语法。。。但也可能出现缓存丢失的现象,比如使用memcached对于频繁使用的key出现丢失应该是整个缓存都丢了(eg:服务器停电等意外情况)。这个获取不到key或许需要先执行每个表的select max(id) 同步后,网站才能开始运作。。。(不过目前我们还没正式使用memecached缓存,还是使用的单机cache缓存类)

 

但是我听有人提出“编码规则”我觉得这个方案挺好的(我第一家公司就是使用这方案的,不过那时太年轻,傻傻分不清楚……)

另外不用guid也考虑到我们需要将订单id给用户,并且常常需要根据id查数据。guid做为聚集索引感觉没什么大做用哎。

滴答的雨 | 园豆:3660 (老鸟四级) | 2013-08-15 15:19

@滴答的雨: 

我觉得第三个方案把问题复杂化了

另外,让人奇怪的是已经分表了,却还没使用memcached。

dudu | 园豆:31007 (高人七级) | 2013-08-15 15:31

@dudu: 实际上现在还没分表!

滴答的雨 | 园豆:3660 (老鸟四级) | 2013-08-15 15:35

@滴答的雨: 没有分表,为什么说自增ID不适合?

dudu | 园豆:31007 (高人七级) | 2013-08-15 15:42

@dudu:

以后,电子商务麻,以后流量上去了自然要分表。应该是下一步要做的。

 

另外:我看了sequence,这个貌似是隶属于数据库的对象,如果数据库集群分表后应该会和自增id出现一样的问题吧。

滴答的雨 | 园豆:3660 (老鸟四级) | 2013-08-15 15:46

@滴答的雨: 你确认数据库集群会带来这个问题?

dudu | 园豆:31007 (高人七级) | 2013-08-15 15:48

@dudu:

我是这样理解集群分表的(我没玩过集群和分表):就是一个表结构有多份存储,多份存在在不同数据库,这样就不能用自增id,而sequence是隶属于某一个数据库的,这样我多份存储在不同数据库就会有不同的sequence,就可能相同了

滴答的雨 | 园豆:3660 (老鸟四级) | 2013-08-15 15:54

@滴答的雨: 

我觉得你面临的是分表的问题(Sharding),与集群无关。

dudu | 园豆:31007 (高人七级) | 2013-08-15 15:58

@dudu: 是啊,理解错误。我以为是差不多的概念。但其实刚才回复的是分表的概念。刚去咨询了DBA了解了一下。。。那现在分表的话就在不同数据库了,那sequence就不适合了哇。。对了?

 

另外:

1、dudu你们出现缓存丢失或爆掉吗?都是因为什么原因啊?

2、还是目前选择编码规则方案,有什么保证唯一的好方法吗?

滴答的雨 | 园豆:3660 (老鸟四级) | 2013-08-15 16:19

@滴答的雨: 

是的,sequence解决不了sharding的问题

1、我们在缓存出问题时,数据库服务器是能撑住的。

2、我推荐的解决方法是Guid

dudu | 园豆:31007 (高人七级) | 2013-08-15 16:30

@dudu: 好的。GUID。也有组员说用这个,再看上面的决定吧

滴答的雨 | 园豆:3660 (老鸟四级) | 2013-08-15 16:40

@滴答的雨: dudu.你看楼下的方法生成唯一吗?我现在写了个测试测他的不过并发生成的是唯一的

滴答的雨 | 园豆:3660 (老鸟四级) | 2013-08-15 18:08

@滴答的雨: 楼下的就是Guid的解决方案,只不过是在C#代码中生成Guid

dudu | 园豆:31007 (高人七级) | 2013-08-15 18:12

@dudu: 他ToInt64了,这样之后还唯一不。数字比GUID提供给客户更加专业。因为有组员说不同的GUID调用ToInt64会有可能生成相同值

滴答的雨 | 园豆:3660 (老鸟四级) | 2013-08-15 18:20

@滴答的雨: 那可以在插入数据之前,先通过ID在数据库中查询一下,如果存在相同的ID,就重新生成

dudu | 园豆:31007 (高人七级) | 2013-08-15 18:25

@dudu: 这样会查一次数据,我们现在比如订单表,会在高并发时我们的数据库服务器会超时

滴答的雨 | 园豆:3660 (老鸟四级) | 2013-08-15 20:39

@滴答的雨: 如果有索引的话,不应该会超时

dudu | 园豆:31007 (高人七级) | 2013-08-15 21:34

@dudu: 估计是表太大了,现在又没分表。。。应该下一步就要分表了。。。

 

奥。现在我们采取的是方案3,使用maxId表。所以每个表都到这边来,超时的是这个表。

滴答的雨 | 园豆:3660 (老鸟四级) | 2013-08-15 22:55

@滴答的雨: 不明白maxid表为什么会超时,难道所有表所有记录的ID都会存入这个表?

dudu | 园豆:31007 (高人七级) | 2013-08-16 07:50

@dudu: 是啊,目前是这样处理的呢。。。我写了篇博客,dudu有空来看看,是方案二:如何在高并发分布式系统中生成全局唯一Id

 

滴答的雨 | 园豆:3660 (老鸟四级) | 2013-08-16 09:37
其他回答(2)
0
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位的

收获园豆:30
黑色的羽翼 | 园豆:232 (菜鸟二级) | 2013-08-15 16:17

确实不错,我发公司群讨论下……你用过这个方案吗?不过也是无序的吧……

支持(0) 反对(1) 滴答的雨 | 园豆:3660 (老鸟四级) | 2013-08-15 16:24

@滴答的雨: 组员说,这guid,ToInt64后就不唯一了。

支持(0) 反对(0) 滴答的雨 | 园豆:3660 (老鸟四级) | 2013-08-15 16:31

@滴答的雨: 

是无序的,得看看你们是否必须要有序的。

有一个教训是,像大规模的应用,这样的业务还不是不要依赖数据库生成了,否则很容易引起数据库的故障

支持(0) 反对(0) 黑色的羽翼 | 园豆:232 (菜鸟二级) | 2013-08-15 16:32

@滴答的雨:

  • 很多时候,要自己测试下看看的,不能想当然。 
支持(0) 反对(0) 黑色的羽翼 | 园豆:232 (菜鸟二级) | 2013-08-15 16:41

@黑色的羽翼: 确实,我写了个测试程序测试,目前还都是并发生成了唯一key。。。我再继续跑下。。。你是不是用过这个,并且验证过是唯一啊

支持(0) 反对(0) 滴答的雨 | 园豆:3660 (老鸟四级) | 2013-08-15 18:08
0

我们是自己写个服务生成int64长度的数字. 所有业务都调用这个服务来生成id,

收获园豆:10
assiwe | 园豆:253 (菜鸟二级) | 2013-08-15 18:46

就是我说的第4种方案,自定义编码规则。你们的规则是什么呢?

支持(0) 反对(0) 滴答的雨 | 园豆:3660 (老鸟四级) | 2013-08-15 20:17

@滴答的雨: 和你们那种不一样, 我们的就是123456往下排. 比如一个业务需要100个id,它就向这个服务调用类似GetRecordID(100)的,就会取得一个起始id比如101, 下次再调用这个服务时返回的就会是201了. 这个服务本身是串行的,所以不用担心并发问题. MaxID会存入一个文本文件里. 其实更类似于第三种, 不过没有采用数据库做持久化,

支持(0) 反对(0) assiwe | 园豆:253 (菜鸟二级) | 2013-08-16 08:36

@assiwe: 明白了,但如果业务大并发的去取GetRecordId,是不是造成速度下降或超时。现在我们采用第三种方案,高访问量的时候出现访问数据库超时呢

支持(0) 反对(0) 滴答的雨 | 园豆:3660 (老鸟四级) | 2013-08-21 22:44

@滴答的雨: 不会, 这个服务实际上就是读取再保存一个超级小(只有一个int64大小的数字)的文本文件. 成本很低的.

支持(0) 反对(0) assiwe | 园豆:253 (菜鸟二级) | 2013-09-02 09:14
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册