欧美三区_成人在线免费观看视频_欧美极品少妇xxxxⅹ免费视频_a级毛片免费播放_鲁一鲁中文字幕久久_亚洲一级特黄

如何在高并發(fā)分布式系統(tǒng)中生成全局唯一Id

系統(tǒng) 2146 0

又一個(gè)多月沒(méi)冒泡了,其實(shí)最近學(xué)了些東西,但是沒(méi)有安排時(shí)間整理成博文,后續(xù)再奉上。最近還寫了一個(gè)發(fā)郵件的組件以及性能測(cè)試請(qǐng)看 《NET開發(fā)郵件發(fā)送功能的全面教程(含郵件組件源碼)》 ?,還弄了個(gè)MSSQL參數(shù)化語(yǔ)法生成器,會(huì)在9月整理出來(lái),有興趣的園友可以關(guān)注下我的博客。

?

分享原由,最近公司用到,并且在找最合適的方案,希望大家多參與討論和提出新方案。我和我的小伙伴們也討論了這個(gè)主題,我受益匪淺啊……

?

博文示例:

  1. GUID生成Int64值后是否還具有唯一性測(cè)試
  2. Random生成高唯一性隨機(jī)碼

?

今天分享的主題是:如何在高并發(fā)分布式系統(tǒng)中生成全局唯一Id。

但這篇博文實(shí)際上是“半分享半討論”的博文:

1)???????? 半分享是我將說(shuō)下我所了解到的關(guān)于今天主題所涉及的幾種方案。

2)???????? 半討論是我希望大家對(duì)各個(gè)方案都說(shuō)說(shuō)自己的見(jiàn)解,更加希望大家能提出更好的方案。(我還另外提問(wèn)在此: http://q.cnblogs.com/q/53552/ 上面已有幾位園友回復(fù)(感謝dudu站長(zhǎng)的參與),若你們有見(jiàn)解和新方案就在本博文留言吧,方便我整理更新到博文中,謝謝!)

?

我了解的方案如下……………………………………………………………………

1、? 使用數(shù)據(jù)庫(kù)自增Id

優(yōu)勢(shì):編碼簡(jiǎn)單,無(wú)需考慮記錄唯一標(biāo)識(shí)的問(wèn)題。

缺陷:

1)???????? 在大表做水平分表時(shí),就不能使用自增Id,因?yàn)镮nsert的記錄插入到哪個(gè)分表依分表規(guī)則判定決定,若是自增Id,各個(gè)分表中Id就會(huì)重復(fù),在做查詢、刪除時(shí)就會(huì)有異常。

2)???????? 在對(duì)表進(jìn)行高并發(fā)單記錄插入時(shí)需要加入事物機(jī)制,否則會(huì)出現(xiàn)Id重復(fù)的問(wèn)題。

3)???????? 在業(yè)務(wù)上操作父、子表(即關(guān)聯(lián)表)插入時(shí),需要在插入數(shù)據(jù)庫(kù) 之前 獲取max(id)用于標(biāo)識(shí)父表和子表關(guān)系,若存在并發(fā)獲取max(id)的情況,max(id)會(huì)同時(shí)被別的線程獲取到。

4)???????? 等等。

結(jié)論:適合小應(yīng)用,無(wú)需分表,沒(méi)有高并發(fā)性能要求。

2、? 單獨(dú)開一個(gè)數(shù)據(jù)庫(kù),獲取全局唯一的自增序列號(hào)或各表的MaxId

1)???????? 使用自增序列號(hào)表

專門一個(gè)數(shù)據(jù)庫(kù),生成序列號(hào)。開啟事物,每次操作插入時(shí),先將數(shù)據(jù)插入到序列表并返回自增序列號(hào)用于做為唯一Id進(jìn)行業(yè)務(wù)數(shù)據(jù)插入。

注意:需要定期清理序列表的數(shù)據(jù)以保證獲取序列號(hào)的效率;插入序列表記錄時(shí)要開啟事物。

使用此方案的問(wèn)題是:每次的查詢序列號(hào)是一個(gè)性能損耗;如果這個(gè)序列號(hào)列暴了,那就杯具了,你不知道哪個(gè)表使用了哪個(gè)序列,所以就必須換另一種唯一Id方式如GUID。

2)???????? 使用MaxId表存儲(chǔ)各表的MaxId值

