使用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 ) 第二步:创建存储过程来取自增ID create procedure up_get_table_key ( @table_name varchar(50), @key_value int output ) as begin 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 table_name=@table_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 table_name=@table_name set @key=@key+1 --update the key value by table name update table_key set key_value=@key where table_name=@table_name end --set ouput value set @key_value=@key --commit tran commit tran if @@error>0 rollback tran end
存储过程中不使用事物,一旦使用到事物性能就急剧下滑。直接使用UPDATE获取到的更新锁,即SQL SERVER会保证UPDATE的顺序执行。(已在用户过千万的并发系统中使用)
create procedure [dbo].[up_get_table_key] ( @table_name varchar(50), @key_value int output ) as begin SET NOCOUNT ON; DECLARE @maxId INT UPDATE table_key SET @maxId = key_value,key_value = key_value + 1 WHERE table_name=@table_name SELECT @maxId end
结论:适用中型应用,此方案解决了分表,关联表插入记录的问题。但是无法满足高并发性能要求。同时也存在单点问题,如果这个数据库cash掉的话……
我们目前正头痛这个问题,因为我们的高并发常常出现数据库访问超时,瓶颈就在这个MaxId表。我们也有考虑使用分布式缓存(eg:memcached)缓存第一次访问MaxId表数据,以提高再次访问速度,并定时用缓存数据更新一次MaxId表,但我们担心的问题是:
a) 倘若缓存失效或暴掉了,那缓存的MaxId没有更新到数据库导致数据丢失,必须停掉站点来执行Select max(id)各个表来同步MaxId表。
b) 分布式缓存不是一保存下去,其他服务器上就立马可以获取到的,即数据存在不确定性。(其实也是缓存的一个误用,缓存应该用来存的是频繁访问并且很少改动的内容)
改进方案:
整体思想:建立两台以上的数据库ID生成服务器,每个服务器都有一张记录各表当前ID的MaxId表,但是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数据因毫无规律可言造成索引效率低下,影响了系统的性能,那么能不能通过组合的方式,保留GUID的10个字节,用另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[五位]条语法)
结论:适合大型应用,从业务上来说,有一个规则的编码能体现产品的专业成度。(强烈推荐)
相关推荐
虽然上述方法可以生成唯一序列号,但在实际应用中,可能需要考虑更多的安全因素,如防止序列号的破解或伪造。可以采用加密算法对生成的序列号进行编码,或者添加额外的验证机制,比如在序列号中包含服务器生成的私有...
总结来说,C#生成不重复时间戳的关键在于结合当前时间与高质量的随机数生成器,以减少重复的可能性。在ASP.NET应用中,这样的功能可以提升系统的可靠性和安全性。在实际应用中,根据业务需求和性能考虑,可以适当...
在给定的标题和描述中,我们关注的是如何在C#中获取这些硬件信息,并利用它们生成机器码。 首先,我们需要了解如何获取硬盘序列号。在Windows系统中,硬盘序列号是一个标识硬盘的唯一数字或字母组合。在C#中,可以...
C#中生成唯一订单号的一种常见方法是结合`Guid`(全局唯一标识符)和当前时间戳。上述代码片段展示了如何结合这两种元素创建一个自定义的`UniqueData`类,用于生成唯一订单号。以下是关键知识点的详细解释: 1. **...
本主题聚焦于“C#生成防伪码”的技术,这是一个常见的需求,特别是在产品安全、版权保护以及数据验证等场景。防伪码通常是一组随机的、难以预测的数字或字母序列,用于确保产品的唯一性,防止伪造。 生成防伪码的...
本项目名为"C#随机生成发货地址,生成详细地址,随机生成中国姓名",正是这样一个工具,它能帮助开发者快速创建出大量具有真实感的中国地区发货地址和姓名,适用于自动化测试、数据填充等场景。 该项目的核心是使用...
总结来说,这个项目涵盖了C#中防伪码的生成策略和文件存储的方法,是学习和提升C#技能的好素材。通过实践,开发者不仅可以掌握基础的编程技巧,还能了解如何在实际场景中应用这些技术,提升软件的安全性和可靠性。
`System.Security.Cryptography`命名空间提供了一系列的哈希算法,如`SHA256`,可以用来生成唯一的哈希值。 ```csharp string uniqueId = Guid.NewGuid().ToString(); // 可以用设备ID或其他唯一标识 long ...
在C#编程中,生成数字和字母混合的递增序列是一项常见的需求,特别是在创建唯一标识符或序列号时。这个任务的关键在于理解如何混合数字和字母,并根据指定的位数和大小写规则进行递增。以下是一些关于如何实现这个...
如果不提供种子值,系统会使用当前时间作为默认种子,确保每次运行时的序列都是唯一的。 2. **生成随机整数**: 要生成指定范围内的随机整数,你可以使用`Random.Next()`方法。例如,要生成介于1到100之间的整数,...
- **哈希函数**:可以使用SHA系列或MD5等哈希函数,将硬件信息组合后进行哈希运算,得到一串固定长度的唯一值。 - **编码与解码**:将哈希后的二进制数据转化为可读的字符串,如十六进制或Base64编码。 - **加盐*...
防伪码是由一系列字符组成的,C#提供了丰富的字符串处理方法,如`string.Concat`、`string.Join`等,用于将随机数转换为字符串并组合成防伪码。同时,`StringBuilder`类在处理大量字符串连接时更高效。 4. **字符...
此方法首先定义了一个SQL查询字符串,通过查询表中的最大值来生成新的订单号。这种方法能够生成连续的流水号编码,但可能会在多用户同时操作时产生冲突。为了避免这种冲突,可以在保存之前进行检查或使用其他机制...
例如,`DateTime`类可用于生成随机日期,而`Guid`类则可生成唯一的ID。 接下来,将生成的数据输出到Excel文档,C#可以借助第三方库,如EPPlus。这个库允许我们操作Excel文件,创建工作表,写入数据,如下所示: ``...
生成并发唯一性流水号是指在并发环境中生成唯一的流水号,以满足业务系统中对流水号的需求。下面将对该解决方案的关键技术点进行详细解释。 表结构设计 在生成并发唯一性流水号的解决方案中,表结构的设计是关键。...
防伪码通常由一系列独特的数字或字母组成,通过特定算法生成,确保每个防伪码的唯一性,使得消费者可以通过验证防伪码来确认产品的真伪。在本场景中,我们将讨论如何使用C#编程语言来实现防伪码的随机生成。 C#是一...
为了实际使用这个验证码,你可以在Web应用或桌面应用中调用这些方法,将生成的图像发送到客户端,并存储验证码的文本值以供后续验证。例如,在Web应用中,可以将图像转换为Base64字符串并嵌入HTML中,或者保存在...
通过以上步骤,你可以实现ASP.NET C#中从数据库动态生成控件并获取它们的值的功能。这在创建灵活的用户界面和处理动态数据时非常有用。记住,动态生成控件虽然强大,但也会增加页面的复杂性和处理时间,因此需谨慎...