一、引言

在使用ORM框架时,一个表有一个主键是必须的,如果没有主键,就没有办法来唯一的更新一条记录。在Sql Server数据库和Mysql数据库设置自增长的主键是一件很轻松的事情,如果在Oracle数据库中设置自增长的主键是比较繁琐的。本文不讨论数据库里单表的自增长问题,探讨的是多表自增长唯一Id的设计。
如果各位看官遇到这个多表自增长唯一Id的这个需求,会怎么处理呢?

二、GUID的介绍

关于自增长主键的问题,有些人可能会想到.Net中的GUID,先对这个GUID进行测试。

  1. public void GuidTest()
  2. {
  3. string guid = Guid.NewGuid().ToString();
  4. Console.WriteLine(guid);
  5. Console.WriteLine(guid.Length);
  6. }

GUID Test

下面是输出结果:

c8eb1c81-eafa-423b-a1bf-15fd5df829c4
36

可以看到这个GUID是一个字符串,长度36位,还真够长的,估计在用的时候,如果想减少位数,可以把横杠-去掉,少了4位,还剩32位,不过还是挺长的。
以我的观察来看,现在如果谁把数据库的主键设计成这个GUID,还真的是个二货:)
原因有三,1)太长了,在数据库里占空间  
              2)排序不方便
              3)检索时字符串的效率不如整数
本来对GUID只想一笔而过,看来写得太多了。

三、单机版自增长Id实现

针对多表下的自增长的唯一的ID,首先想到的就是时间,把这个时间格式化成整形的数字,就可以解决问题。为了减少位数,把日期的年的部分20去掉,毫秒数取两位,共15位。
为了防止重复,程序每次生成,如果当前生成的Id大于当前时间下的Id,会保存在在xml里,下次程序启动,会检查xml里保存的Id和当前时间下的Id,两者取其大。这样做是为了,防止程序重启,生成的Id重复。当然,为了多线程的安全,顺手lock。代码如下:

  1. public class IdGenerator
  2. {
  3. private static string idXml = "id.xml";
  4. private static long current = ;
  5. private static object obj = new object();
  6. private static string format = "yyMMddHHmmssfff"; //15位
  7.  
  8. static IdGenerator()
  9. {
  10. var xDoc = XDocument.Load(idXml);
  11. long last = ;
  12. long.TryParse(xDoc.Root.Value, out last);
  13.  
  14. //每次启动检查最后生成的Id是否大于当前时间下的Id
  15. long now = long.Parse(DateTime.Now.ToString(format));
  16. if (last > now)
  17. {
  18. current = last;
  19. }
  20. else
  21. {
  22. current = now;
  23. }
  24. }
  25.  
  26. public static long GetId()
  27. {
  28. lock (obj)
  29. {
  30. current += ;
  31.  
  32. //如果当前的生成的Id大于当前时间下的Id,就要保存下来
  33. if (current > long.Parse(DateTime.Now.ToString(format)))
  34. {
  35. var xDoc = XDocument.Load(idXml);
  36. xDoc.Root.Value = current.ToString();
  37. xDoc.Save(idXml);
  38. }
  39. return current;
  40. }
  41. }
  42. }

IdGenerator

四、分布式下的自增长Id实现
在现实环境下,项目中可能有多个网站或多个应用程序使用这个自增长的Id,这样就要考虑分布式的情况,这里实现分布式使用WCF技术,WCF实现在本地和服务器通过契约来通信,抽象之后,调用服务器代码就像调用本地代码。
首先定义契约:

  1. namespace WCFServiceTest
  2. {
  3. [ServiceContract]
  4. public interface IIdGenContract
  5. {
  6. [OperationContract]
  7. long GetIdByWCF();
  8. }
  9. }

IIdGenContract

该接口里有一个方法GetIdByWCF,这个方法需要服务端来实现。

  1. namespace WCFServiceTest
  2. {
  3. [ServiceBehavior(IncludeExceptionDetailInFaults = true, InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Multiple, MaxItemsInObjectGraph = Int32.MaxValue)]
  4. public class IdGenService : IIdGenContract
  5. {
  6. public static IdGenService Instance = new IdGenService();
  7.  
  8. public long GetIdByWCF()
  9. {
  10. return IdGenerateHelper.IdGenerator.GetId();
  11. }
  12.  
  13. }
  14. }

IdGenService

下一步就是开启这个服务

  1. namespace WCFServiceTest
  2. {
  3. class Program
  4. {
  5. static void Main(string[] args)
  6. {
  7. ServiceHost host = new ServiceHost(IdGenService.Instance);
  8. host.Open();
  9. Console.WriteLine(typeof(IdGenService) + " Opened");
  10. Console.WriteLine("服务地址:" + host.Description.Endpoints[].ListenUri);
  11.  
  12. Console.Read();
  13. }
  14. }
  15. }

Program

这里面需要配置文件,如果是网站就是web.config,桌面程序就是app.config,配置文件的详细会在下面的demo代码里。
服务端完成了,下面就是编写客户端的代码来调用该服务了。客户端通过契约(接口)来和服务端进行通信,也要实现这个接口。

  1. namespace ConsoleApplication2
  2. {
  3. public class IdService : ClientBase<IIdGenContract>, IIdGenContract
  4. {
  5. public long GetIdByWCF()
  6. {
  7. return base.Channel.GetIdByWCF();
  8. }
  9. }
  10. }

ClientIdService