專門一個(gè)數(shù)據(jù)庫(kù),記錄各個(gè)表的MaxId值,建一個(gè)存儲(chǔ)過(guò)程來(lái)取Id,邏輯大致為:開啟事物,對(duì)于在表中不存在記錄,直接返回一個(gè)默認(rèn)值為1的鍵值,同時(shí)插入該條記錄到table_key表中。而對(duì)于已存在的記錄,key值直接在原來(lái)的key基礎(chǔ)上加1更新到MaxId表中并返回key。

使用此方案的問(wèn)題是:每次的查詢MaxId是一個(gè)性能損耗;不過(guò)不會(huì)像自增序列表那么容易列暴掉,因?yàn)槭菙[表進(jìn)行劃分的。

詳細(xì)可參考: 《使用MaxId表存儲(chǔ)各表的MaxId值,以獲取全局唯一Id》

?????????????????? 我截取此文中的sql語(yǔ)法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
第一步:創(chuàng)建表
create table table_key
(
??????? table_name?? varchar (50) not null primary key ,
??????? key_value??? int ???????? not null
)
?
?
第二步:創(chuàng)建存儲(chǔ)過(guò)程來(lái)取自增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

感謝園友的好建議:

  1. @輝_輝 )建議給table_key中為每個(gè)表初始化一條key為1的記錄,這樣就不用每次if來(lái)判斷了。
  2. @樂(lè)活的CodeMonkey )建議給存儲(chǔ)過(guò)程中 數(shù)據(jù)庫(kù)事物隔離級(jí)別 提高一下,因?yàn)槌霈F(xiàn)在CS代碼層上使用如下事物代碼會(huì)導(dǎo)致并發(fā)重復(fù)問(wèn)題.
1
2
3
4
5
6
7
8
TransactionOptions option = new TransactionOptions();
option.IsolationLevel = IsolationLevel.ReadUncommitted;
option.Timeout = new TimeSpan(0, 10, 0);
? ?
using (TransactionScope transaction = new TransactionScope(TransactionScopeOption.RequiresNew, option))
{
???????? //調(diào)用存儲(chǔ)過(guò)程
}

在咨詢過(guò)DBA后,這個(gè)存儲(chǔ)過(guò)程提高數(shù)據(jù)庫(kù)隔離級(jí)別會(huì)加大數(shù)據(jù)庫(kù)訪問(wèn)壓力,導(dǎo)致響應(yīng)超時(shí)問(wèn)題。所以這個(gè)建議我們只能在代碼編寫宣導(dǎo)上做。

  1. @土豆烤肉 )存儲(chǔ)過(guò)程中不使用事物,一旦使用到事物性能就急劇下滑。直接使用UPDATE獲取到的更新鎖,即SQL SERVER會(huì)保證UPDATE的順序執(zhí)行。(已在用戶過(guò)千萬(wàn)的并發(fā)系統(tǒng)中使用)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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

結(jié)論:適用中型應(yīng)用,此方案解決了分表,關(guān)聯(lián)表插入記錄的問(wèn)題。但是無(wú)法滿足高并發(fā)性能要求。同時(shí)也存在單點(diǎn)問(wèn)題,如果這個(gè)數(shù)據(jù)庫(kù)cash 掉的話……

我們目前正頭痛這個(gè)問(wèn)題,因?yàn)槲覀兊母卟l(fā)常常出現(xiàn)數(shù)據(jù)庫(kù)訪問(wèn)超時(shí),瓶頸就在這個(gè)MaxId表。我們也有考慮使用分布式緩存(eg:memcached)緩存第一次訪問(wèn)MaxId表數(shù)據(jù),以提高再次訪問(wèn)速度,并定時(shí)用緩存數(shù)據(jù)更新一次MaxId表,但我們擔(dān)心的問(wèn)題是:

a)???????? 倘若緩存失效或暴掉了,那緩存的MaxId沒(méi)有更新到數(shù)據(jù)庫(kù)導(dǎo)致數(shù)據(jù)丟失,必須停掉站點(diǎn)來(lái)執(zhí)行Select max(id)各個(gè)表來(lái)同步MaxId表。

b)???????? 分布式緩存不是一保存下去,其他服務(wù)器上就立馬可以獲取到的,即數(shù)據(jù)存在不確定性。(其實(shí)也是緩存的一個(gè)誤用,緩存應(yīng)該用來(lái)存的是頻繁訪問(wèn)并且很少改動(dòng)的內(nèi)容)

???????? 改進(jìn)方案:

