溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Redis SortedSet結(jié)構(gòu)score字段丟失精度問題解決辦法是什么

發(fā)布時間:2021-12-08 14:35:21 來源:億速云 閱讀:390 作者:柒染 欄目:數(shù)據(jù)庫

這期內(nèi)容當(dāng)中小編將會給大家?guī)碛嘘P(guān)Redis SortedSet結(jié)構(gòu)score字段丟失精度問題解決辦法是什么,文章內(nèi)容豐富且以專業(yè)的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。

一、問題現(xiàn)象

項目中采用Redis SortedSet存儲用戶的離線消息,score值存儲的msgid(消息ID)。msgid采用snowflake算法生成,按照時間有序。

生成的msgid有18位十進制整數(shù),例如 215857550229364736

我們發(fā)現(xiàn)數(shù)值很接近的msgid,在redis中無法通過score進行區(qū)分。

舉個列子,在redis中tzset結(jié)構(gòu)里存入如下幾條數(shù)據(jù)  

ZADD tzset 215857497028812800 test1

ZADD tzset 215857540511162369 test2

ZADD tzset 215857550229364736 test3

ZADD tzset 215857550229364737 test4

查詢看一下結(jié)果 

Redis SortedSet結(jié)構(gòu)score字段丟失精度問題解決辦法是什么

我們發(fā)現(xiàn)score值采用科學(xué)計數(shù)法表示,test3,test4兩個元素的score值顯示是一樣的。

使用score=215857550229364736 執(zhí)行查詢,結(jié)果如下圖 

Redis SortedSet結(jié)構(gòu)score字段丟失精度問題解決辦法是什么

使用215857550229364736查詢,結(jié)果score為215857550229364737的test4也被查出來了

用215857550229364739去查,竟然也能查出來 

Redis SortedSet結(jié)構(gòu)score字段丟失精度問題解決辦法是什么

這一現(xiàn)象給我們的系統(tǒng)功能帶了困擾,會影響到消息同步TimeLine的精確性(參看《基于TimeLine模型的消息同步機制》)。

二、問題原因

查詢相關(guān)資料發(fā)現(xiàn)Sorted Sets中的Score是double類型,我們的msgid是long類型。問題是long轉(zhuǎn)換為double時,丟失精度。

1、snowflake算法簡介

消息ID采用snowflake算法,采用64位二進制整數(shù)。二進制具體位數(shù)含義如下圖。

Redis SortedSet結(jié)構(gòu)score字段丟失精度問題解決辦法是什么

1位,不用。二進制中最高位為1的都是負數(shù),但是我們生成的id都使用正數(shù),所以這個最高位固定是0

41位,用來記錄時間戳(毫秒)。

如果只用來表示正整數(shù)(計算機中正數(shù)包含0),可以表示的數(shù)值范圍是:0 至 241?1,減1是因為可表示的數(shù)值范圍是從0開始算的,而不是1。

也就是說41位可以表示241?1個毫秒的值,轉(zhuǎn)化成單位年則是(241?1)/(1000?60?60?24?365)=69年

10位,用來記錄工作機器id。

可以部署在1024個節(jié)點,包括5位datacenterId和5位workerId

12位,序列號,用來記錄同毫秒內(nèi)產(chǎn)生的不同id。

12位(bit)可以表示的最大正整數(shù)是4095,即可以用0、1、2、3、....4095這4096個數(shù)字,來表示同一機器同一時間截(毫秒)內(nèi)產(chǎn)生的4096個ID序號

2、doublel數(shù)據(jù)結(jié)構(gòu)

double數(shù)據(jù)的結(jié)構(gòu)如下圖 

Redis SortedSet結(jié)構(gòu)score字段丟失精度問題解決辦法是什么

3、問題定位

63bit(去掉符號位)的數(shù)轉(zhuǎn)換為52bit的數(shù),從某一位開始進行了四舍五入,導(dǎo)致精度下降。所以215857550229364736、215857550229364737、215857550229364739三個數(shù)據(jù)被轉(zhuǎn)換為double類型后,計算機認為是相同的數(shù)。

三、解決辦法

問題找到了,怎么解決呢?

id生成策略要保證整個系統(tǒng)生命周期類所有ID唯一,設(shè)計一個52bit的ID生成器保證ID唯一難度較大。

Redis的score數(shù)據(jù)類型更是修改不了

用52bit來表示63bit的數(shù)據(jù)一定會丟失信息,長整型long默認轉(zhuǎn)換為double的方式丟失的信息會影響到業(yè)務(wù),能不能結(jié)合業(yè)務(wù)特點自定義一種轉(zhuǎn)換(映射)方式,答案是肯定的。

有以下幾種想法

1、因為Redis緩存的消息最多保存15天(假設(shè))或者最多保存多少條。能不能截去41位時間戳的部分高位,確保Redis緩存時間周期內(nèi)時間戳長度夠用就行呢?計算了一下長度 log(15*24*60*60*1000)=30.2,大約30位二進制數(shù)即可在現(xiàn)有規(guī)則下表示15天時間。所以將41位時間戳的前11位屏蔽掉,可以節(jié)約11位二進制信息。這樣63bit剛好能用52bit來表示。

然而這個方式有個致命問題,當(dāng)15天時間周期到了后,時間戳?xí)兊锰貏e小(新的周期),這導(dǎo)致上一個周期后邊的數(shù)據(jù)Score值大于新周期。消息順序混亂了,會導(dǎo)致拉離線丟消息,這不能接受!

2、去掉10bit工作機id和序列號的最高位bit。

去掉這11bit,不會對消息的順序造成影響,但是可能造成score數(shù)值沖突(相同)。分析一下score沖突的可能性。

(1)12bit序列號能表示4096個數(shù)。去掉最高位,能表示2048個數(shù)。所以單個msgid生成節(jié)點(dispatch模塊)每毫秒,每個用戶要超過2048條消息,才可能出現(xiàn)score重復(fù)。這個基本不可能發(fā)生。

(2)去掉10bit工作機id號,需要同一毫秒,同一用戶在不同的dispatch節(jié)點都接收到消息,score才可能沖突。即使出現(xiàn)這種情況,由于12位序列號我們做了模128的隨機分布(解決分庫問題),即使出現(xiàn)同一毫秒不同disptch生成同一用戶msgid的情況下,score沖突的概率還要除以 128*128。這個概率非常低。

(3)即使出現(xiàn)了score沖突(兩條消息有相同score),最多造成拉取離線消息多拉取相同score的消息(本來一次拉取10條離線,結(jié)果可能拉到11條),對業(yè)務(wù)也沒有影響。

因此采用去掉10bit工作機id和序列號的最高位bit將63bit(不含符號位)的msgid轉(zhuǎn)換成52bit的score對業(yè)務(wù)上沒有影響。同時解決了redis sorted set丟失精度的問題。 

因此采用去掉10bit工作機id和序列號的最高位bit將63bit(不含符號位)的msgid轉(zhuǎn)換成52bit的score對業(yè)務(wù)上沒有影響。同時解決了redis sorted set丟失精度的問題。

上述就是小編為大家分享的Redis SortedSet結(jié)構(gòu)score字段丟失精度問題解決辦法是什么了,如果剛好有類似的疑惑,不妨參照上述分析進行理解。如果想知道更多相關(guān)知識,歡迎關(guān)注億速云行業(yè)資訊頻道。

向AI問一下細節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI