日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
RedisSyncer同步引擎的設(shè)計與實現(xiàn)

RedisSyncer一款通過replication協(xié)議模擬slave來獲取源Redis節(jié)點數(shù)據(jù)并寫入目標(biāo)Redis從而實現(xiàn)數(shù)據(jù)同步的Redis同步中間件。 該項目主要包括以下子項目:

10多年的靖州網(wǎng)站建設(shè)經(jīng)驗,針對設(shè)計、前端、開發(fā)、售后、文案、推廣等六對一服務(wù),響應(yīng)快,48小時及時工作處理。網(wǎng)絡(luò)營銷推廣的優(yōu)勢是能夠根據(jù)用戶設(shè)備顯示端的尺寸不同,自動調(diào)整靖州建站的顯示方式,使網(wǎng)站能夠適用不同顯示終端,在瀏覽器中調(diào)整網(wǎng)站的寬度,無論在任何一種瀏覽器上瀏覽網(wǎng)站,都能展現(xiàn)優(yōu)雅布局與設(shè)計,從而大程度地提升瀏覽體驗。創(chuàng)新互聯(lián)從事“靖州網(wǎng)站設(shè)計”,“靖州網(wǎng)站推廣”以來,每個客戶項目都認(rèn)真落實執(zhí)行。

  • redis 同步服務(wù)引擎 redissyncer-server
  • redissycner 客戶端 redissyncer-cli
  • redis 數(shù)據(jù)校驗工具 redissycner-compare
  • 基于docker-compse的一體化部署方案 redissyncer

本文主要介紹reidssyncer引擎(既redissyncer-server)的設(shè)計與實現(xiàn),以及引擎運行的機(jī)制。

同步流程

原生redis master slave 模式主要分為兩個階段,第一個階段同步rdb鏡像,也就是全量同步部分;全量同步完成后進(jìn)入命令傳播模式,每個執(zhí)行成功的數(shù)據(jù)變更操作會同步給slave節(jié)點。redissyncer 的模擬了這一機(jī)制并將兩部分拆解,既可以執(zhí)行完整同步任務(wù)也可以單獨執(zhí)行全量或增量同步。

  • 建立socket
  • 發(fā)送auth user password (6.0新增user)
OK 成功
其他 error
  • send->ping
返回:
ERR invalid password 密碼錯誤
NOAUTH Authentication required.沒有發(fā)送密碼
operation not permitted 操作沒權(quán)限
PONG 密碼成功

作用:
檢測主從節(jié)點之間的網(wǎng)絡(luò)是否可用。
檢查主從節(jié)點當(dāng)前是否接受處理命令。
  • 發(fā)送從節(jié)點端口信息
REPLCONF listening-port 

-->OK 成功
-->其他 失敗
  • 發(fā)送從節(jié)點IP
REPLCONF ip-address 

--> OK 成功
--> 其他 失敗
  • 發(fā)送EOF能力(capability)
REPLCONF capa eof

--> OK 成功
--> 失敗
作用:
是否支持EOF風(fēng)格的RDB傳輸,用于無盤復(fù)制,就是能夠解析出RDB文件的EOF流格式。用于無盤復(fù)制的方式中。
redis4.0支持兩種能力 EOF 和 PSYNC2
redis4.0之前版本僅支持EOF能力
  • 發(fā)送PSYNC2能力
REPLCONF capa  PSYNC2

--> OK 成功
--> 失敗
作用:
告訴master支持PSYNC2命令 , master 會忽略它不支持的能力. PSYNC2則表示支持Redis4.0最新的PSYN復(fù)制操作。
  • 發(fā)送PSYNC
PSYNC {replid} {offset}

--> FULLRESYNC {replid} {offset} 完整同步
--> CONTINUE 部分同步
--> -ERR 主服務(wù)器低于2.8,不支持psync,從服務(wù)器需要發(fā)送sync
--> NOMASTERLINK 重試
--> LOADING 重試
--> 超過重試機(jī)制閾值宕掉任務(wù)

讀取PSYNC命令狀態(tài),判斷是部分同步還是完整同步
  • PSYNC ---> 啟動heartbeat
REPLCONF ACK 
心跳檢測
在命令傳播階段,從服務(wù)器默認(rèn)會以每秒一次的頻率
發(fā)送REPLCONF ACK命令對于主從服務(wù)器有三個作用:
作用:
檢測主從服務(wù)器的網(wǎng)絡(luò)連接狀態(tài);
輔助實現(xiàn)min-slaves選項;
檢測命令丟失。

