当前位置: 代码迷 >> C# >> C#生成独一值的方法
  详细解决方案

C#生成独一值的方法

热度:7   发布时间:2016-05-05 03:13:33.0
C#生成唯一值的方法

使用1、MaxId表存储各表的MaxId

?

专门一个数据库,记录各个表的MaxId值,建一个存储过程来取Id,逻辑大致为:开启事物,对于在表中不存在记录,直接返回一个默认值为1的键值,同时插入该条记录到table_key表中。而对于已存在的记录,key值直接在原来的key基础上加1更新到MaxId表中并返回key

第一步:创建表create table table_key(       table_name   varchar(50) not null primary key,       key_value    int         not null)第二步:创建存储过程来取自增IDcreate procedure up_get_table_key(   @table_name     varchar(50),   @key_value      int output)asbegin     begin tran         declare @key  int                  --initialize the key with 1         set @key=1         --whether the specified table is exist         if not exists(select table_name from table_key where [email protected]_name)            begin              insert into table_key values(@table_name,@key)        --default key vlaue:1            end         -- step increase         else                begin                select @key=key_value from table_key with (nolock) where [email protected]_name                set @[email protected]+1                --update the key value by table name                update table_key set [email protected] where [email protected]_name            end        --set ouput value    set @[email protected]    --commit tran    commit tran        if @@error>0      rollback tranend

?存储过程中不使用事物,一旦使用到事物性能就急剧下滑。直接使用UPDATE获取到的更新锁,即SQL SERVER会保证UPDATE的顺序执行。(已在用户过千万的并发系统中使用)

create procedure [dbo].[up_get_table_key](   @table_name     varchar(50),   @key_value      int output)asbegin	SET NOCOUNT ON;	DECLARE @maxId INT	UPDATE table_key	SET @maxId = key_value,key_value = key_value + 1 	WHERE [email protected]_name	SELECT @maxIdend

?结论:适用中型应用,此方案解决了分表,关联表插入记录的问题。但是无法满足高并发性能要求。同时也存在单点问题,如果这个数据库cash掉的话……

我们目前正头痛这个问题,因为我们的高并发常常出现数据库访问超时,瓶颈就在这个MaxId表。我们也有考虑使用分布式缓存(egmemcached)缓存第一次访问MaxId表数据,以提高再次访问速度,并定时用缓存数据更新一次MaxId表,但我们担心的问题是:

a)???????? 倘若缓存失效或暴掉了,那缓存的MaxId没有更新到数据库导致数据丢失,必须停掉站点来执行Select max(id)各个表来同步MaxId表。

b)???????? 分布式缓存不是一保存下去,其他服务器上就立马可以获取到的,即数据存在不确定性。(其实也是缓存的一个误用,缓存应该用来存的是频繁访问并且很少改动的内容)

???????? 改进方案:

整体思想:建立两台以上的数据库ID生成服务器,每个服务器都有一张记录各表当前IDMaxId表,但是MaxId表中Id的增长步长是服务器的数量,起始值依次错开,这样相当于把ID的生成散列到每个服务器节点上。例如:如果我们设置两台数据库ID生成服务器,那么就让一台的MaxId表的Id起始值为1(或当前最大Id+1),每次增长步长为2,另一台的MaxId表的ID起始值为2(或当前最大Id+2),每次步长也为2。这样就将产生ID的压力均匀分散到两台服务器上,同时配合应用程序控制,当一个服务器失效后,系统能自动切换到另一个服务器上获取ID,从而解决的单点问题保证了系统的容错。(Flickr思想)

但是要注意:1、多服务器就必须面临负载均衡的问题;2、倘若添加新节点,需要对原有数据重新根据步长计算迁移数据。

结论:适合大型应用,生成Id较短,友好性比较好。(强烈推荐)

2、COMB数据类型的基本设计思路是这样的:既然GUID数据因毫无规律可言造成索引效率低下,影响了系统的性能,那么能不能通过组合的方式,保留GUID10个字节,用另6个字节表示GUID生成的时间(DateTime),这样我们将时间信息与GUID组合起来,在保留GUID的唯一性的同时增加了有序性,以此来提高索引效率。

NHibernate中,COMB型主键的生成代码如下所示:

/// <summary> /// Generate a new <see cref="Guid"/> using the comb algorithm.         /// </summary>         private Guid GenerateComb()        {            byte[] guidArray = Guid.NewGuid().ToByteArray();            DateTime baseDate = new DateTime(1900, 1, 1);            DateTime now = DateTime.Now;            // Get the days and milliseconds which will be used to build                //the byte string                TimeSpan days = new TimeSpan(now.Ticks - baseDate.Ticks);            TimeSpan msecs = now.TimeOfDay;            // Convert to a byte array                    // Note that SQL Server is accurate to 1/300th of a                // millisecond so we divide by 3.333333                byte[] daysArray = BitConverter.GetBytes(days.Days);            byte[] msecsArray = BitConverter.GetBytes((long)              (msecs.TotalMilliseconds / 3.333333));            // Reverse the bytes to match SQL Servers ordering                Array.Reverse(daysArray);            Array.Reverse(msecsArray);            // Copy the bytes into the guid                Array.Copy(daysArray, daysArray.Length - 2, guidArray,              guidArray.Length - 6, 2);            Array.Copy(msecsArray, msecsArray.Length - 4, guidArray,              guidArray.Length - 4, 4);            return new Guid(guidArray);        }

?结论:适合大型应用。即保留GUID的唯一性的同时增加了GUID有序性,提高了索引效率;解决了关联表业务问题;生成的Id不够友好;占据了32位。(强烈推荐)

3、自己写编码规则

优点:全局唯一Id,符合业务后续长远的发展(可能具体业务需要自己的编码规则等等)。

缺陷:根据具体编码规则实现而不同;还要考虑倘若主键在业务上允许改变的,会带来外键同步的麻烦。

我这边写两个编码规则方案:(可能不唯一,只是个人方案,也请大家提出自己的编码规则

1)???????? 12位年月日时分秒+5位随机码+3位服务器编码??(这样就完全单机完成生成全局唯一编码)---20

缺陷:因为附带随机码,所以编码缺少一定的顺序感。(生成高唯一性随机码的方案稍后给给出程序)

2)???????? 12位年月日时分秒+5位流水码+3位服务器编码?(这样流水码就需要结合数据库和缓存)---20位 ? (将影响顺序权重大的“流水码”放前面,影响顺序权重小的服务器编码放后)

缺陷:因为使用到流水码,流水码的生成必然会遇到和MaxId、序列表、Sequence方案中类似的问题

(为什么没有毫秒?毫秒也不具备业务可读性,我改用5位随机码、流水码代替,推测1秒内应该不会下99999[五位]条语法)

?

结论:适合大型应用,从业务上来说,有一个规则的编码能体现产品的专业成度。(强烈推荐)

  相关解决方案