整體思想:建立兩臺(tái)以上的數(shù)據(jù)庫(kù)ID生成服務(wù)器,每個(gè)服務(wù)器都有一張記錄各表當(dāng)前ID的MaxId表,但是MaxId表中Id的增長(zhǎng)步長(zhǎng)是服務(wù)器的數(shù)量,起始值依次錯(cuò)開,這樣相當(dāng)于把ID的生成散列到每個(gè)服務(wù)器節(jié)點(diǎn)上。例如:如果我們?cè)O(shè)置兩臺(tái)數(shù)據(jù)庫(kù)ID生成服務(wù)器,那么就讓一臺(tái)的MaxId表的Id起始值為1(或當(dāng)前最大Id+1),每次增長(zhǎng)步長(zhǎng)為2,另一臺(tái)的MaxId表的ID起始值為2(或當(dāng)前最大Id+2),每次步長(zhǎng)也為2。這樣就將產(chǎn)生ID的壓力均勻分散到兩臺(tái)服務(wù)器上,同時(shí)配合應(yīng)用程序控制,當(dāng)一個(gè)服務(wù)器失效后,系統(tǒng)能自動(dòng)切換到另一個(gè)服務(wù)器上獲取ID,從而解決的單點(diǎn)問(wèn)題保證了系統(tǒng)的容錯(cuò)。(Flickr思想)

但是要注意:1、多服務(wù)器就必須面臨負(fù)載均衡的問(wèn)題;2、倘若添加新節(jié)點(diǎn),需要對(duì)原有數(shù)據(jù)重新根據(jù)步長(zhǎng)計(jì)算遷移數(shù)據(jù)。

結(jié)論:適合大型應(yīng)用,生成 Id 較短,友好性比較好。(強(qiáng)烈推薦)

3、? Sequence特性

這個(gè)特性在SQL Server 2012、Oracle中可用。這個(gè)特性是數(shù)據(jù)庫(kù)級(jí)別的,允許在多個(gè)表之間共享序列號(hào)。它可以解決分表在同一個(gè)數(shù)據(jù)庫(kù)的情況,但倘若分表放在不同數(shù)據(jù)庫(kù),那將共享不到此序列號(hào)。(eg:Sequence使用場(chǎng)景:你需要在多個(gè)表之間公用一個(gè)流水號(hào)。以往的做法是額外建立一個(gè)表,然后存儲(chǔ)流水號(hào))

相關(guān)Sequence特性資料:

SQL Server2012中的SequenceNumber嘗試

SQL Server 2012 開發(fā)新功能——序列對(duì)象(Sequence)

identity和sequence的區(qū)別

Difference between Identity and Sequence in SQL Server 2012

結(jié)論:適用中型應(yīng)用,此方案不能完全解決分表問(wèn)題, 而且無(wú)法滿足高并發(fā)性能要求。同時(shí)也存在單點(diǎn)問(wèn)題,如果這個(gè)數(shù)據(jù)庫(kù)cash掉的話……

4、? 通過(guò)數(shù)據(jù)庫(kù)集群編號(hào)+集群內(nèi)的自增類型兩個(gè)字段共同組成唯一主鍵

優(yōu)點(diǎn):實(shí)現(xiàn)簡(jiǎn)單,維護(hù)也比較簡(jiǎn)單。

缺點(diǎn):關(guān)聯(lián)表操作相對(duì)比較復(fù)雜,需要兩個(gè)字段。并且業(yè)務(wù)邏輯必須是一開始就設(shè)計(jì)為處理復(fù)合主鍵的邏輯,倘若是到了后期,由單主鍵轉(zhuǎn)為復(fù)合主鍵那改動(dòng)成本就太大了。

結(jié)論:適合大型應(yīng)用,但需要業(yè)務(wù)邏輯配合處理復(fù)合主鍵。

5、? 通過(guò)設(shè)置每個(gè)集群中自增 ID 起始點(diǎn)(auto_increment_offset),將各個(gè)集群的ID進(jìn)行絕對(duì)的分段來(lái)實(shí)現(xiàn)全局唯一。當(dāng)遇到某個(gè)集群數(shù)據(jù)增長(zhǎng)過(guò)快后,通過(guò)命令調(diào)整下一個(gè) ID 起始位置跳過(guò)可能存在的沖突。

優(yōu)點(diǎn):實(shí)現(xiàn)簡(jiǎn)單,且比較容易根據(jù) ID 大小直接判斷出數(shù)據(jù)處在哪個(gè)集群,對(duì)應(yīng)用透明。缺點(diǎn):維護(hù)相對(duì)較復(fù)雜,需要高度關(guān)注各個(gè)集群 ID 增長(zhǎng)狀況。