REPLCONF GETACK
->REPLCONF ACK

rdb 鏡像同步完成后進(jìn)入命令傳播,master 會不斷將變化數(shù)據(jù)推送給slave。

為了保證

RedisSyncer內(nèi)部有斷點續(xù)傳、數(shù)據(jù)補償、斷線重連等機(jī)制來保證數(shù)據(jù)同步過程中穩(wěn)定性和可用性,具體的機(jī)制如下。

斷點續(xù)傳機(jī)制

RedisSyncer的斷點續(xù)傳機(jī)制是基于Redis的replid和offset來實現(xiàn)的,RedisSyncer有兩個版本的斷點續(xù)傳機(jī)制v1和v2。

  • v1版本:

v1版本數(shù)據(jù)寫入到目的端redis后,將offset持久化到本地,這樣下次重啟就從上次的offset拉取。但是由于該方案寫目的端的操作和offset持久化不是一個原子的操作。如果中間發(fā)生中斷會導(dǎo)致數(shù)據(jù)的不一致。 例如,先寫入數(shù)據(jù)到目的端成功,后持久化offset還沒成功就發(fā)生了宕機(jī)、重啟等情況,那么再次斷點續(xù)傳拉取上一次的offset數(shù)據(jù)最后就不一致了。

  • v2版本:

在v2版本策略中RedisSyncer會將每一個pipeline批次中不存在事務(wù)的的命令通過multi和exec進(jìn)行包裝,并在事務(wù)尾部插入offset檢查點。 當(dāng)斷點續(xù)傳時需要從目標(biāo)Redis的所以db庫中查找checkpoint并找到所對應(yīng)源節(jié)點當(dāng)最大offset,再根據(jù)該offset進(jìn)行斷點續(xù)傳。目前v2版本只支持目標(biāo)為單機(jī)Redis的情況。 在v2版本中

  • v2命令事務(wù)封裝結(jié)構(gòu)
  • v2 checkpoint檢查點結(jié)構(gòu):
HASH  hset redis-syncer-checkpoint {value}
{value}:
* {ip}:{port}-runid {replid}
* {ip}:{port}-offset {offset}
* pointcheckVersion {version}

在Redis的事務(wù)機(jī)制中雖然不支持回滾,并且如果事務(wù)中間命令執(zhí)行出錯后但是事務(wù)還是被執(zhí)行完成,但是除特殊情況外能夠保證一致性。 在v2的機(jī)制中,為了防止'寫放大'會在目標(biāo)redis的每一個邏輯庫中寫入一個checkpoint,因此在執(zhí)行斷點續(xù)傳操作的時候,同步工具會先掃描目標(biāo)各個邏輯庫中的checkpoint并選出里面最大offset的checkpoint作為斷點續(xù)傳的參數(shù)。

數(shù)據(jù)補償機(jī)制

在數(shù)據(jù)同步過程中,存在由于網(wǎng)絡(luò)穩(wěn)定性或其他因素導(dǎo)致key寫入失敗的情況,為此redissyncer實現(xiàn)了一套補償機(jī)制來保證源端與目的端數(shù)據(jù)的一致性。 數(shù)據(jù)補償?shù)那疤崾敲顚懭氲膬绲刃?,因此在RedisSyncer中會先將INCR、INCRBY、INCRBYFLOAT、APPEND、DECR、DECRBY等部分非冪等命令轉(zhuǎn)換成冪等命令后再寫入目標(biāo)端Redis。 RedisSyncer在目標(biāo)為單機(jī)Redis或者Proxy的時候是通過pipeline機(jī)制將數(shù)據(jù)寫入到目標(biāo)Redis中的,每一個批次的pipeline的提交會返回一個結(jié)果列表, 同步工具會驗證pipeline中結(jié)果的正確性,如果部分命令寫入失敗,同步工具對該批次與該key相關(guān)的命令進(jìn)行重試。 如果重試超過指定的閥值,將會宕掉任務(wù)。對于存在大key的list等非冪等結(jié)構(gòu),將不會進(jìn)行數(shù)據(jù)補償,強制結(jié)束任務(wù)待人工處理。

斷線重連機(jī)制

由于網(wǎng)絡(luò)抖動等原因可能會導(dǎo)致同步工具源端與目標(biāo)端連接在同步過程中斷開,因此需要斷線重試機(jī)制來保證在任務(wù)同步的過程中如果出現(xiàn)異常斷開的問題。斷線重連機(jī)制存在于與源Redis節(jié)點和RedisSyncer、RedisSyncer與目標(biāo)Redis節(jié)點的連接之間,兩者分別有各自的處理機(jī)制。

