新聞中心
分布式全局唯一id方案這么多?
作者: 鴨血粉絲 2021-06-05 07:33:09
網(wǎng)絡(luò)
通信技術(shù)
分布式 前段時(shí)間阿粉想著如何去優(yōu)化我們公司中已經(jīng)存在的分布式中的唯一ID,而提起唯一的ID,相信如果不是從事傳統(tǒng)行業(yè)的人,肯定都有所了解,分布式架構(gòu)下,唯一ID生成方案,是我們?cè)谠O(shè)計(jì)一個(gè)系統(tǒng),尤其是數(shù)據(jù)庫(kù)使用分庫(kù)分表的時(shí)候常常會(huì)遇見(jiàn)的問(wèn)題,尤其是當(dāng)我們進(jìn)行了分庫(kù)分表之后,對(duì)這個(gè)唯一ID的要求也就越來(lái)越高。

創(chuàng)新互聯(lián)公司2013年開(kāi)創(chuàng)至今,是專(zhuān)業(yè)互聯(lián)網(wǎng)技術(shù)服務(wù)公司,擁有項(xiàng)目做網(wǎng)站、網(wǎng)站設(shè)計(jì)網(wǎng)站策劃,項(xiàng)目實(shí)施與項(xiàng)目整合能力。我們以讓每一個(gè)夢(mèng)想脫穎而出為使命,1280元沙洋做網(wǎng)站,已為上家服務(wù),為沙洋各地企業(yè)和個(gè)人服務(wù),聯(lián)系電話:028-86922220
本文轉(zhuǎn)載自微信公眾號(hào)「Java極客技術(shù)」,作者鴨血粉絲。轉(zhuǎn)載本文請(qǐng)聯(lián)系Java極客技術(shù)公眾號(hào)。
前段時(shí)間阿粉想著如何去優(yōu)化我們公司中已經(jīng)存在的分布式中的唯一ID,而提起唯一的ID,相信如果不是從事傳統(tǒng)行業(yè)的人,肯定都有所了解,分布式架構(gòu)下,唯一ID生成方案,是我們?cè)谠O(shè)計(jì)一個(gè)系統(tǒng),尤其是數(shù)據(jù)庫(kù)使用分庫(kù)分表的時(shí)候常常會(huì)遇見(jiàn)的問(wèn)題,尤其是當(dāng)我們進(jìn)行了分庫(kù)分表之后,對(duì)這個(gè)唯一ID的要求也就越來(lái)越高。那么唯一ID方案都有哪些呢?
分布式全局唯一ID
往往一談分布式,總是會(huì) 色變,因?yàn)樵诤芏嗝嬖嚨臅r(shí)候,都會(huì)問(wèn)你,會(huì)不會(huì)分布式?你們項(xiàng)目的架構(gòu)是怎么做的,做的如何?你們既然使用了分布式,那么你們的分布式事務(wù)是怎么處理的,你們分布式全局唯一 ID 使用的是什么算法來(lái)實(shí)現(xiàn)的?
往往談到這個(gè)的時(shí)候,很多面試的朋友就會(huì)很尷尬,我都是直接用的,我好像完全沒(méi)有注意過(guò)。當(dāng)你意識(shí)到這一點(diǎn)的時(shí)候,往往接下來(lái)的問(wèn)題,你回答的就會(huì)開(kāi)始磕磕絆絆,于是面試涼了。
并發(fā)越大的系統(tǒng),數(shù)據(jù)就越大,數(shù)據(jù)越大就越需要分布式,而大量的分布式數(shù)據(jù)就越需要唯一標(biāo)識(shí)來(lái)識(shí)別它們,而這些唯一標(biāo)識(shí),我們就稱(chēng)之為分布式全局唯一的ID。
Redis實(shí)現(xiàn)全局唯一ID
阿粉的項(xiàng)目說(shuō)實(shí)話,還不是特別的差勁。于是阿粉就開(kāi)始想著,這分布式的全局唯一ID,為啥生成的時(shí)候都是使用 UUID ,要么就是自增主鍵呢?
于是阿粉準(zhǔn)備使用 Redis 來(lái)生成分布式全局唯一ID。
Redis實(shí)現(xiàn)全局唯一ID原理
因?yàn)?Redis 的所有命令是單線程的,所以可以利用 Redis 的原子操作 INCR 和 INCRBY,來(lái)生成全局唯一的ID。方式一:StringRedisTemplate
- public class Activity {
- private Long id;
- private String name;
- private BigDecimal price;
- }
上面是我們的活動(dòng)的實(shí)體類(lèi),馬上就要 618 了,各位做電商的是不是開(kāi)始準(zhǔn)備搞事情了?可以學(xué)習(xí)一下用一下試試,我們活動(dòng)中有 id ,活動(dòng)的名稱(chēng) name ,還有對(duì)應(yīng)活動(dòng)設(shè)置好的價(jià)格 price 等等,字段可能還會(huì)有很多,我們需要的暫時(shí)就列出這么多。
- public class IdGeneratorService {
- @Autowired
- private StringRedisTemplate stringRedisTemplate;
- private static final String ID_KEY = "id:generator:activity";
- public Long incrementId() {
- return stringRedisTemplate.opsForValue().increment(ID_KEY);
- }
- long id = idGeneratorService.incrementId(); 調(diào)用生成
但是看起來(lái)是不是總是感覺(jué)好像有點(diǎn) low ,我們是不是就要準(zhǔn)備來(lái)整的高大上一點(diǎn),畢竟代碼就像一個(gè)程序員的內(nèi)褲,雖然自己看著有洞感覺(jué)沒(méi)啥,但是別人看到是不是就很不爽了,那就整個(gè)他們看起來(lái)比較高大上一點(diǎn)的。
方式二:
為什么會(huì)有方案二,那是因?yàn)槲覀兊?Redis 很多時(shí)候都不是只有一個(gè) Redis,都是搭建的集群,既然是集群,我們就要開(kāi)始合理的利用上集群。
那么我們就要開(kāi)始考慮到集群方面的知識(shí)了,那么我們的思路就有了。于是出現(xiàn)了:集群中每個(gè)節(jié)點(diǎn)預(yù)生成生成ID;然后與redis的已經(jīng)存在的ID做比較。如果大于,則取節(jié)點(diǎn)生成的ID;小于的話,取Redis中最大ID自增。
這個(gè)時(shí)候我們還需要一段 lua 腳本來(lái)保證我們實(shí)現(xiàn)的ID是唯一的,這才是真正的本質(zhì),不然我們實(shí)現(xiàn)的ID在高端,不唯一,有個(gè)錘子用
核心腳本:
- local function get_max_seq()
- local key = tostring(KEYS[1])
- local incr_amoutt = tonumber(KEYS[2])
- local seq = tostring(KEYS[3])
- local month_in_seconds = 24 * 60 * 60 * 30
- if (1 == redis.call(\'setnx\', key, seq))
- then
- redis.call(\'expire\', key, month_in_seconds)
- return seq
- else
- local prev_seq = redis.call(\'get\', key)
- if (prev_seq < seq)
- then
- redis.call(\'set\', key, seq)
- return seq
- else
- --[[
- 不能直接返回redis.call(\'incr\', key),因?yàn)榉祷氐氖莕umber浮點(diǎn)數(shù)類(lèi)型,會(huì)出現(xiàn)不精確情況。
- 注意: 類(lèi)似"16081817202494579"數(shù)字大小已經(jīng)快超時(shí)lua和reids最大數(shù)值,請(qǐng)謹(jǐn)慎的增加seq的位數(shù)
- --]]
- redis.call(\'incrby\', key, incr_amoutt)
- return redis.call(\'get\', key)
- end
- end
- end
- return get_max_seq()
以上的 lua 的腳本來(lái)自于Ydoing,一個(gè)博客的大佬,我們現(xiàn)在既然會(huì)使用他生成全局唯一的ID,那么是不是就得搞清楚為什么會(huì)選擇 Redis 來(lái)實(shí)現(xiàn)分布式全局唯一的ID。
Redis 的所有命令是單線程的
上一段開(kāi)頭,阿粉就說(shuō) Redis 的命令都是單線程的,相信如果你在面試官面前這么說(shuō),面試官肯定會(huì)問(wèn)你一句,為什么 Redis 是單線程而不是多線程的呢?
Redis 基于 Reactor 模式開(kāi)發(fā)了網(wǎng)絡(luò)事件處理器,這個(gè)處理器被稱(chēng)為文件事件處理器。它的組成結(jié)構(gòu)為4部分:多個(gè)套接字、IO多路復(fù)用程序、文件事件分派器、事件處理器。因?yàn)槲募录峙善麝?duì)列的消費(fèi)是單線程的,所以 Redis 才叫單線程模型。
當(dāng)你說(shuō)到這個(gè) Reactor 模式的時(shí)候,如果大家深入研究過(guò) Netty 的模型,就會(huì)發(fā)現(xiàn),這個(gè)模式在 Netty 中也是有使用的,我們這時(shí)候是不是就得需要去官網(wǎng)上去瞅瞅看,為什么這么說(shuō)。
什么是Reactor模型
Reactor模型實(shí)際上都知道,就是一個(gè)多路復(fù)用I/O模型,主要用于在高并發(fā)、高吞吐量的環(huán)境中進(jìn)行I/O處理。
而這種多路復(fù)用的模型所依賴(lài)的永遠(yuǎn)都是那么幾個(gè)內(nèi)容,事件分發(fā)器,事件處理器,還有調(diào)用的客戶(hù)端,
Reactor模型是一個(gè)同步的I/O多路復(fù)用模型,我們還是先把這個(gè)同步的I/O多路復(fù)用模型給弄清楚了再看其他的。
這個(gè)相信大家肯定不是很熟悉,而阿粉在之前也給大家說(shuō)了關(guān)于Netty中的Channel,文章地址發(fā)給大家,用Socket編程?我還是選擇了Netty,在文章中,我們已經(jīng)給大家說(shuō)了關(guān)于Channel,而這種單線程的模型是什么樣子的呢?
圖已經(jīng)給大家畫(huà)出來(lái)了,丑是丑了點(diǎn),但是意思還是表達(dá)出來(lái)了。
這種模型也就是說(shuō):Redis 單線程指的是網(wǎng)絡(luò)請(qǐng)求模塊使用了一個(gè)線程(所以不需考慮并發(fā)安全性),即一個(gè)線程處理所有網(wǎng)絡(luò)請(qǐng)求,其他模塊仍用了多個(gè)線程。
而面試官還會(huì)有一種問(wèn)法,為什么使用 Redis 就會(huì)快。
這個(gè)相信大家肯定能回答出來(lái),因?yàn)?Redis 是一種基于內(nèi)存的存儲(chǔ)數(shù)據(jù),為什么內(nèi)存快?
因?yàn)檫@種快速是針對(duì)存儲(chǔ)在磁盤(pán)上的數(shù)據(jù)來(lái)說(shuō)的,因?yàn)閮?nèi)存中的數(shù)據(jù),斷電之后,消失了,你下次來(lái)的時(shí)候,不還是需要從磁盤(pán)讀取出來(lái),然后保存,所以說(shuō)在Redis速度快。扯遠(yuǎn)了,回來(lái)繼續(xù)說(shuō) Redis的單線程。
我們來(lái)看看官網(wǎng)給我們的解釋?zhuān)?/p>
- Redis is single threaded. How can I exploit multiple CPU / cores?
- It's not very frequent that CPU becomes your bottleneck with Redis, as usually Redis is either memory or network bound. For instance, using pipelining Redis running on an average Linux system can deliver even 1 million requests per second, so if your application mainly uses O(N) or O(log(N)) commands, it is hardly going to use too much CPU.
- However, to maximize CPU usage you can start multiple instances of Redis in the same box and treat them as different servers. At some point a single box may not be enough anyway, so if you want to use multiple CPUs you can start thinking of some way to shard earlier.
- You can find more information about using multiple Redis instances in the Partitioning page.
- However with Redis 4.0 we started to make Redis more threaded. For now this is limited to deleting objects in the background, and to blocking commands implemented via Redis modules. For future releases, the plan is to make Redis more and more threaded.
其實(shí)翻譯過(guò)來(lái)大致就是說(shuō),當(dāng)我們使用 Redis 的時(shí)候,CPU 成為瓶頸的情況并不常見(jiàn),因?yàn)?Redis 通常是內(nèi)存或網(wǎng)絡(luò)受限的。
其實(shí)說(shuō)白了,官網(wǎng)就是說(shuō)我們 Redis 就是這么的快,并且正是由于在單線程模式的情況下已經(jīng)很快了,就沒(méi)有必要在使用多線程了。這整的是不是就有點(diǎn)惡心了。阿粉也說(shuō)說(shuō)自己的見(jiàn)解,畢竟這官網(wǎng)的話有點(diǎn)糊弄人的意思。
其實(shí) Redis 使用單個(gè) CPU 綁定一個(gè)內(nèi)存,針對(duì)內(nèi)存的處理就是單線程的,而我們使用多個(gè) CPU 模擬出多個(gè)線程來(lái),光在多個(gè) CPU 之間的切換,然后再操作 Redis ,實(shí)際上就不如直接從內(nèi)存中拿出來(lái),畢竟耗時(shí)在這里擺著。
你認(rèn)為的 Redis 為什么是單線程的?
當(dāng)前文章:分布式全局唯一ID方案這么多?
路徑分享:http://m.5511xx.com/article/djjeijj.html


咨詢(xún)
建站咨詢(xún)