結(jié)論:適合大型應(yīng)用,但需要高度關(guān)注各個(gè)集群 ID 增長(zhǎng)狀況。

6、? GUID(Globally Unique Identifier,全局唯一標(biāo)識(shí)符)

GUID通常表示成32個(gè)16進(jìn)制數(shù)字(0-9,A-F)組成的字符串,如:{21EC2020-3AEA-1069-A2DD-08002B30309D},它實(shí)質(zhì)上是一個(gè)128位長(zhǎng)的二進(jìn)制整數(shù)。

GUID制定的算法中使用到用戶的網(wǎng)卡MAC地址,以保證在計(jì)算機(jī)集群中生成唯一GUID;在相同計(jì)算機(jī)上隨機(jī)生成兩個(gè)相同GUID的可能性是非常小的,但并不為0。所以,用于生成GUID的算法通常都加入了非隨機(jī)的參數(shù)(如時(shí)間),以保證這種重復(fù)的情況不會(huì)發(fā)生。

優(yōu)點(diǎn):GUID是最簡(jiǎn)單的方案,跨平臺(tái),跨語(yǔ)言,跨業(yè)務(wù)邏輯,全局唯一的Id,數(shù)據(jù)間同步、遷移都能簡(jiǎn)單實(shí)現(xiàn)。

缺點(diǎn):

1)???????? 存儲(chǔ)占了32位,且無(wú)可讀性,返回GUID給客戶顯得很不專業(yè);

2)???????? 占用了珍貴的聚集索引,一般我們不會(huì)根據(jù)GUID去查單據(jù),并且插入時(shí)因?yàn)镚UID是無(wú)需的,在聚集索引的排序規(guī)則下可能移動(dòng)大量的記錄。

有兩位園友主推GUID,無(wú)須順序GUID方案原因如下:

@徐少俠 ?????????? GUID無(wú)序在并發(fā)下效率高,并且一個(gè)數(shù)據(jù)頁(yè)內(nèi)添加新行,是在B樹內(nèi)增加,本質(zhì)沒(méi)有什么數(shù)據(jù)被移動(dòng), 唯一可能的,是頁(yè)填充因子滿了 ,需要拆頁(yè)。而GUID方案導(dǎo)致的拆頁(yè)比 順序 ID要低太多了(數(shù)據(jù)庫(kù)不是很懂,暫時(shí)無(wú)法斷定,大家自己認(rèn)識(shí))

@無(wú)色 ??????????????? 我們要明白id是什么,是身份標(biāo)識(shí),標(biāo)識(shí)身份是id最大的業(yè)務(wù)邏輯,不要引入什么時(shí)間,什么用戶業(yè)務(wù)邏輯, 那是另外一個(gè)字段干的事 ,使用base64(guid,uuid),是通盤考慮,完全可以更好的兼容nosql,key-value存儲(chǔ)。

(推薦),但是倘若你系統(tǒng)一開始沒(méi)有規(guī)劃一個(gè)業(yè)務(wù) Id ,那么將導(dǎo)致大量的改動(dòng),所以這個(gè)方案的最佳狀態(tài)是一開始就設(shè)計(jì)業(yè)務(wù) Id ,當(dāng)然業(yè)務(wù) Id 的唯一性也是我們要考慮的。

結(jié)論:適合大型應(yīng)用;生成的Id 不夠友好;占據(jù)了32 位;索引效率較低。

改進(jìn):

1)???????? ( @dudu 提點(diǎn))在SQL Server 2005中新增了NEWSEQUENTIALID函數(shù)。

詳細(xì)請(qǐng)看: 《理解newid()和newsequentialid()》

在指定計(jì)算機(jī)上創(chuàng)建大于先前通過(guò)該函數(shù)生成的任何 GUID 的 GUID。 newsequentialid 產(chǎn)生的新的值是有規(guī)律的,則索引B+樹的變化是有規(guī)律的,就不會(huì)導(dǎo)致索引列插入時(shí)移動(dòng)大量記錄的問(wèn)題。

但一旦服務(wù)器重新啟動(dòng),其再次生成的GUID可能反而變小(但仍然保持唯一)。這在很大程度上提高了索引的性能,但并不能保證所生成的GUID一直增大。 SQL 的這個(gè)函數(shù)產(chǎn)生的GUID 很簡(jiǎn)單就可以預(yù)測(cè),因此不適合用于安全目的。

