谁有电影性瘾日记微信美化百度网盘链接接 微信给个红包几块钱也可以

2011年12月 .NET技术大版内专家分月排行榜第三
2017年2月 总版技术专家分月排行榜第三
2017年5月 .NET技术大版内专家分月排行榜第一2017年4月 .NET技术大版内专家分月排行榜第一2017年3月 .NET技术大版内专家分月排行榜第一2017年2月 .NET技术大版内专家分月排行榜第一2016年10月 .NET技术大版内专家分月排行榜第一2016年8月 .NET技术大版内专家分月排行榜第一2016年7月 .NET技术大版内专家分月排行榜第一
2017年2月 总版技术专家分月排行榜第三
2017年5月 .NET技术大版内专家分月排行榜第一2017年4月 .NET技术大版内专家分月排行榜第一2017年3月 .NET技术大版内专家分月排行榜第一2017年2月 .NET技术大版内专家分月排行榜第一2016年10月 .NET技术大版内专家分月排行榜第一2016年8月 .NET技术大版内专家分月排行榜第一2016年7月 .NET技术大版内专家分月排行榜第一
2017年2月 总版技术专家分月排行榜第三
2017年5月 .NET技术大版内专家分月排行榜第一2017年4月 .NET技术大版内专家分月排行榜第一2017年3月 .NET技术大版内专家分月排行榜第一2017年2月 .NET技术大版内专家分月排行榜第一2016年10月 .NET技术大版内专家分月排行榜第一2016年8月 .NET技术大版内专家分月排行榜第一2016年7月 .NET技术大版内专家分月排行榜第一
2017年2月 总版技术专家分月排行榜第三
2017年5月 .NET技术大版内专家分月排行榜第一2017年4月 .NET技术大版内专家分月排行榜第一2017年3月 .NET技术大版内专家分月排行榜第一2017年2月 .NET技术大版内专家分月排行榜第一2016年10月 .NET技术大版内专家分月排行榜第一2016年8月 .NET技术大版内专家分月排行榜第一2016年7月 .NET技术大版内专家分月排行榜第一
2017年2月 总版技术专家分月排行榜第三
2017年5月 .NET技术大版内专家分月排行榜第一2017年4月 .NET技术大版内专家分月排行榜第一2017年3月 .NET技术大版内专家分月排行榜第一2017年2月 .NET技术大版内专家分月排行榜第一2016年10月 .NET技术大版内专家分月排行榜第一2016年8月 .NET技术大版内专家分月排行榜第一2016年7月 .NET技术大版内专家分月排行榜第一
2017年2月 总版技术专家分月排行榜第三
2017年5月 .NET技术大版内专家分月排行榜第一2017年4月 .NET技术大版内专家分月排行榜第一2017年3月 .NET技术大版内专家分月排行榜第一2017年2月 .NET技术大版内专家分月排行榜第一2016年10月 .NET技术大版内专家分月排行榜第一2016年8月 .NET技术大版内专家分月排行榜第一2016年7月 .NET技术大版内专家分月排行榜第一
2017年2月 总版技术专家分月排行榜第三
2017年5月 .NET技术大版内专家分月排行榜第一2017年4月 .NET技术大版内专家分月排行榜第一2017年3月 .NET技术大版内专家分月排行榜第一2017年2月 .NET技术大版内专家分月排行榜第一2016年10月 .NET技术大版内专家分月排行榜第一2016年8月 .NET技术大版内专家分月排行榜第一2016年7月 .NET技术大版内专家分月排行榜第一
2017年2月 总版技术专家分月排行榜第三
2017年5月 .NET技术大版内专家分月排行榜第一2017年4月 .NET技术大版内专家分月排行榜第一2017年3月 .NET技术大版内专家分月排行榜第一2017年2月 .NET技术大版内专家分月排行榜第一2016年10月 .NET技术大版内专家分月排行榜第一2016年8月 .NET技术大版内专家分月排行榜第一2016年7月 .NET技术大版内专家分月排行榜第一
2017年2月 总版技术专家分月排行榜第三
2017年5月 .NET技术大版内专家分月排行榜第一2017年4月 .NET技术大版内专家分月排行榜第一2017年3月 .NET技术大版内专家分月排行榜第一2017年2月 .NET技术大版内专家分月排行榜第一2016年10月 .NET技术大版内专家分月排行榜第一2016年8月 .NET技术大版内专家分月排行榜第一2016年7月 .NET技术大版内专家分月排行榜第一
本帖子已过去太久远了,不再提供回复功能。全局唯一ID在分布式系统中用来做什么用? - 知乎151被浏览4922分享邀请回答0添加评论分享收藏感谢收起与世界分享知识、经验和见解用户名:gridmix
文章数:32
访问量:14127
注册日期:
阅读量:1297
阅读量:3317
阅读量:435792
阅读量:1123144
51CTO推荐博文
又一个多月没冒泡了,其实最近学了些东西,但是没有安排时间整理成博文,后续再奉上。最近还写了一个发邮件的组件以及性能测试请看
,还弄了个MSSQL参数化语法生成器,会在9月整理出来,有兴趣的园友可以关注下我的博客。分享原由,最近公司用到,并且在找最合适的方案,希望大家多参与讨论和提出新方案。我和我的小伙伴们也讨论了这个主题,我受益匪浅啊……博文示例:1. 2. 今天分享的主题是:如何在高并发分布式系统中生成全局唯一Id。但这篇博文实际上是“半分享半讨论”的博文:1) 半分享是我将说下我所了解到的关于今天主题所涉及的几种方案。2) 半讨论是我希望大家对各个方案都说说自己的见解,更加希望大家能提出更好的方案。(我还另外提问在此:上面已有几位园友回复(感谢dudu站长的参与),若你们有见解和新方案就在本博文留言吧,方便我整理更新到博文中,谢谢!)我了解的方案如下……………………………………………………………………1、 使用数据库自增Id优势:编码简单,无需考虑记录唯一标识的问题。缺陷:1) 在大表做水平分表时,就不能使用自增Id,因为Insert的记录插入到哪个分表依分表规则判定决定,若是自增Id,各个分表中Id就会重复,在做查询、删除时就会有异常。2) 在对表进行高并发单记录插入时需要加入事物机制,否则会出现Id重复的问题。3) 在业务上操作父、子表(即关联表)插入时,需要在插入数据库之前获取max(id)用于标识父表和子表关系,若存在并发获取max(id)的情况,max(id)会同时被别的线程获取到。4) 等等。结论:适合小应用,无需分表,没有高并发性能要求。2、 单独开一个数据库,获取全局唯一的自增序列号或各表的MaxId1) 使用自增序列号表专门一个数据库,生成序列号。开启事物,每次操作插入时,先将数据插入到序列表并返回自增序列号用于做为唯一Id进行业务数据插入。注意:需要定期清理序列表的数据以保证获取序列号的效率;插入序列表记录时要开启事物。使用此方案的问题是:每次的查询序列号是一个性能损耗;如果这个序列号列暴了,那就杯具了,你不知道哪个表使用了哪个序列,所以就必须换另一种唯一Id方式如GUID。2) 使用MaxId表存储各表的MaxId值专门一个数据库,记录各个表的MaxId值,建一个存储过程来取Id,逻辑大致为:开启事物,对于在表中不存在记录,直接返回一个默认值为1的键值,同时插入该条记录到table_key表中。而对于已存在的记录,key值直接在原来的key基础上加1更新到MaxId表中并返回key。使用此方案的问题是:每次的查询MaxId是一个性能损耗;不过不会像自增序列表那么容易列暴掉,因为是摆表进行划分的。详细可参考:我截取此文中的sql语法如下:第一步:创建表createtabletable_key
table_name
varchar(50) notnullprimarykey,
intnotnull
)第二步:创建存储过程来取自增IDcreateprocedureup_get_table_key
@table_name
varchar(50),
@key_value
declare@keyint
--initialize the key with 1
--whether the specified table is exist
if notexists(selecttable_name fromtable_key wheretable_name=@table_name)
insertintotable_key values(@table_name,@key)
--default key vlaue:1
-- step increase
select@key=key_value fromtable_key with(nolock) wheretable_name=@table_name
set@key=@key+1
--update the key value by table name
updatetable_key setkey_value=@keywheretable_name=@table_name
--set ouput value
set@key_value=@key
--commit tran
committran
if @@error&0
rollbacktran
end感谢园友的好建议:1. ()建议给table_key中为每个表初始化一条key为1的记录,这样就不用每次if来判断了。2. ()建议给存储过程中提高一下,因为出现在CS代码层上使用如下事物代码会导致并发重复问题.TransactionOptions option = newTransactionOptions();
option.IsolationLevel = IsolationLevel.ReadU
option.Timeout = newTimeSpan(0, 10, 0);
using(TransactionScope transaction = newTransactionScope(TransactionScopeOption.RequiresNew, option))
//调用存储过程
}在咨询过DBA后,这个存储过程提高数据库隔离级别会加大数据库访问压力,导致响应超时问题。所以这个建议我们只能在代码编写宣导上做。3. ()存储过程中不使用事物,一旦使用到事物性能就急剧下滑。直接使用UPDATE获取到的更新锁,即SQL SERVER会保证UPDATE的顺序执行。(已在用户过千万的并发系统中使用)createprocedure[dbo].[up_get_table_key]
@table_name
varchar(50),
@key_value
SETNOCOUNT ON;
DECLARE@maxId INT
UPDATEtable_key
SET@maxId = key_value,key_value = key_value + 1
WHEREtable_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较短,友好性比较好。(强烈推荐)3、 Sequence特性这个特性在SQL Server 2012、Oracle中可用。这个特性是数据库级别的,允许在多个表之间共享序列号。它可以解决分表在同一个数据库的情况,但倘若分表放在不同数据库,那将共享不到此序列号。(eg:Sequence使用场景:你需要在多个表之间公用一个流水号。以往的做法是额外建立一个表,然后存储流水号)相关Sequence特性资料:结论:适用中型应用,此方案不能完全解决分表问题,而且无法满足高并发性能要求。同时也存在单点问题,如果这个数据库cash掉的话……4、 通过数据库集群编号+集群内的自增类型两个字段共同组成唯一主键优点:实现简单,维护也比较简单。缺点:关联表操作相对比较复杂,需要两个字段。并且业务逻辑必须是一开始就设计为处理复合主键的逻辑,倘若是到了后期,由单主键转为复合主键那改动成本就太大了。结论:适合大型应用,但需要业务逻辑配合处理复合主键。5、 通过设置每个集群中自增 ID 起始点(auto_increment_offset),将各个集群的ID进行绝对的分段来实现全局唯一。当遇到某个集群数据增长过快后,通过命令调整下一个 ID 起始位置跳过可能存在的冲突。优点:实现简单,且比较容易根据 ID 大小直接判断出数据处在哪个集群,对应用透明。缺点:维护相对较复杂,需要高度关注各个集群 ID 增长状况。结论:适合大型应用,但需要高度关注各个集群 ID 增长状况。6、 GUID通常表示成32个16进制数字(0-9,A-F)组成的字符串,如:{21EC2020-3AEA-1069-A2DD-D},它实质上是一个128位长的二进制整数。GUID制定的算法中使用到用户的网卡MAC地址,以保证在计算机集群中生成唯一GUID;在相同计算机上随机生成两个相同GUID的可能性是非常小的,但并不为0。所以,用于生成GUID的算法通常都加入了非随机的参数(如时间),以保证这种重复的情况不会发生。优点:GUID是最简单的方案,跨平台,跨语言,跨业务逻辑,全局唯一的Id,数据间同步、迁移都能简单实现。缺点:1) 存储占了32位,且无可读性,返回GUID给客户显得很不专业;2) 占用了珍贵的聚集索引,一般我们不会根据GUID去查单据,并且插入时因为GUID是无需的,在聚集索引的排序规则下可能移动大量的记录。有两位园友主推GUID,无须顺序GUID方案原因如下: GUID无序在并发下效率高,并且一个数据页内添加新行,是在B树内增加,本质没有什么数据被移动,唯一可能的,是页填充因子满了,需要拆页。而GUID方案导致的拆页比顺序ID要低太多了(数据库不是很懂,暂时无法断定,大家自己认识) 我们要明白id是什么,是身份标识,标识身份是id最大的业务逻辑,不要引入什么时间,什么用户业务逻辑,那是另外一个字段干的事,使用base64(guid,uuid),是通盘考虑,完全可以更好的兼容nosql,key-value存储。(推荐),但是倘若你系统一开始没有规划一个业务Id,那么将导致大量的改动,所以这个方案的最佳状态是一开始就设计业务Id,当然业务Id的唯一性也是我们要考虑的。结论:适合大型应用;生成的Id不够友好;占据了32位;索引效率较低。改进:1) (提点)在SQL Server 2005中新增了NEWSEQUENTIALID函数。详细请看:在指定计算机上创建大于先前通过该函数生成的任何 GUID 的 GUID。 newsequentialid 产生的新的值是有规律的,则索引B+树的变化是有规律的,就不会导致索引列插入时移动大量记录的问题。但一旦服务器重新启动,其再次生成的GUID可能反而变小(但仍然保持唯一)。这在很大程度上提高了索引的性能,但并不能保证所生成的GUID一直增大。SQL的这个函数产生的GUID很简单就可以预测,因此不适合用于安全目的。a) 只能做为数据库列的DEFAULT VALUE,不能执行类似SELECT NEWSEQUENTIALID()的语句.b) 如何获得生成的GUID.如果生成的GUID所在字段做为外键要被其他表使用,我们就需要得到这个生成的值。通常,PK是一个IDENTITY字段,我们可以在INSERT之后执行 SELECT SCOPE_IDENTITY()来获得新生成的ID,但是由于NEWSEQUENTIALID()不是一个INDETITY类型,这个办法是做不到了,而他本身又只能在默认值中使用,不可以事先SELECT好再插入,那么我们如何得到呢?有以下两种方法:--1. 定义临时表变量
DECLARE@outputTable TABLE(ID uniqueidentifier)
INSERTINTOTABLE1(col1, col2)
OUTPUTINSERTED.ID INTO@outputTable
VALUES('value1', 'value2')
SELECTID FROM@outputTable
--2. 标记ID字段为ROWGUID(一个表只能有一个ROWGUID)
INSERTINTOTABLE1(col1, col2)
VALUES('value1', 'value2')
--在这里,ROWGUIDCOL其实相当于一个别名
SELECTROWGUIDCOL FROMTABLE1结论:适合大型应用,解决了GUID无序特性导致索引列插入移动大量记录的问题。但是在关联表插入时需要返回数据库中生成的GUID;生成的Id不够友好;占据了32位。2) “COMB”(combined guid/timestamp,意思是:组合GUID/时间截)(感谢: , )COMB数据类型的基本设计思路是这样的:既然GUID数据因毫无规律可言造成索引效率低下,影响了系统的性能,那么能不能通过组合的方式,保留GUID的10个字节,用另6个字节表示GUID生成的时间(DateTime),这样我们将时间信息与GUID组合起来,在保留GUID的唯一性的同时增加了有序性,以此来提高索引效率。在NHibernate中,COMB型主键的生成代码如下所示:/// &summary& /// Generate a new &see cref="Guid"/& using the comb algorithm.
/// &/summary&
privateGuid GenerateComb()
byte[] guidArray = Guid.NewGuid().ToByteArray();
DateTime baseDate = newDateTime();
DateTime now = DateTime.N
// Get the days and milliseconds which will be used to build
//the byte string
TimeSpan days = newTimeSpan(now.Ticks - baseDate.Ticks);
TimeSpan msecs = now.TimeOfD
// 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);
returnnewGuid(guidArray);
}结论:适合大型应用。即保留GUID的唯一性的同时增加了GUID有序性,提高了索引效率;解决了关联表业务问题;生成的Id不够友好;占据了32位。(强烈推荐)3) 长度问题,使用Base64或Ascii85编码解决。(要注意的是上述有序性方案在进行编码后也会变得无序)如:GUID:{3FF89-11D3-9A0C-1}当需要使用更少的字符表示GUID时,可能会使用Base64或Ascii85编码。Base64编码的GUID有22-24个字符,如:7QDBkvCA1+B9K/U0vrQx1A7QDBkvCA1+B9K/U0vrQx1A==Ascii85编码后是20个字符,如:5:$Hj:Pf\4RLB9%kU\Lj代码如:Guid guid = Guid.NewGuid();byte[] buffer = guid.ToByteArray();var shortGuid = Convert.ToBase64String(buffer);结论:适合大型应用,缩短GUID的长度。生成的Id不够友好;索引效率较低。7、 GUID TO Int64对于GUID的可读性,有园友给出如下方案:(感谢:)/// &summary&
/// 根据GUID获取19位的唯一数字序列
/// &/summary&
publicstaticlongGuidToLongID()
byte[] buffer = Guid.NewGuid().ToByteArray();
returnBitConverter.ToInt64(buffer, 0);
}即将GUID转为了19位数字,数字反馈给客户可以一定程度上缓解友好性问题。EG:GUID: cfdab168-211d-41e6-8634-ef5ba6502a22 (不友好)Int64: 9746068 (友好性还行)不过我的小伙伴说ToInt64后就不唯一了。因此我专门写了个并发测试程序,后文将给出测试结果截图及代码简单说明。(唯一性、业务适合性是可以权衡的,这个唯一性肯定比不过GUID的,一般程序上都会安排错误处理机制,比如异常后执行一次重插的方案……)结论:适合大型应用,生成相对友好的Id(纯数字)------因简单和业务友好性而推荐。8、 自己写编码规则优点:全局唯一Id,符合业务后续长远的发展(可能具体业务需要自己的编码规则等等)。缺陷:根据具体编码规则实现而不同;还要考虑倘若主键在业务上允许改变的,会带来外键同步的麻烦。我这边写两个编码规则方案:(可能不唯一,只是个人方案,也请大家提出自己的编码规则)1) 12位年月日时分秒+3位服务器编码+3位表编码+5位随机码 (这样就完全单机完成生成全局唯一编码)---共23位缺陷:因为附带随机码,所以编码缺少一定的顺序感。(生成高唯一性随机码的方案稍后给给出程序)2) 12位年月日时分秒+3位服务器编码+3位表编码+5位流水码 (这样流水码就需要结合数据库和缓存)---共23位缺陷:因为使用到流水码,流水码的生成必然会遇到和MaxId、序列表、Sequence方案中类似的问题(为什么没有毫秒?毫秒也不具备业务可读性,我改用5位随机码、流水码代替,推测1秒内应该不会下99999[五位]条语法)结论:适合大型应用,从业务上来说,有一个规则的编码能体现产品的专业成度。(强烈推荐)GUID生成Int64值后是否还具有唯一性测试测试环境主要测试思路:1. 根据内核数使用多线程并发生成Guid后再转为Int64位值,放入集合A、B、…N,多少个线程就有多少个集合。2. 再使用Dictionary字典高效查key的特性,将步骤1中生成的多个集合全部加到Dictionary中,看是否有重复值。示例注解:测了 Dictionary&long,bool& 最大容量就在5999470左右,所以每次并发生成的唯一值总数控制在此范围内,让测试达到最有效话。主要代码:for(inti = 0; i &= Environment.ProcessorCount - 1; i++)
ThreadPool.QueueUserWorkItem(
List&long& tempList = list asList&long&;
for(intj = 1; j & listL j++)
byte[] buffer = Guid.NewGuid().ToByteArray();
tempList.Add(BitConverter.ToInt64(buffer, 0));
barrier.SignalAndWait();
}, totalList[i]);
}测试数据截图:数据一(循环1000次,测试数:)数据二(循环5000次,测试数:)--跑了一个晚上……感谢的专业回答:(大家分析下,我数学比较差,稍后再说自己的理解)GUID桶数量:(2 ^ 4) ^ 32 = 2 ^ 128Int64桶数量: 2 ^ 64倘若每个桶的机会是均等的,则每个桶的GUID数量为:(2 ^ 128) / (2 ^ 64) = 2 ^ 64 = 也就是说,其实重复的机会是有的,只是概率问题。楼主测试数是,发生重复的概率是:1 - ((1 - (1 / (2 ^ 64))) ^ ) ≈ 1 - ((1 - 1 / (2 ^ 64)) ^ (2 ^ 32)) & 1 - 1 + 1 / (2 ^ 32) = 1 / (2 ^ 32) ≈ 2.(唯一性、业务适合性是可以权衡的,这个唯一性肯定比不过GUID的,一般程序上都会安排错误处理机制,比如异常后执行一次重插的方案……)(唯一性、业务适合性是可以权衡的,这个唯一性肯定比不过GUID的,一般程序上都会安排错误处理机制,比如异常后执行一次重插的方案……)结论:GUID转为Int64值后,也具有高唯一性,可以使用与项目中。Random生成高唯一性随机码我使用了五种Random生成方案,要Random生成唯一主要因素就是种子参数要唯一。(这是比较久以前写的测试案例了,一直找不到合适的博文放,今天终于找到合适的地方了)不过该测试是在单线程下的,多线程应使用不同的Random实例,所以对结果影响不会太大。1. 使用Environment.TickCount做为Random参数(即Random的默认参数),重复性最大。2. 使用DateTime.Now.Ticks做为Random参数,存在重复。3. 使用unchecked((int)DateTime.Now.Ticks)做为Random参数,存在重复。4. 使用Guid.NewGuid().GetHashCode()做为random参数,测试不存在重复(或存在性极小)。5. 使用RNGCryptoServiceProvider做为random参数,测试不存在重复(或存在性极小)。即:static int GetRandomSeed()
byte[] bytes = new byte[4];
System.Security.Cryptography.RNGCryptoServiceProvider rng
= new System.Security.Cryptography.RNGCryptoServiceProvider();
rng.GetBytes(bytes);
return BitConverter.ToInt32(bytes, 0);
}测试结果:结论:随机码使用RNGCryptoServiceProvider或Guid.NewGuid().GetHashCode()生成的唯一性较高。一些精彩评论(部分更新到原博文对应的地方)一、数据库文件体积只是一个参考值,可水平扩展系统性能(如nosql,缓存系统)并不和文件体积有高指数的线性相关。如taobao/qq的系统比拼byte系统慢,关键在于索引的命中率,缓存,系统的水平扩展。如果数据库很少,你搞这么多byte能提高性能?如果数据库很大,你搞这么多byte不兼容索引不兼容缓存,不是害自已吗?如果数据库要求伸缩性,你搞这么多byte,需要不断改程序,不是自找苦吗?如果数据库要求移植性,你搞这么多byte,移植起来不如重新设计,这是不是很多公司不断加班的原因?不依赖于数据存储系统是分层设计思想的精华,实现战略性能最大化,而不是追求战术单机性能最大化。不要迷信数据库性能,不要迷信三范式,不要使用外键,不要使用byte,不要使用自增id,不要使用存储过程,不要使用内部函数,不要使用非标准sql,存储系统只做存储系统的事。当出现系统性能时,如此设计的数据库可以更好的实现迁移数据库(如mysql-&oracle),实现nosql改造((mongodb/hadoop),实现key-value缓存(redis,memcache)。二、很多程序员有对性能认识有误区,如使用存储过程代替正常程序,其实使用存储过程只是追求单服务器的高性能,当需要服务器水平扩展时,存储过程中的业务逻辑就是你的噩运。三、除数字日期,能用字符串存储的字段尽量使用字符串存储,不要为节省那不值钱的1个g的硬盘而使用类似字节之类的字段,进而大幅牺牲系统可伸缩性和可扩展性。不要为了追求所谓的性能,引入byte,使用byte注定是短命和难于移植,想想为什么html,email一直流行,因为它们使用的是字符串表示法,只要有人类永远都能解析,如email把二进制转成base64存储。除了实时系统,视频外,建议使用字符串来存储数据,系统性能的关键在于分布式,在于水平扩展。原文地址:
了这篇文章
类别:┆阅读(0)┆评论(0)2032人阅读
IT技术(57)
java技术(55)
遇到的各种疑难杂症及解决方案(6)
又一个多月没冒泡了,其实最近学了些东西,但是没有安排时间整理成博文,后续再奉上。最近还写了一个发邮件的组件以及性能测试请看&&,还弄了个MSSQL参数化语法生成器,会在9月整理出来,有兴趣的园友可以关注下我的博客。
分享原由,最近公司用到,并且在找最合适的方案,希望大家多参与讨论和提出新方案。我和我的小伙伴们也讨论了这个主题,我受益匪浅啊……
博文示例:
1.&&&&&&&&&
2.&&&&&&&&&
今天分享的主题是:如何在高并发分布式系统中生成全局唯一Id。
但这篇博文实际上是“半分享半讨论”的博文:
1)&&&&&&&&&半分享是我将说下我所了解到的关于今天主题所涉及的几种方案。
2)&&&&&&&&&半讨论是我希望大家对各个方案都说说自己的见解,更加希望大家能提出更好的方案。(我还另外提问在此:上面已有几位园友回复(感谢dudu站长的参与),若你们有见解和新方案就在本博文留言吧,方便我整理更新到博文中,谢谢!)
我了解的方案如下……………………………………………………………………
1、&&使用数据库自增Id
优势:编码简单,无需考虑记录唯一标识的问题。
1)&&&&&&&&&在大表做水平分表时,就不能使用自增Id,因为Insert的记录插入到哪个分表依分表规则判定决定,若是自增Id,各个分表中Id就会重复,在做查询、删除时就会有异常。
2)&&&&&&&&&在对表进行高并发单记录插入时需要加入事物机制,否则会出现Id重复的问题。
3)&&&&&&&&&在业务上操作父、子表(即关联表)插入时,需要在插入数据库之前获取max(id)用于标识父表和子表关系,若存在并发获取max(id)的情况,max(id)会同时被别的线程获取到。
4)&&&&&&&&&等等。
结论:适合小应用,无需分表,没有高并发性能要求。
2、&&单独开一个数据库,获取全局唯一的自增序列号或各表的MaxId
1)&&&&&&&&&使用自增序列号表
专门一个数据库,生成序列号。开启事物,每次操作插入时,先将数据插入到序列表并返回自增序列号用于做为唯一Id进行业务数据插入。
注意:需要定期清理序列表的数据以保证获取序列号的效率;插入序列表记录时要开启事物。
使用此方案的问题是:每次的查询序列号是一个性能损耗;如果这个序列号列暴了,那就杯具了,你不知道哪个表使用了哪个序列,所以就必须换另一种唯一Id方式如GUID。
2)&&&&&&&&&使用MaxId表存储各表的MaxId值
专门一个数据库,记录各个表的MaxId值,建一个存储过程来取Id,逻辑大致为:开启事物,对于在表中不存在记录,直接返回一个默认值为1的键值,同时插入该条记录到table_key表中。而对于已存在的记录,key值直接在原来的key基础上加1更新到MaxId表中并返回key。
使用此方案的问题是:每次的查询MaxId是一个性能损耗;不过不会像自增序列表那么容易列暴掉,因为是摆表进行划分的。
详细可参考:
&&&&&&&&&&&&&&&&&&&我截取此文中的sql语法如下:
第一步:创建表
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
&&&&&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
感谢园友的好建议:
1.&&&&&&&&&()建议给table_key中为每个表初始化一条key为1的记录,这样就不用每次if来判断了。
2.&&&&&&&&&()建议给存储过程中提高一下,因为出现在CS代码层上使用如下事物代码会导致并发重复问题.
TransactionOptions option =
new&TransactionOptions();
option.IsolationLevel = IsolationLevel.ReadU
option.Timeout =
new&TimeSpan(0, 10, 0);
using&(TransactionScope transaction =
new&TransactionScope(TransactionScopeOption.RequiresNew, option))
&&&&&&&&//调用存储过程
在咨询过DBA后,这个存储过程提高数据库隔离级别会加大数据库访问压力,导致响应超时问题。所以这个建议我们只能在代码编写宣导上做。
3.&&&&&&&&&()存储过程中不使用事物,一旦使用到事物性能就急剧下滑。直接使用UPDATE获取到的更新锁,即SQL
SERVER会保证UPDATE的顺序执行。(已在用户过千万的并发系统中使用)
create&procedure&[dbo].[up_get_table_key]
&&&@table_name&&&&
varchar(50),
&&&@key_value&&&&&
int&output
&&&&SET&NOCOUNT
&&&&DECLARE&@maxId
&&&&UPDATE&table_key
&&&&SET&@maxId = key_value,key_value = key_value
&&&&WHERE&table_name=@table_name
&&&&SELECT&@maxId
结论:适用中型应用,此方案解决了分表,关联表插入记录的问题。但是无法满足高并发性能要求。同时也存在单点问题,如果这个数据库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较短,友好性比较好。(强烈推荐)
3、&&Sequence特性
这个特性在SQL Server 2012、Oracle中可用。这个特性是数据库级别的,允许在多个表之间共享序列号。它可以解决分表在同一个数据库的情况,但倘若分表放在不同数据库,那将共享不到此序列号。(eg:Sequence使用场景:你需要在多个表之间公用一个流水号。以往的做法是额外建立一个表,然后存储流水号)
相关Sequence特性资料:
结论:适用中型应用,此方案不能完全解决分表问题,而且无法满足高并发性能要求。同时也存在单点问题,如果这个数据库cash掉的话……
4、&&通过数据库集群编号+集群内的自增类型两个字段共同组成唯一主键
优点:实现简单,维护也比较简单。
缺点:关联表操作相对比较复杂,需要两个字段。并且业务逻辑必须是一开始就设计为处理复合主键的逻辑,倘若是到了后期,由单主键转为复合主键那改动成本就太大了。
结论:适合大型应用,但需要业务逻辑配合处理复合主键。
5、&&通过设置每个集群中自增&ID&起始点(auto_increment_offset),将各个集群的ID进行绝对的分段来实现全局唯一。当遇到某个集群数据增长过快后,通过命令调整下一个&ID&起始位置跳过可能存在的冲突。
优点:实现简单,且比较容易根据&ID&大小直接判断出数据处在哪个集群,对应用透明。缺点:维护相对较复杂,需要高度关注各个集群&ID&增长状况。
结论:适合大型应用,但需要高度关注各个集群&ID&增长状况。
GUID通常表示成32个16进制数字(0-9,A-F)组成的字符串,如:{21EC2020-3AEA-1069-A2DD-D},它实质上是一个128位长的二进制整数。
GUID制定的算法中使用到用户的网卡MAC地址,以保证在计算机集群中生成唯一GUID;在相同计算机上随机生成两个相同GUID的可能性是非常小的,但并不为0。所以,用于生成GUID的算法通常都加入了非随机的参数(如时间),以保证这种重复的情况不会发生。
优点:GUID是最简单的方案,跨平台,跨语言,跨业务逻辑,全局唯一的Id,数据间同步、迁移都能简单实现。
1)&&&&&&&&&存储占了32位,且无可读性,返回GUID给客户显得很不专业;
2)&&&&&&&&&占用了珍贵的聚集索引,一般我们不会根据GUID去查单据,并且插入时因为GUID是无需的,在聚集索引的排序规则下可能移动大量的记录。
有两位园友主推GUID,无须顺序GUID方案原因如下:
&&&&&&&&&&&GUID无序在并发下效率高,并且一个数据页内添加新行,是在B树内增加,本质没有什么数据被移动,唯一可能的,是页填充因子满了,需要拆页。而GUID方案导致的拆页比顺序ID要低太多了(数据库不是很懂,暂时无法断定,大家自己认识)
&&&&&&&&&&&&&&&&我们要明白id是什么,是身份标识,标识身份是id最大的业务逻辑,不要引入什么时间,什么用户业务逻辑,那是另外一个字段干的事,使用base64(guid,uuid),是通盘考虑,完全可以更好的兼容nosql,key-value存储。
(推荐),但是倘若你系统一开始没有规划一个业务Id,那么将导致大量的改动,所以这个方案的最佳状态是一开始就设计业务Id,当然业务Id的唯一性也是我们要考虑的。
结论:适合大型应用;生成的Id不够友好;占据了32位;索引效率较低。
1)&&&&&&&&&(提点)在SQL
Server 2005中新增了NEWSEQUENTIALID函数。
详细请看:
在指定计算机上创建大于先前通过该函数生成的任何&GUID&的&GUID。&newsequentialid&产生的新的值是有规律的,则索引B+树的变化是有规律的,就不会导致索引列插入时移动大量记录的问题。
但一旦服务器重新启动,其再次生成的GUID可能反而变小(但仍然保持唯一)。这在很大程度上提高了索引的性能,但并不能保证所生成的GUID一直增大。SQL的这个函数产生的GUID很简单就可以预测,因此不适合用于安全目的。
a)&&&&&&&&&只能做为数据库列的DEFAULT VALUE,不能执行类似SELECT NEWSEQUENTIALID()的语句.
b)&&&&&&&&&如何获得生成的GUID.
如果生成的GUID所在字段做为外键要被其他表使用,我们就需要得到这个生成的值。通常,PK是一个IDENTITY字段,我们可以在INSERT之后执行&SELECT SCOPE_IDENTITY()来获得新生成的ID,但是由于NEWSEQUENTIALID()不是一个INDETITY类型,这个办法是做不到了,而他本身又只能在默认值中使用,不可以事先SELECT好再插入,那么我们如何得到呢?有以下两种方法:
--1. 定义临时表变量
DECLARE&@outputTable
TABLE(ID uniqueidentifier)
INSERT&INTO&TABLE1(col1,
OUTPUT&INSERTED.ID
INTO&@outputTable
VALUES('value1',
FROM&@outputTable
--2. 标记ID字段为ROWGUID(一个表只能有一个ROWGUID)
INSERT&INTO&TABLE1(col1,
VALUES('value1',
--在这里,ROWGUIDCOL其实相当于一个别名
SELECT&ROWGUIDCOL
FROM&TABLE1
结论:适合大型应用,解决了GUID无序特性导致索引列插入移动大量记录的问题。但是在关联表插入时需要返回数据库中生成的GUID;生成的Id不够友好;占据了32位。
2)&&&&&&&&&“COMB”(combined guid/timestamp,意思是:组合GUID/时间截)
(感谢:&,&)
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();
&&&&DateTime now = DateTime.N
&&&&// 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.TimeOfD
&&&&// 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)&&&&&&&&&长度问题,使用Base64或Ascii85编码解决。(要注意的是上述有序性方案在进行编码后也会变得无序)
GUID:{3FF89-11D3-9A0C-1}
当需要使用更少的字符表示GUID时,可能会使用Base64或Ascii85编码。Base64编码的GUID有22-24个字符,如:
7QDBkvCA1+B9K/U0vrQx1A
7QDBkvCA1+B9K/U0vrQx1A==
Ascii85编码后是20个字符,如:
5:$Hj:Pf\4RLB9%kU\Lj
&&&&&&&&&&&&&&&&&&&代码如:
&&&&&&&&&Guid guid = Guid.NewGuid();
&&&&&&&&&byte[] buffer = guid.ToByteArray();
&&&&&&&&&var shortGuid = Convert.ToBase64String(buffer);
&&&&&&&&&&&&&&&&&&&结论:适合大型应用,缩短GUID的长度。生成的Id不够友好;索引效率较低。
7、&&GUID TO Int64
对于GUID的可读性,有园友给出如下方案:(感谢:)
/// &summary&
/// 根据GUID获取19位的唯一数字序列
/// &/summary&
public&static&long&GuidToLongID()
&&&&byte[] buffer = Guid.NewGuid().ToByteArray();
&&&&return&BitConverter.ToInt64(buffer, 0);
即将GUID转为了19位数字,数字反馈给客户可以一定程度上缓解友好性问题。EG:
GUID: cfdab168-211d-41e6-8634-ef5ba6502a22&&&&(不友好)
Int64: 9746068&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&(友好性还行)
不过我的小伙伴说ToInt64后就不唯一了。因此我专门写了个并发测试程序,后文将给出测试结果截图及代码简单说明。
(唯一性、业务适合性是可以权衡的,这个唯一性肯定比不过GUID的,一般程序上都会安排错误处理机制,比如异常后执行一次重插的方案……)
结论:适合大型应用,生成相对友好的Id(纯数字)------因简单和业务友好性而推荐。
8、&&自己写编码规则
优点:全局唯一Id,符合业务后续长远的发展(可能具体业务需要自己的编码规则等等)。
缺陷:根据具体编码规则实现而不同;还要考虑倘若主键在业务上允许改变的,会带来外键同步的麻烦。
我这边写两个编码规则方案:(可能不唯一,只是个人方案,也请大家提出自己的编码规则)
1)&&&&&&&&&12位年月日时分秒+5位随机码+3位服务器编码&&(这样就完全单机完成生成全局唯一编码)---共20位
缺陷:因为附带随机码,所以编码缺少一定的顺序感。(生成高唯一性随机码的方案稍后给给出程序)
2)&&&&&&&&&12位年月日时分秒+5位流水码+3位服务器编码&(这样流水码就需要结合数据库和缓存)---共20位 & (将影响顺序权重大的“流水码”放前面,影响顺序权重小的服务器编码放后)
缺陷:因为使用到流水码,流水码的生成必然会遇到和MaxId、序列表、Sequence方案中类似的问题
(为什么没有毫秒?毫秒也不具备业务可读性,我改用5位随机码、流水码代替,推测1秒内应该不会下99999[五位]条语法)
结论:适合大型应用,从业务上来说,有一个规则的编码能体现产品的专业成度。(强烈推荐)
GUID生成Int64值后是否还具有唯一性测试
主要测试思路:
1.&&&&&&&&&根据内核数使用多线程并发生成Guid后再转为Int64位值,放入集合A、B、…N,多少个线程就有多少个集合。
2.&&&&&&&&&再使用Dictionary字典高效查key的特性,将步骤1中生成的多个集合全部加到Dictionary中,看是否有重复值。
示例注解:测了&Dictionary&long,bool&&最大容量就在5999470左右,所以每次并发生成的唯一值总数控制在此范围内,让测试达到最有效话。
主要代码:
for&(int&i
= 0; i &= Environment.ProcessorCount - 1; i++)
&&&&ThreadPool.QueueUserWorkItem(
&&&&&&&&(list) =&
&&&&&&&&&&&&List&long&
tempList = list as&List&long&;
&&&&&&&&&&&&for&(int&j
= 1; j & listL j++)
&&&&&&&&&&&&{
&&&&&&&&&&&&&&&&byte[] buffer = Guid.NewGuid().ToByteArray();
&&&&&&&&&&&&&&&&tempList.Add(BitConverter.ToInt64(buffer, 0));
&&&&&&&&&&&&}
&&&&&&&&&&&&barrier.SignalAndWait();
&&&&&&&&}, totalList[i]);
测试数据截图:&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
数据一(循环1000次,测试数:)
数据二(循环5000次,测试数:)--跑了一个晚上……
感谢的专业回答:(大家分析下,我数学比较差,稍后再说自己的理解)
GUID桶数量:(2 ^ 4) ^ 32 = 2 ^ 128
Int64桶数量:&2 ^ 64
倘若每个桶的机会是均等的,则每个桶的GUID数量为:
(2 ^ 128) / (2 ^ 64) = 2 ^ 64 =
也就是说,其实重复的机会是有的,只是概率问题。
楼主测试数是,发生重复的概率是:
1 - ((1 - (1 / (2 ^ 64))) ^ )&≈&1 - ((1 - 1 /&(2 ^ 64)) ^ (2 ^ 32)) & 1 - 1 + 1 / (2 ^ 32) = 1 / (2 ^ 32)&≈&2.
(唯一性、业务适合性是可以权衡的,这个唯一性肯定比不过GUID的,一般程序上都会安排错误处理机制,比如异常后执行一次重插的方案……)
(唯一性、业务适合性是可以权衡的,这个唯一性肯定比不过GUID的,一般程序上都会安排错误处理机制,比如异常后执行一次重插的方案……)
结论:GUID转为Int64值后,也具有高唯一性,可以使用与项目中。
Random生成高唯一性随机码
我使用了五种Random生成方案,要Random生成唯一主要因素就是种子参数要唯一。(这是比较久以前写的测试案例了,一直找不到合适的博文放,今天终于找到合适的地方了)
不过该测试是在单线程下的,多线程应使用不同的Random实例,所以对结果影响不会太大。
1.&&&&&&&&&使用Environment.TickCount做为Random参数(即Random的默认参数),重复性最大。
2.&&&&&&&&&使用DateTime.Now.Ticks做为Random参数,存在重复。
3.&&&&&&&&&使用unchecked((int)DateTime.Now.Ticks)做为Random参数,存在重复。
4.&&&&&&&&&使用Guid.NewGuid().GetHashCode()做为random参数,测试不存在重复(或存在性极小)。
5.&&&&&&&&&使用RNGCryptoServiceProvider做为random参数,测试不存在重复(或存在性极小)。
&&&&&&&&static int GetRandomSeed()
&&&&&&&&&&&&byte[] bytes = new byte[4];
&&&&&&&&&&&&System.Security.Cryptography.RNGCryptoServiceProvider rng
= new System.Security.Cryptography.RNGCryptoServiceProvider();
&&&&&&&&&&&&rng.GetBytes(bytes);
&&&&&&&&&&&&return BitConverter.ToInt32(bytes, 0);
测试结果:
结论:随机码使用RNGCryptoServiceProvider或Guid.NewGuid().GetHashCode()生成的唯一性较高。
一些精彩评论(部分更新到原博文对应的地方)
数据库文件体积只是一个参考值,可水平扩展系统性能(如nosql,缓存系统)并不和文件体积有高指数的线性相关。
如taobao/qq的系统比拼byte系统慢,关键在于索引的命中率,缓存,系统的水平扩展。
如果数据库很少,你搞这么多byte能提高性能?
如果数据库很大,你搞这么多byte不兼容索引不兼容缓存,不是害自已吗?
如果数据库要求伸缩性,你搞这么多byte,需要不断改程序,不是自找苦吗?
如果数据库要求移植性,你搞这么多byte,移植起来不如重新设计,这是不是很多公司不断加班的原因?
不依赖于数据存储系统是分层设计思想的精华,实现战略性能最大化,而不是追求战术单机性能最大化。
不要迷信数据库性能,不要迷信三范式,不要使用外键,不要使用byte,不要使用自增id,不要使用存储过程,不要使用内部函数,不要使用非标准sql,存储系统只做存储系统的事。当出现系统性能时,如此设计的数据库可以更好的实现迁移数据库(如mysql-&oracle),实现nosql改造((mongodb/hadoop),实现key-value缓存(redis,memcache)。
很多程序员有对性能认识有误区,如使用存储过程代替正常程序,其实使用存储过程只是追求单服务器的高性能,当需要服务器水平扩展时,存储过程中的业务逻辑就是你的噩运。
除数字日期,能用字符串存储的字段尽量使用字符串存储,不要为节省那不值钱的1个g的硬盘而使用类似字节之类的字段,进而大幅牺牲系统可伸缩性和可扩展性。
不要为了追求所谓的性能,引入byte,使用byte注定是短命和难于移植,想想为什么html,email一直流行,因为它们使用的是字符串表示法,只要有人类永远都能解析,如email把二进制转成base64存储。除了实时系统,视频外,建议使用字符串来存储数据,系统性能的关键在于分布式,在于水平扩展。
本次博文到此结束,希望大家对本次主题“如何在高并发分布式系统中生成全局唯一Id”多提出自己宝贵的意见。另外看着感觉舒服,还请多帮推荐…推荐……
& & & & & &
&&相关文章推荐
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:459163次
积分:3576
积分:3576
排名:第8800名
原创:32篇
转载:41篇
评论:76条
(2)(3)(2)(5)(1)(2)(3)(4)(1)(1)(3)(2)(3)(2)(2)(1)(1)(1)(2)(2)(1)(1)(8)(2)(10)(8)}

我要回帖

更多关于 微信美化百度网盘链接 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信