源端重連機(jī)制源Redis與RedisSyncer的斷線重連機(jī)制是通過記錄的offset來實現(xiàn)的,當(dāng)因網(wǎng)絡(luò)異常等原因斷開了連接時,RedisSyncer會重新嘗試與源Redis節(jié)點建立連接,并通過當(dāng)前任務(wù)記錄的runid、offset等信息去拉取斷開之前的增量數(shù)據(jù),連接重新建立成功后RedisSyncer的同步任務(wù)將會無感知繼續(xù)同步。當(dāng)斷線重連超過指定重試閥值或者因為offset刷過導(dǎo)致沒有辦法續(xù)傳數(shù)據(jù)時,RedisSyncer會宕掉當(dāng)前當(dāng)同步任務(wù),等待人工干預(yù)。

目標(biāo)端重連機(jī)制RedisSyncer與目標(biāo)Redis之間的斷線重連機(jī)制是通過緩存上一批次的pipeline的命令來實現(xiàn)的,當(dāng)連接斷開異常時RedisSyncer進(jìn)行重重連回放上一批次寫入失敗的命令。當(dāng)回放失敗或者超過連續(xù)重試次數(shù)RedisSyncer會宕掉當(dāng)前當(dāng)同步任務(wù),等待人工干預(yù)。

命令的鏈?zhǔn)教幚?/h4>

RedisSyncer中采用鏈?zhǔn)讲呗蕴幚硗綌?shù)據(jù),任何一個策略返回失敗,該key都將不會被同步。鏈?zhǔn)讲呗粤鞒倘鐖D所示

每一個key在RedisSyncer都會經(jīng)過一個策略鏈進(jìn)行處理,只要有一個策略未通過則這個key將不會同步到目標(biāo)Redis,比如key過期時間的計算策略如果計算出全量階段key已過期,則將會自動拋棄該key。

  • 策略鏈中的策略包括

類型

策略描述

DataAnalysisStrategy

命令統(tǒng)計分析

KeyFilterStrategy

命令過濾

DbMappingStrategy

Db映射

TimeCalculationStrategy

過期時間計算

RdbCommandSendStrategy

全量數(shù)據(jù)寫入

AofCommandSendStrategy

增量數(shù)據(jù)寫入

.....

.....

任務(wù)管理

  • 任務(wù)啟動流程

  • 任務(wù)停止及清理流程

任務(wù)主動停止時,RedisSyncer會先停止源Redis端的數(shù)據(jù)寫入然后進(jìn)入數(shù)據(jù)保護(hù)狀態(tài),確??赡苓€處在RedisSyncer中未寫入目標(biāo)的少部分?jǐn)?shù)據(jù)能夠完整的寫入目標(biāo)端,并且正確的記錄寫入的最后一條數(shù)據(jù)的offset并持久化,保證斷點續(xù)傳時RedisSyncer能夠提供正確的offset。

  • 任務(wù)狀態(tài)

TYPE

code

description

status

STOP

0

任務(wù)停止

已使用

CREATING

1

創(chuàng)建中

已使用

CREATED

2

創(chuàng)建完成

已使用

RUN

3

運行狀態(tài)

已使用

BROKEN

5

任務(wù)異常

已使用

RDBRUNING

6

全量RDB同步過程中

已使用

COMMANDRUNING

7

增量同步中

已使用

FINISH

8

完成狀態(tài)

已使用(用于文件導(dǎo)入)

  • 任務(wù)異常處理原則在RedisSycner任務(wù)中如果遇到可能會導(dǎo)致數(shù)據(jù)不一致的錯誤,RedisSyncer都會宕掉任務(wù),等待人工干預(yù)。

rdb跨版本同步實現(xiàn)