a)???????? 只能做為數(shù)據(jù)庫(kù)列的DEFAULT VALUE,不能執(zhí)行類似SELECT NEWSEQUENTIALID()的語(yǔ)句.

b)???????? 如何獲得生成的GUID.

如果生成的GUID所在字段做為外鍵要被其他表使用,我們就需要得到這個(gè)生成的值。通常,PK是一個(gè)IDENTITY字段,我們可以在INSERT之后執(zhí)行 SELECT SCOPE_IDENTITY()來(lái)獲得新生成的ID,但是由于NEWSEQUENTIALID()不是一個(gè)INDETITY類型,這個(gè)辦法是做不到了,而他本身又只能在默認(rèn)值中使用,不可以事先SELECT好再插入,那么我們?nèi)绾蔚玫侥兀坑幸韵聝煞N方法:

1
2
3
4
5
6
7
8
9
10
11
12
--1. 定義臨時(shí)表變量
DECLARE @outputTable TABLE (ID uniqueidentifier)
INSERT INTO TABLE1(col1, col2)
OUTPUT INSERTED.ID INTO @outputTable
VALUES ( 'value1' , 'value2' )
SELECT ID FROM @outputTable
? ?
--2. 標(biāo)記ID字段為ROWGUID(一個(gè)表只能有一個(gè)ROWGUID)
INSERT INTO TABLE1(col1, col2)
VALUES ( 'value1' , 'value2' )
--在這里,ROWGUIDCOL其實(shí)相當(dāng)于一個(gè)別名
SELECT ROWGUIDCOL FROM TABLE1

結(jié)論:適合大型應(yīng)用,解決了GUID 無(wú)序特性導(dǎo)致索引列插入移動(dòng)大量記錄的問(wèn)題。但是在關(guān)聯(lián)表插入時(shí)需要返回?cái)?shù)據(jù)庫(kù)中生成的GUID ;生成的Id 不夠友好;占據(jù)了32 位。

2)???????? “COMB”(combined guid/timestamp,意思是:組合GUID/時(shí)間截)

(感謝: @ ethan-luo ?, @lcs-帥

COMB數(shù)據(jù)類型的基本設(shè)計(jì)思路是這樣的:既然GUID數(shù)據(jù)因毫無(wú)規(guī)律可言造成索引效率低下,影響了系統(tǒng)的性能,那么能不能通過(guò)組合的方式,保留GUID的10個(gè)字節(jié),用另6個(gè)字節(jié)表示GUID生成的時(shí)間(DateTime),這樣我們將時(shí)間信息與GUID組合起來(lái),在保留GUID的唯一性的同時(shí)增加了有序性,以此來(lái)提高索引效率。

在NHibernate中,COMB型主鍵的生成代碼如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/// <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);
}

結(jié)論:適合大型應(yīng)用。即保留 GUID 的唯一性的同時(shí)增加了 GUID 有序性,提高了索引效率;解決了關(guān)聯(lián)表業(yè)務(wù)問(wèn)題;生成的 Id 不夠友好;占據(jù)了 32 位。(強(qiáng)烈推薦)

3)???????? 長(zhǎng)度問(wèn)題,使用Base64或Ascii85編碼解決。(要注意的是上述有序性方案在進(jìn)行編碼后也會(huì)變得無(wú)序)

如:

GUID:{3F2504E0-4F89-11D3-9A0C-0305E82C3301}

當(dāng)需要使用更少的字符表示GUID時(shí),可能會(huì)使用Base64或Ascii85編碼。Base64編碼的GUID有22-24個(gè)字符,如:

7QDBkvCA1+B9K/U0vrQx1A

7QDBkvCA1+B9K/U0vrQx1A==

Ascii85編碼后是20個(gè)字符,如:

5:$Hj:Pf\4RLB9%kU\Lj

?????????????????? 代碼如:

???????? Guid guid = Guid.NewGuid();

???????? byte[] buffer = guid.ToByteArray();

???????? var shortGuid = Convert.ToBase64String(buffer);

?????????????????? 結(jié)論:適合大型應(yīng)用,縮短GUID 的長(zhǎng)度。生成的Id 不夠友好;索引效率較低。

7、? GUID TO Int64

