自增长主键Id的另类设计
一、引言
在使用ORM框架时,一个表有一个主键是必须的,如果没有主键,就没有办法来唯一的更新一条记录。在Sql Server数据库和Mysql数据库设置自增长的主键是一件很轻松的事情,如果在Oracle数据库中设置自增长的主键是比较繁琐的。本文不讨论数据库里单表的自增长问题,探讨的是多表自增长唯一Id的设计。
如果各位看官遇到这个多表自增长唯一Id的这个需求,会怎么处理呢?
二、GUID的介绍
关于自增长主键的问题,有些人可能会想到.Net中的GUID,先对这个GUID进行测试。
public void GuidTest()
{
string guid = Guid.NewGuid().ToString();
Console.WriteLine(guid);
Console.WriteLine(guid.Length);
}
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。代码如下:
public class IdGenerator
{
private static string idXml = "id.xml";
private static long current = ;
private static object obj = new object();
private static string format = "yyMMddHHmmssfff"; //15位 static IdGenerator()
{
var xDoc = XDocument.Load(idXml);
long last = ;
long.TryParse(xDoc.Root.Value, out last); //每次启动检查最后生成的Id是否大于当前时间下的Id
long now = long.Parse(DateTime.Now.ToString(format));
if (last > now)
{
current = last;
}
else
{
current = now;
}
} public static long GetId()
{
lock (obj)
{
current += ; //如果当前的生成的Id大于当前时间下的Id,就要保存下来
if (current > long.Parse(DateTime.Now.ToString(format)))
{
var xDoc = XDocument.Load(idXml);
xDoc.Root.Value = current.ToString();
xDoc.Save(idXml);
}
return current;
}
}
}
IdGenerator
四、分布式下的自增长Id实现
在现实环境下,项目中可能有多个网站或多个应用程序使用这个自增长的Id,这样就要考虑分布式的情况,这里实现分布式使用WCF技术,WCF实现在本地和服务器通过契约来通信,抽象之后,调用服务器代码就像调用本地代码。
首先定义契约:
namespace WCFServiceTest
{
[ServiceContract]
public interface IIdGenContract
{
[OperationContract]
long GetIdByWCF();
}
}
IIdGenContract
该接口里有一个方法GetIdByWCF,这个方法需要服务端来实现。
namespace WCFServiceTest
{
[ServiceBehavior(IncludeExceptionDetailInFaults = true, InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Multiple, MaxItemsInObjectGraph = Int32.MaxValue)]
public class IdGenService : IIdGenContract
{
public static IdGenService Instance = new IdGenService(); public long GetIdByWCF()
{
return IdGenerateHelper.IdGenerator.GetId();
} }
}
IdGenService
下一步就是开启这个服务
namespace WCFServiceTest
{
class Program
{
static void Main(string[] args)
{
ServiceHost host = new ServiceHost(IdGenService.Instance);
host.Open();
Console.WriteLine(typeof(IdGenService) + " Opened");
Console.WriteLine("服务地址:" + host.Description.Endpoints[].ListenUri); Console.Read();
}
}
}
Program
这里面需要配置文件,如果是网站就是web.config,桌面程序就是app.config,配置文件的详细会在下面的demo代码里。
服务端完成了,下面就是编写客户端的代码来调用该服务了。客户端通过契约(接口)来和服务端进行通信,也要实现这个接口。
namespace ConsoleApplication2
{
public class IdService : ClientBase<IIdGenContract>, IIdGenContract
{
public long GetIdByWCF()
{
return base.Channel.GetIdByWCF();
}
}
}
ClientIdService
接下来就是测试代码了
namespace ConsoleApplication2
{
/// <summary>
/// 通过调用WCF服务的方式生成自增长Id
/// </summary>
class Program
{
static void Main(string[] args)
{
long id = new ClientIdService().GetIdByWCF();
Console.WriteLine(id);
Console.Read();
}
}
}
Program
四、结束
一切按计划执行,输出一个唯一的Id,本文完整Demo下载。
自增长主键Id的另类设计的更多相关文章
- ibatis annotations 注解方式返回刚插入的自增长主键ID的值
mybatis提供了注解方式编写sql,省去了配置并编写xml mapper文件的麻烦,今天遇到了获取自增长主键返回值的问题,发现相关问答比较少,还好最后还是圆满解决了,现把重点记录一下,解决问题的关 ...
- ibatis annotations 注解方式返回刚插入的自增长主键ID的值--转
原文地址:http://www.blogs8.cn/posts/WWpt35l mybatis提供了注解方式编写sql,省去了配置并编写xml mapper文件的麻烦,今天遇到了获取自增长主键返回值的 ...
- 自增长主键Id的设计
http://www.cnblogs.com/lhking/p/3945865.html
- C#连接mysql数据库插入数据后获取自增长主键ID值
From: http://blog.csdn.net/zbc496218/article/details/51082983 MySqlConnection conn = new MySqlConnec ...
- Access获取新插入数据的自增长主键Id
sqlserver有output,Oracle有Sequence.Access用下面的方法: public int InsertEx(User user) { ; using (OleDbConnec ...
- mysql 插入数据失败防止自增长主键增长的方法
mysql设置了自增长主键ID,插入失败的那个自增长ID也加一的,比如失败5个,下一个成功的不是在原来最后成功数据加1,而是直接变成加6了,失败次数一次就自动增长1了,能不能让失败的不增长的? 或者说 ...
- mybaits返回自增主键ID
mybaits两种获取自增主键ID的方法:一种是使用useGeneratedKeys,第二种是selectKey方法获取. useGeneratedKeys <insert id="i ...
- Mycat探索之旅(4)----Mycat的自增长主键和返回生成主键ID的实现
说明:MyCAT自增长主键和返回生成主键ID的实现 1) mysql本身对非自增长主键,使用last_insert_id()是不会返回结果的,只会返回0:这里做一个简单的测试 创建测试表 ------ ...
- mybatis添加记录时返回主键id
参考:mybatis添加记录时返回主键id 场景 有些时候我们在添加记录成功后希望能直接获取到该记录的主键id值,而不需要再执行一次查询操作.在使用mybatis作为ORM组件时,可以很方便地达到这个 ...
随机推荐
- C#循环语句练习2
1.羽毛球拍15元,球3元,水2元,有200元,每一种至少买一件,问有几种买法? 2.洗发水15元,牙刷5元,香皂2元,有150元,每一种至少买一件,问有几种买法? 3.用100元钱买100只鸡,公鸡 ...
- quartz(1)
关于定时任务的操作方法,java语言本身具有 Timer 来解决,但Timer 作用起来不是特别的舒服,由于项目的需要,使用了Quartz 这个调度框架,现把学习过程记录下来,方便以后查阅. 本教程是 ...
- DNS学习笔记之DNS理论知识
DNS: Domain Name System (将域名和ip地址相互转化) 域名是一个范围,例如baidu.com,.com.而www.baidu.com是个主机名,即FQDN: Full Qual ...
- sublime Text 2 配置以及 Python环境搭建
在搭建Python环境前,先设置好Sublime Text 2的环境. 一.Sublime Text 2配置: 1.离线安装: Perferences-->Browser Packages -- ...
- 转:Android开发:使用JNI读取应用签名
博文转自http://www.tuicool.com/articles/UVjme2r,感谢博主的分享 为了防止被反编译,打算把关键代码写到so里(比如加解密),在so里加上判断APk包签名是否一致的 ...
- JavaScript 的循环语句语法摘要
if条件语句语法: if(condition){ statements; } 理解:圆括号里的是条件参数 ,花括号里的为执行的语句. 示例代码:if(1>2){ alert("Th ...
- CentOS 6 到 CentOS 7
redhat提供了一个redhat-upgrade-tool的升级工具: [1]配置软件源 # vim /etc/yum.repos.d/upgrade.repo [upgrade] name=u ...
- javascript操控浏览器
测试环境为Chrome浏览器47.0.2526.106 m 测试窗口为F12->Console 跳转网页 // 跳转到百度 window.location.href = "https: ...
- webstorm 运行配置gulp
打开Run/Debug Configurations,设置
- Android 增量更新(BSDiff / bspatch)
Android 增量更新 BSDiff / bspatchhttp://www.daemonology.net/bsdiff/android的代码目录下 \external\bsdiff bsdiff ...