接下来就是测试代码了

  1. namespace ConsoleApplication2
  2. {
  3. /// <summary>
  4. /// 通过调用WCF服务的方式生成自增长Id
  5. /// </summary>
  6. class Program
  7. {
  8. static void Main(string[] args)
  9. {
  10. long id = new ClientIdService().GetIdByWCF();
  11. Console.WriteLine(id);
  12. Console.Read();
  13. }
  14. }
  15. }

Program

四、结束
一切按计划执行,输出一个唯一的Id,本文完整Demo下载。

自增长主键Id的另类设计的更多相关文章

  1. ibatis annotations 注解方式返回刚插入的自增长主键ID的值

    mybatis提供了注解方式编写sql,省去了配置并编写xml mapper文件的麻烦,今天遇到了获取自增长主键返回值的问题,发现相关问答比较少,还好最后还是圆满解决了,现把重点记录一下,解决问题的关 ...

  2. ibatis annotations 注解方式返回刚插入的自增长主键ID的值--转

    原文地址:http://www.blogs8.cn/posts/WWpt35l mybatis提供了注解方式编写sql,省去了配置并编写xml mapper文件的麻烦,今天遇到了获取自增长主键返回值的 ...

  3. 自增长主键Id的设计

    http://www.cnblogs.com/lhking/p/3945865.html

  4. C#连接mysql数据库插入数据后获取自增长主键ID值

    From: http://blog.csdn.net/zbc496218/article/details/51082983 MySqlConnection conn = new MySqlConnec ...

  5. Access获取新插入数据的自增长主键Id

    sqlserver有output,Oracle有Sequence.Access用下面的方法: public int InsertEx(User user) { ; using (OleDbConnec ...

  6. mysql 插入数据失败防止自增长主键增长的方法

    mysql设置了自增长主键ID,插入失败的那个自增长ID也加一的,比如失败5个,下一个成功的不是在原来最后成功数据加1,而是直接变成加6了,失败次数一次就自动增长1了,能不能让失败的不增长的? 或者说 ...

  7. mybaits返回自增主键ID

    mybaits两种获取自增主键ID的方法:一种是使用useGeneratedKeys,第二种是selectKey方法获取. useGeneratedKeys <insert id="i ...

  8. Mycat探索之旅(4)----Mycat的自增长主键和返回生成主键ID的实现

    说明:MyCAT自增长主键和返回生成主键ID的实现 1) mysql本身对非自增长主键,使用last_insert_id()是不会返回结果的,只会返回0:这里做一个简单的测试 创建测试表 ------ ...

  9. mybatis添加记录时返回主键id

    参考:mybatis添加记录时返回主键id 场景 有些时候我们在添加记录成功后希望能直接获取到该记录的主键id值,而不需要再执行一次查询操作.在使用mybatis作为ORM组件时,可以很方便地达到这个 ...

随机推荐

  1. linux别名和快捷键

    别名永久生效和删除别名 永久生效: vi ~./bashrc 写入环境变量配置文件 删除别名: unalias 别名 source命令可以让本身需要重新登录的命令不重新登录也生效 命令生效顺序: 1. ...

  2. HTML DOM 节点

    一切皆节点 在 HTML DOM (文档对象模型)中,节点主要包括(括号中用数字表示节点类型):元素(1).属性(2).文本(3,其中换行符也是一个文本节点).注释(8).文档(9). 其中重要的方法 ...

  3. HDU-3247 Resource Archiver(AC自动机+BFS)

    Description Great! Your new software is almost finished! The only thing left to do is archiving all ...

  4. 获取应用程序信息.h

    ////  获取应用程序信息.h//  IOS笔记// 一般会用来判断是否有新版本.是否需要强制更新 iOS的版本号,一个叫做Version,一个叫做Build,这两个值都可以在Xcode 中选中ta ...

  5. algorithm 学习之 for_each

    对于algorithm里面的函数使用不算多,但是用过之后才发现,之前写过很多多余的代码,所以打算系统的学习使用下algorithm里的东西,首先就是for_each. 先看下for_each的定义: ...

  6. 10天学会phpWeChat——第五天:实现新闻投稿功能

    在前几讲里,我们逐渐实现了自己小模块的新闻列表展示.新闻详情展示功能,现在您已经初步有能力开发一个phpWeChat小模块了,本文将在已开发的hello world模块基础上,增加一个新的功能--新闻 ...

  7. AT Tool --- android手机发送at指令

    之前网上也有一款类似的软件,估计是华为内部人员开发的,不过很变态,不但只支持华为的几款手机,而且只能发一条AT命令,然后就不让你发了:所以很气愤,今天花了一天时间自己写了这么款程序,而且是支持所有An ...

  8. JMeter基础之--元件的作用域与执行顺序

    前面有介绍过jmeter的元件类别,对于新手来说,jmeter的元件是还是不少的,如果我们按照每一个元件的每一个参数的含义去学习,无疑会降低学习性能测试的热情,就算我们熟悉了所有元件以及元件上的参数了 ...

  9. centos 7安装部署docker

    1.centos 7 在windows下通过vm虚拟机安装centos 7: VMware-workstation-full-10.0.3-1895310 centos 7 2.要求 操作系统的内核版 ...

  10. [PHP] - Laravel 5 的 Hello Wold

    吐槽一段 整了半天,Laravel下载麻烦得可以去死.先要安装composer,而composer又被共墙了,之后又要安装git,安装完git还要注册git,等等.... 最终放弃这种玩法,太恶心了. ...