對(duì)于GUID的可讀性,有園友給出如下方案:(感謝: @黑色的羽翼

1
2
3
4
5
6
7
8
/// <summary>
/// 根據(jù)GUID獲取19位的唯一數(shù)字序列
/// </summary>
public static long GuidToLongID()
{
???? byte [] buffer = Guid.NewGuid().ToByteArray();
???? return BitConverter.ToInt64(buffer, 0);
}

即將GUID轉(zhuǎn)為了19位數(shù)字,數(shù)字反饋給客戶可以一定程度上緩解友好性問(wèn)題。EG:

GUID: cfdab168-211d-41e6-8634-ef5ba6502a22??? (不友好)

Int64: 5717212979449746068????????????????????????????????????? (友好性還行)

不過(guò)我的小伙伴說(shuō)ToInt64后就不唯一了。因此我專門寫了個(gè)并發(fā)測(cè)試程序,后文將給出測(cè)試結(jié)果截圖及代碼簡(jiǎn)單說(shuō)明。

(唯一性、業(yè)務(wù)適合性是可以權(quán)衡的,這個(gè)唯一性肯定比不過(guò)GUID的,一般程序上都會(huì)安排錯(cuò)誤處理機(jī)制,比如異常后執(zhí)行一次重插的方案……)

結(jié)論:適合大型應(yīng)用,生成相對(duì)友好的Id (純數(shù)字)-- ----因簡(jiǎn)單和業(yè)務(wù)友好性而推薦。

8、? 自己寫編碼規(guī)則

優(yōu)點(diǎn):全局唯一Id,符合業(yè)務(wù)后續(xù)長(zhǎng)遠(yuǎn)的發(fā)展(可能具體業(yè)務(wù)需要自己的編碼規(guī)則等等)。

缺陷:根據(jù)具體編碼規(guī)則實(shí)現(xiàn)而不同;還要考慮倘若主鍵在業(yè)務(wù)上允許改變的,會(huì)帶來(lái)外鍵同步的麻煩。

我這邊寫兩個(gè)編碼規(guī)則方案: (可能不唯一,只是個(gè)人方案,也請(qǐng)大家提出自己的編碼規(guī)則)

1)???????? 12位年月日時(shí)分秒+3位服務(wù)器編碼+3位表編碼+5位隨機(jī)碼? (這樣就完全單機(jī)完成生成全局唯一編碼)---共23位

缺陷:因?yàn)楦綆щS機(jī)碼,所以編碼缺少一定的順序感。(生成高唯一性隨機(jī)碼的方案稍后給給出程序)

2)???????? 12位年月日時(shí)分秒+3位服務(wù)器編碼+3位表編碼+5位流水碼? (這樣流水碼就需要結(jié)合數(shù)據(jù)庫(kù)和緩存)---共23位

缺陷:因?yàn)槭褂玫搅魉a,流水碼的生成必然會(huì)遇到和MaxId、序列表、Sequence方案中類似的問(wèn)題

(為什么沒(méi)有毫秒?毫秒也不具備業(yè)務(wù)可讀性,我改用5位隨機(jī)碼、流水碼代替,推測(cè)1秒內(nèi)應(yīng)該不會(huì)下99999[五位]條語(yǔ)法)

?

結(jié)論:適合大型應(yīng)用,從業(yè)務(wù)上來(lái)說(shuō),有一個(gè)規(guī)則的編碼能體現(xiàn)產(chǎn)品的專業(yè)成度。(強(qiáng)烈推薦)

?

?

GUID生成Int64值后是否還具有唯一性測(cè)試

測(cè)試環(huán)境

?

主要測(cè)試思路:

  1. 根據(jù)內(nèi)核數(shù)使用多線程并發(fā)生成Guid后再轉(zhuǎn)為Int64位值,放入集合A、B、…N,多少個(gè)線程就有多少個(gè)集合。
  2. 再使用Dictionary字典高效查key的特性,將步驟1中生成的多個(gè)集合全部加到Dictionary中,看是否有重復(fù)值。

示例注解:測(cè)了 Dictionary<long,bool> 最大容量就在5999470左右,所以每次并發(fā)生成的唯一值總數(shù)控制在此范圍內(nèi),讓測(cè)試達(dá)到最有效話。

主要代碼:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
for ( int i = 0; i <= Environment.ProcessorCount - 1; i++)
{
???? ThreadPool.QueueUserWorkItem(
???????? (list) =>
???????? {
???????????? List< long > tempList = list as List< long >;
???????????? for ( int j = 1; j < listLength; j++)
???????????? {
???????????????? byte [] buffer = Guid.NewGuid().ToByteArray();
???????????????? tempList.Add(BitConverter.ToInt64(buffer, 0));
???????????? }
???????????? barrier.SignalAndWait();
???????? }, totalList[i]);
}

測(cè)試數(shù)據(jù)截圖:???????????????????????????????????????????????????????????????????????????

?

?

數(shù)據(jù)一(循環(huán)1000次,測(cè)試數(shù):1000*5999470)

?

數(shù)據(jù)二(循環(huán)5000次,測(cè)試數(shù):5000*5999470)--跑了一個(gè)晚上……

?

?

感謝 @Justany_WhiteSnow 的專業(yè)回答:( 大家分析下,我數(shù)學(xué)比較差,稍后再說(shuō)自己的理解)

GUID桶數(shù)量:(2 ^ 4) ^ 32 = 2 ^ 128

Int64桶數(shù)量: 2 ^ 64

倘若每個(gè)桶的機(jī)會(huì)是均等的,則每個(gè)桶的GUID數(shù)量為:

(2 ^ 128) / (2 ^ 64) = 2 ^ 64 = 18446744073709551616

也就是說(shuō),其實(shí)重復(fù)的機(jī)會(huì)是有的,只是概率問(wèn)題。

樓主測(cè)試數(shù)是29997350000,發(fā)生重復(fù)的概率是:

1 - ((1 - (1 / (2 ^ 64))) ^ 29997350000) ≈ 1 - ((1 - 1 / (2 ^ 64)) ^ (2 ^ 32)) < 1 - 1 + 1 / (2 ^ 32) = 1 / (2 ^ 32) ≈ 2.3283064e-10

(唯一性、業(yè)務(wù)適合性是可以權(quán)衡的,這個(gè)唯一性肯定比不過(guò)GUID的,一般程序上都會(huì)安排錯(cuò)誤處理機(jī)制,比如異常后執(zhí)行一次重插的方案……)

(唯一性、業(yè)務(wù)適合性是可以權(quán)衡的,這個(gè)唯一性肯定比不過(guò)GUID的,一般程序上都會(huì)安排錯(cuò)誤處理機(jī)制,比如異常后執(zhí)行一次重插的方案……)

結(jié)論:GUID 轉(zhuǎn)為Int64 值后,也具有高唯一性,可以使用與項(xiàng)目中。

?

Random生成高唯一性隨機(jī)碼

我使用了五種Random生成方案,要Random生成唯一主要因素就是種子參數(shù)要唯一。(這是比較久以前寫的測(cè)試案例了,一直找不到合適的博文放,今天終于找到合適的地方了)

不過(guò)該測(cè)試是在單線程下的,多線程應(yīng)使用不同的Random實(shí)例,所以對(duì)結(jié)果影響不會(huì)太大。

  1. 使用Environment.TickCount做為Random參數(shù)(即Random的默認(rèn)參數(shù)),重復(fù)性最大。
  2. 使用DateTime.Now.Ticks做為Random參數(shù),存在重復(fù)。
  3. 使用unchecked((int)DateTime.Now.Ticks)做為Random參數(shù),存在重復(fù)。
  4. 使用Guid.NewGuid().GetHashCode()做為random參數(shù),測(cè)試不存在重復(fù)(或存在性極小)。
  5. 使用RNGCryptoServiceProvider做為random參數(shù),測(cè)試不存在重復(fù)(或存在性極小)。

即:

??????? 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);

??????? }

測(cè)試結(jié)果:

?

結(jié)論:隨機(jī)碼使用RNGCryptoServiceProvider 或Guid.NewGuid().GetHashCode() 生成的唯一性較高。

?

?

一些精彩評(píng)論(部分更新到原博文對(duì)應(yīng)的地方)

一、

數(shù)據(jù)庫(kù)文件體積只是一個(gè)參考值,可水平擴(kuò)展系統(tǒng)性能(如nosql,緩存系統(tǒng))并不和文件體積有高指數(shù)的線性相關(guān)。

如taobao/qq的系統(tǒng)比拼byte系統(tǒng)慢,關(guān)鍵在于索引的命中率,緩存,系統(tǒng)的水平擴(kuò)展。

如果數(shù)據(jù)庫(kù)很少,你搞這么多byte能提高性能?

如果數(shù)據(jù)庫(kù)很大,你搞這么多byte不兼容索引不兼容緩存,不是害自已嗎?

如果數(shù)據(jù)庫(kù)要求伸縮性,你搞這么多byte,需要不斷改程序,不是自找苦嗎?

