新聞中心
如何去優(yōu)化我們公司中已經(jīng)存在的分布式中的唯一ID,而提起唯一的ID,相信如果不是從事傳統(tǒng)行業(yè)的人,肯定都有所了解,分布式架構(gòu)下,唯一ID生成方案,是我們在設(shè)計(jì)一個(gè)系統(tǒng),尤其是數(shù)據(jù)庫使用分庫分表的時(shí)候常常會(huì)遇見的問題,尤其是當(dāng)我們進(jìn)行了分庫分表之后,對這個(gè)唯一ID的要求也就越來越高。那么唯一ID方案都有哪些呢?

鎮(zhèn)原網(wǎng)站建設(shè)公司創(chuàng)新互聯(lián),鎮(zhèn)原網(wǎng)站設(shè)計(jì)制作,有大型網(wǎng)站制作公司豐富經(jīng)驗(yàn)。已為鎮(zhèn)原上1000+提供企業(yè)網(wǎng)站建設(shè)服務(wù)。企業(yè)網(wǎng)站搭建\成都外貿(mào)網(wǎng)站制作要多少錢,請找那個(gè)售后服務(wù)好的鎮(zhèn)原做網(wǎng)站的公司定做!
分布式全局唯一ID
往往一談分布式,總是會(huì) 色變,因?yàn)樵诤芏嗝嬖嚨臅r(shí)候,都會(huì)問你,會(huì)不會(huì)分布式?你們項(xiàng)目的架構(gòu)是怎么做的,做的如何?你們既然使用了分布式,那么你們的分布式事務(wù)是怎么處理的,你們分布式全局唯一 ID 使用的是什么算法來實(shí)現(xiàn)的?
往往談到這個(gè)的時(shí)候,很多面試的朋友就會(huì)很尷尬,我都是直接用的,我好像完全沒有注意過。當(dāng)你意識到這一點(diǎn)的時(shí)候,往往接下來的問題,你回答的就會(huì)開始磕磕絆絆,于是面試涼了。
并發(fā)越大的系統(tǒng),數(shù)據(jù)就越大,數(shù)據(jù)越大就越需要分布式,而大量的分布式數(shù)據(jù)就越需要唯一標(biāo)識來識別它們,而這些唯一標(biāo)識,我們就稱之為分布式全局唯一的ID。
Redis實(shí)現(xiàn)全局唯一ID
阿粉的項(xiàng)目說實(shí)話,還不是特別的差勁。于是阿粉就開始想著,這分布式的全局唯一ID,為啥生成的時(shí)候都是使用 UUID ,要么就是自增主鍵呢?
于是阿粉準(zhǔn)備使用 Redis 來生成分布式全局唯一ID。
Redis實(shí)現(xiàn)全局唯一ID原理
因?yàn)?Redis 的所有命令是單線程的,所以可以利用 Redis 的原子操作 INCR 和 INCRBY,來生成全局唯一的ID。方式一:StringRedisTemplate
public class Activity {
private Long id;
private String name;
private BigDecimal price;
}
上面是我們的活動(dòng)的實(shí)體類,馬上就要 618 了,各位做電商的是不是開始準(zhǔn)備搞事情了?可以學(xué)習(xí)一下用一下試試,我們活動(dòng)中有 id ,活動(dòng)的名稱 name ,還有對應(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)用生成
但是看起來是不是總是感覺好像有點(diǎn) low ,我們是不是就要準(zhǔn)備來整的高大上一點(diǎn),畢竟代碼就像一個(gè)程序員的內(nèi)褲,雖然自己看著有洞感覺沒啥,但是別人看到是不是就很不爽了,那就整個(gè)他們看起來比較高大上一點(diǎn)的。
方式二:
為什么會(huì)有方案二,那是因?yàn)槲覀兊?Redis 很多時(shí)候都不是只有一個(gè) Redis,都是搭建的集群,既然是集群,我們就要開始合理的利用上集群。
那么我們就要開始考慮到集群方面的知識了,那么我們的思路就有了。于是出現(xiàn)了:集群中每個(gè)節(jié)點(diǎn)預(yù)生成生成ID;然后與redis的已經(jīng)存在的ID做比較。如果大于,則取節(jié)點(diǎn)生成的ID;小于的話,取Redis中最大ID自增。
這個(gè)時(shí)候我們還需要一段 lua 腳本來保證我們實(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 then
redis.call(\'set\', key, seq)
return seq
else
--[[
不能直接返回redis.call(\'incr\', key),因?yàn)榉祷氐氖莕umber浮點(diǎn)數(shù)類型,會(huì)出現(xiàn)不精確情況。
注意: 類似"16081817202494579"數(shù)字大小已經(jīng)快超時(shí)lua和reids最大數(shù)值,請謹(jǐn)慎的增加seq的位數(shù)
--]]
redis.call(\'incrby\', key, incr_amoutt)
return redis.call(\'get\', key)
end
end
end
return get_max_seq()
以上的 lua 的腳本來自于Ydoing,一個(gè)博客的大佬,我們現(xiàn)在既然會(huì)使用他生成全局唯一的ID,那么是不是就得搞清楚為什么會(huì)選擇 Redis 來實(shí)現(xiàn)分布式全局唯一的ID。
Redis 的所有命令是單線程的
上一段開頭,阿粉就說 Redis 的命令都是單線程的,相信如果你在面試官面前這么說,面試官肯定會(huì)問你一句,為什么 Redis 是單線程而不是多線程的呢?
Redis 基于 Reactor 模式開發(fā)了網(wǎng)絡(luò)事件處理器,這個(gè)處理器被稱為文件事件處理器。它的組成結(jié)構(gòu)為4部分:多個(gè)套接字、IO多路復(fù)用程序、文件事件分派器、事件處理器。因?yàn)槲募录峙善麝?duì)列的消費(fèi)是單線程的,所以 Redis 才叫單線程模型。
當(dāng)你說到這個(gè) Reactor 模式的時(shí)候,如果大家深入研究過 Netty 的模型,就會(huì)發(fā)現(xiàn),這個(gè)模式在 Netty 中也是有使用的,我們這時(shí)候是不是就得需要去官網(wǎng)上去瞅瞅看,為什么這么說。
什么是Reactor模型
Reactor模型實(shí)際上都知道,就是一個(gè)多路復(fù)用I/O模型,主要用于在高并發(fā)、高吞吐量的環(huán)境中進(jìn)行I/O處理。
而這種多路復(fù)用的模型所依賴的永遠(yuǎn)都是那么幾個(gè)內(nèi)容,事件分發(fā)器,事件處理器,還有調(diào)用的客戶端
分布式全局唯一ID分布式全局唯一ID
Reactor模型是一個(gè)同步的I/O多路復(fù)用模型,我們還是先把這個(gè)同步的I/O多路復(fù)用模型給弄清楚了再看其他的。
這個(gè)相信大家肯定不是很熟悉,而阿粉在之前也給大家說了關(guān)于Netty中的Channel,文章地址發(fā)給大家,用Socket編程?我還是選擇了Netty,在文章中,我們已經(jīng)給大家說了關(guān)于Channel,而這種單線程的模型是什么樣子的呢?
分布式全局唯一ID分布式全局唯一ID
圖已經(jīng)給大家畫出來了,丑是丑了點(diǎn),但是意思還是表達(dá)出來了。
這種模型也就是說:Redis 單線程指的是網(wǎng)絡(luò)請求模塊使用了一個(gè)線程(所以不需考慮并發(fā)安全性),即一個(gè)線程處理所有網(wǎng)絡(luò)請求,其他模塊仍用了多個(gè)線程。
而面試官還會(huì)有一種問法,為什么使用 Redis 就會(huì)快。
這個(gè)相信大家肯定能回答出來,因?yàn)?Redis 是一種基于內(nèi)存的存儲(chǔ)數(shù)據(jù),為什么內(nèi)存快?
因?yàn)檫@種快速是針對存儲(chǔ)在磁盤上的數(shù)據(jù)來說的,因?yàn)閮?nèi)存中的數(shù)據(jù),斷電之后,消失了,你下次來的時(shí)候,不還是需要從磁盤讀取出來,然后保存,所以說在Redis速度快。扯遠(yuǎn)了,回來繼續(xù)說 Redis的單線程。
我們來看看官網(wǎng)給我們的解釋:
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í)翻譯過來大致就是說,當(dāng)我們使用 Redis 的時(shí)候,CPU 成為瓶頸的情況并不常見,因?yàn)?Redis 通常是內(nèi)存或網(wǎng)絡(luò)受限的。
其實(shí)說白了,官網(wǎng)就是說我們 Redis 就是這么的快,并且正是由于在單線程模式的情況下已經(jīng)很快了,就沒有必要在使用多線程了。這整的是不是就有點(diǎn)惡心了。阿粉也說說自己的見解,畢竟這官網(wǎng)的話有點(diǎn)糊弄人的意思。
其實(shí) Redis 使用單個(gè) CPU 綁定一個(gè)內(nèi)存,針對內(nèi)存的處理就是單線程的,而我們使用多個(gè) CPU 模擬出多個(gè)線程來,光在多個(gè) CPU 之間的切換,然后再操作 Redis ,實(shí)際上就不如直接從內(nèi)存中拿出來,畢竟耗時(shí)在這里擺著。
網(wǎng)站欄目:分布式全局唯一ID
標(biāo)題URL:http://fisionsoft.com.cn/article/dpoggdo.html


咨詢
建站咨詢