rdb文件存在向前兼容問題,即高版本的rdb文件無法導(dǎo)入低rdb版本的Redis

  • 對于對數(shù)據(jù)成員沒有順序性要求的命令如:SET,ZSET,HASH命令解析器將其解析成一個或多個sadd,zadd,hmset等命令進(jìn)行處理
  • 對于對數(shù)據(jù)成員有順序性要求的命令如:List等命令,若被命令解析器判斷為大key并將其拆分為多個子命令,此時必須保證按順序發(fā)送至目標(biāo)REDIS節(jié)點
  • REDIS跨版本間存在的問題: 由于REDIS是向下兼容(低版本無法兼容高版本RDB),在其RDB文件協(xié)議中存在一個vesion版本號標(biāo)識,REDIS在RDB導(dǎo)入或者全量同步執(zhí)行rdbLoad時會先檢測RDB VERSION是否符合向下兼容,如果不符合則會拋出 Can’t handle RDB format version   錯誤。
  • syncer跨版本實現(xiàn)機(jī)制 對于全量同步RDB數(shù)據(jù)部分syncer將其分命令為兩類進(jìn)行處理

RDB文件協(xié)議中關(guān)于 RDB VERSION部分

REDIS RDB文件結(jié)構(gòu)開頭部分示例
----------------------------# RDB is a binary format. There are no new lines or spaces in the file.
52 45 44 49 53 # Magic String "REDIS"
30 30 30 37 # 4 digit ASCCII RDB Version Number. In this case, version = "0007" = 7 RDB VERSION字段
----------------------------
FE 00 # FE = code that indicates database selector. db number = 00

關(guān)于 RDB VERSION檢查部分偽代碼:

def rdbLoad(filename):
rio = rioInitWithFile(filename);
# 設(shè)置標(biāo)記:
# a. 服務(wù)器狀態(tài):rdb_loading = 1
# b. 載入時間:loading_start_time = now_time
# c. 載入大小:loading_total_bytes = filename.size
startLoading(rio)
# 1.檢查該文件是否為RDB文件(即文件開頭前5個字符是否為"REDIS")
if !checkRDBHeader(rio):
redislog("error, Wrong signature trying to load DB from file")
return
# 2.檢查當(dāng)前RDB文件版本是否兼容(向下兼容)
if !checkRDBVersion(rio):
redislog("error, Can't handle RDB format version")
return
.........
//Redis中關(guān)于RDB_VERSION檢查的代碼
rdbver = atoi(buf+5);
if (rdbver < 1 || rdbver > RDB_VERSION) {
rdbCheckError("Can't handle RDB format version %d",rdbver);
goto err;
}

RDB 同步過程中的大 Key 拆分

RedisSyncer在全量同步階段在遇到LIST、SET、ZSET、HASH等結(jié)構(gòu)等時候,當(dāng)數(shù)據(jù)大小超過閥值后RedisSyncer會通過迭代器的形式將key拆分成多個子命令寫入目標(biāo)庫。防止部分超大key一次性讀入內(nèi)存導(dǎo)致程序產(chǎn)生oom并提高同步的速度。而對于不存在大key的命令同步工具會通過序列化逆序列化的形式寫入目標(biāo)。

附錄一  Redis RDB協(xié)議

redis RDB Dump 文件格式

----------------------------# RDB is a binary format. There are no new lines or spaces in the file.
52 45 44 49 53 # Magic String "REDIS"
30 30 30 37 # 4 digit ASCCII RDB Version Number. In this case, version = "0007" = 7
----------------------------
FE 00 # FE = code that indicates database selector. db number = 00
----------------------------# Key-Value pair starts
FD $unsigned int # FD indicates "expiry time in seconds". After that, expiry time is read as a 4 byte unsigned int
$value-type # 1 byte flag indicating the type of value - set, map, sorted set etc.
$string-encoded-key # The key, encoded as a redis string
$encoded-value # The value. Encoding depends on $value-type
----------------------------
FC $unsigned long # FC indicates "expiry time in ms". After that, expiry time is read as a 8 byte unsigned long
$value-type # 1 byte flag indicating the type of value - set, map, sorted set etc.
$string-encoded-key # The key, encoded as a redis string
$encoded-value # The value. Encoding depends on $value-type
----------------------------
$value-type # This key value pair doesn't have an expiry. $value_type guaranteed != to FD, FC, FE and FF
$string-encoded-key
$encoded-value
----------------------------
FE $length-encoding # Previous db ends, next db starts. Database number read using length encoding.
----------------------------
... # Key value pairs for this database, additonal database

FF ## End of RDB file indicator
8 byte checksum ## CRC 64 checksum of the entire file.

RDB文件以魔術(shù)字符串“REDIS”開頭。
52 45 44 49 53 # "REDIS"

RDB 版本號
接下來的 4 個字節(jié)存儲 rdb 格式的版本號。這 4 個字節(jié)被解釋為 ascii 字符,然后使用字符串到整數(shù)轉(zhuǎn)換轉(zhuǎn)換為整數(shù)。
00 00 00 03 # Version = 3