如果數(shù)據(jù)庫(kù)要求移植性,你搞這么多byte,移植起來(lái)不如重新設(shè)計(jì),這是不是很多公司不斷加班的原因?

?

不依賴于數(shù)據(jù)存儲(chǔ)系統(tǒng)是分層設(shè)計(jì)思想的精華,實(shí)現(xiàn)戰(zhàn)略性能最大化,而不是追求戰(zhàn)術(shù)單機(jī)性能最大化。

?

不要迷信數(shù)據(jù)庫(kù)性能,不要迷信三范式,不要使用外鍵,不要使用byte,不要使用自增id,不要使用存儲(chǔ)過(guò)程,不要使用內(nèi)部函數(shù),不要使用非標(biāo)準(zhǔn)sql,存儲(chǔ)系統(tǒng)只做存儲(chǔ)系統(tǒng)的事。當(dāng)出現(xiàn)系統(tǒng)性能時(shí),如此設(shè)計(jì)的數(shù)據(jù)庫(kù)可以更好的實(shí)現(xiàn)遷移數(shù)據(jù)庫(kù)(如mysql->oracle),實(shí)現(xiàn)nosql改造((mongodb/hadoop),實(shí)現(xiàn)key-value緩存(redis,memcache)。

?

二、

很多程序員有對(duì)性能認(rèn)識(shí)有誤區(qū),如使用存儲(chǔ)過(guò)程代替正常程序,其實(shí)使用存儲(chǔ)過(guò)程只是追求單服務(wù)器的高性能,當(dāng)需要服務(wù)器水平擴(kuò)展時(shí),存儲(chǔ)過(guò)程中的業(yè)務(wù)邏輯就是你的噩運(yùn)。

?

三、

除數(shù)字日期,能用字符串存儲(chǔ)的字段盡量使用字符串存儲(chǔ),不要為節(jié)省那不值錢的1個(gè)g的硬盤而使用類似字節(jié)之類的字段,進(jìn)而大幅犧牲系統(tǒng)可伸縮性和可擴(kuò)展性。

不要為了追求所謂的性能,引入byte,使用byte注定是短命和難于移植,想想為什么html,email一直流行,因?yàn)樗鼈兪褂玫氖亲址硎痉ǎ灰腥祟愑肋h(yuǎn)都能解析,如email把二進(jìn)制轉(zhuǎn)成base64存儲(chǔ)。除了實(shí)時(shí)系統(tǒng),視頻外,建議使用字符串來(lái)存儲(chǔ)數(shù)據(jù),系統(tǒng)性能的關(guān)鍵在于分布式,在于水平擴(kuò)展。

?

?

本次博文到此結(jié)束,希望大家對(duì)本次主題“如何在高并發(fā)分布式系統(tǒng)中生成全局唯一Id”多提出自己寶貴的意見(jiàn)。

如何在高并發(fā)分布式系統(tǒng)中生成全局唯一Id


更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號(hào)聯(lián)系: 360901061

您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對(duì)您有幫助就好】

您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長(zhǎng)會(huì)非常 感謝您的哦!!!

發(fā)表我的評(píng)論
最新評(píng)論 總共0條評(píng)論
主站蜘蛛池模板: 热灸灸这里只有精品 | 色在线视频 | 国产精品免费一级在线观看 | 三级高清| 久久久久无码国产精品一区 | 精品国产三级 | 我的朋友丈夫 | 婷婷亚洲综合五月天小说 | 成人在线h| 91小视频在线观看免费版高清 | 日本精品在线 | 欧美精品亚洲一区二区在线播放 | 欧美色综合天天久久综合精品 | 麻豆免费永久网址入口网址 | 亚洲欧美综合日韩字幕v在线 | 99re久久精品国产首页2020 | www.av520 | 国产精品综合 | 国产精品无码永久免费888 | 亚洲精品福利 | 日本免费不卡在线一区二区三区 | 日韩av电影免费看 | 亚洲免费人成在线视频观看 | 免费性| 久久亚洲欧美日本精品品 | 免费黄色av网站 | 午夜小网站 | 九九精品视频在线 | av小说在线 | 精品欧美成人高清视频在线观看 | 亚洲国产欧洲综合997久久 | 久久99国产综合精品 | 91成人午夜性a一级毛片 | 亚洲高清一区二区三区 | 色婷婷久久| 婷婷国产成人精品视频 | 久久狠狠| www.riripa.com| seku.tv | a国产精品| 99在线播放视频 |