Database Selector
一個Redis實例可以有多個數(shù)據(jù)庫。
單個字節(jié)0xFE標(biāo)記數(shù)據(jù)庫選擇器的開始。在該字節(jié)之后,一個可變長度字段指示數(shù)據(jù)庫編號。請參閱“長度編碼”部分以了解如何讀取此數(shù)據(jù)庫編號。

鍵值對
在數(shù)據(jù)庫選擇器之后,該文件包含一系列鍵值對。
za
每個鍵值對有 4 個部分 -

1.密鑰到期時間戳。
2.指示值類型的一字節(jié)標(biāo)志
3.密鑰,編碼為 Redis 字符串。請參閱“Redis 字符串編碼”
4.根據(jù)值類型編碼的值。參見“Redis 值編碼”

附錄二 Redis RESP協(xié)議

Redis RESP協(xié)議

RESP 協(xié)議是在 Redis 1.2 中引入的,但它成為了 Redis 2.0 中與 Redis 服務(wù)器通信的標(biāo)準(zhǔn)方式。是在Redis 客戶端中實現(xiàn)的協(xié)議。 RESP 實際上是一種序列化協(xié)議,它支持以下數(shù)據(jù)類型:簡單字符串、錯誤、整數(shù)、批量字符串和數(shù)組。

RESP 在 Redis 中用作請求-響應(yīng)協(xié)議的方式如下:

  • 客戶端將命令作為批量字符串的 RESP 數(shù)組發(fā)送到 Redis 服務(wù)器。
  • 服務(wù)器根據(jù)命令實現(xiàn)以其中一種 RESP 類型進(jìn)行回復(fù)。

在 RESP 中,某些數(shù)據(jù)的類型取決于第一個字節(jié):

  • 對于簡單字符串,回復(fù)的第一個字節(jié)是“+”
  • 對于錯誤,回復(fù)的第一個字節(jié)是“-”
  • 對于整數(shù),回復(fù)的第一個字節(jié)是“:”
  • 對于批量字符串,回復(fù)的第一個字節(jié)是“$”
  • 對于數(shù)組,回復(fù)的第一個字節(jié)是“ *”

RESP 能夠使用稍后指定的批量字符串或數(shù)組的特殊變體來表示 Null 值。在 RESP 中,協(xié)議的不同部分總是以“\r\n”(CRLF)終止。

RESP Simple Strings

'+' 字符開頭,后跟不能包含 CR 或 LF 字符(不允許換行)的字符串,以 CRLF 結(jié)尾(即“\r\n”)。如:

"+OK\r\n"

RESP Errors

"-Error message\r\n"

如:

-ERR unknown command 'foobar'
-WRONGTYPE Operation against a key holding the wrong kind of value

RESP Integers

Integers只是一個 CRLF 終止的字符串,代表一個整數(shù),以“:”字節(jié)為前綴。 例如

":0\r\n" 
":1000\r\n"

Bulk Strings

用于表示長度最大為 512 MB 的單個二進(jìn)制安全字符串。批量字符串按以下方式編碼:

“$”字節(jié)后跟組成字符串的字節(jié)數(shù)(前綴長度),以 CRLF 結(jié)尾。

實際的字符串?dāng)?shù)據(jù)。

最后的 CRLF。

“foobar”的編碼如下:

"$6\r\nfoobar\r\n"

當(dāng)字符串為空

"$0\r\n\r\n"

Bulk Strings還可以用于表示 Null 值的特殊格式來表示值不存在。在這種特殊格式中,長度為 -1,并且沒有數(shù)據(jù),因此 Null 表示為:

"$-1\r\n"

RESP Arrays

格式:

  • 一個'*'字符作為第一個字節(jié),然后是數(shù)組中元素的數(shù)量作為十進(jìn)制數(shù),然后是 CRLF。
  • Array 的每個元素的附加 RESP 類型。 空數(shù)組表示為:
"*0\r\n"

“foo”和“bar”的數(shù)組表示為

"*2\r\n$3\r\nfoo\r\n$3\r\nbar\r\n"

["foo",nil,"bar"] (Null elements in Arrays)

*3\r\n$3\r\nfoo\r\n$-1\r\n$3\r\nbar\r\n

名稱欄目:RedisSyncer同步引擎的設(shè)計與實現(xiàn)
文章出自:http://m.5511xx.com/article/coecjoh.html