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

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

新聞中心

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

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

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

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

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

同步流程

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

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

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

-->OK 成功
-->其他 失敗
  • 發(fā)送從節(jié)點(diǎn)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 會(huì)忽略它不支持的能力. 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 ---> 啟動(dòng)heartbeat
REPLCONF ACK 
心跳檢測
在命令傳播階段,從服務(wù)器默認(rèn)會(huì)以每秒一次的頻率
發(fā)送REPLCONF ACK命令對于主從服務(wù)器有三個(gè)作用:
作用:
檢測主從服務(wù)器的網(wǎng)絡(luò)連接狀態(tài);
輔助實(shí)現(xiàn)min-slaves選項(xiàng);
檢測命令丟失。

REPLCONF GETACK
->REPLCONF ACK

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

為了保證

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

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

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

  • v1版本:

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

  • v2版本:

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

  • v2命令事務(wù)封裝結(jié)構(gòu)
  • v2 checkpoint檢查點(diǎn)結(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í)行出錯(cuò)后但是事務(wù)還是被執(zhí)行完成,但是除特殊情況外能夠保證一致性。 在v2的機(jī)制中,為了防止'寫放大'會(huì)在目標(biāo)redis的每一個(gè)邏輯庫中寫入一個(gè)checkpoint,因此在執(zhí)行斷點(diǎn)續(xù)傳操作的時(shí)候,同步工具會(huì)先掃描目標(biāo)各個(gè)邏輯庫中的checkpoint并選出里面最大offset的checkpoint作為斷點(diǎn)續(xù)傳的參數(shù)。

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

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

斷線重連機(jī)制

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

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

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

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

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

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

  • 策略鏈中的策略包括

類型

策略描述

DataAnalysisStrategy

命令統(tǒng)計(jì)分析

KeyFilterStrategy

命令過濾

DbMappingStrategy

Db映射

TimeCalculationStrategy

過期時(shí)間計(jì)算

RdbCommandSendStrategy

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

AofCommandSendStrategy

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

.....

.....

任務(wù)管理

  • 任務(wù)啟動(dòng)流程

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

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

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

TYPE

code

description

status

STOP

0

任務(wù)停止

已使用

CREATING

1

創(chuàng)建中

已使用

CREATED

2

創(chuàng)建完成

已使用

RUN

3

運(yùn)行狀態(tài)

已使用

BROKEN

5

任務(wù)異常

已使用

RDBRUNING

6

全量RDB同步過程中

已使用

COMMANDRUNING

7

增量同步中

已使用

FINISH

8

完成狀態(tài)

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

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

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

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

  • 對于對數(shù)據(jù)成員沒有順序性要求的命令如:SET,ZSET,HASH命令解析器將其解析成一個(gè)或多個(gè)sadd,zadd,hmset等命令進(jìn)行處理
  • 對于對數(shù)據(jù)成員有順序性要求的命令如:List等命令,若被命令解析器判斷為大key并將其拆分為多個(gè)子命令,此時(shí)必須保證按順序發(fā)送至目標(biāo)REDIS節(jié)點(diǎn)
  • REDIS跨版本間存在的問題: 由于REDIS是向下兼容(低版本無法兼容高版本RDB),在其RDB文件協(xié)議中存在一個(gè)vesion版本號(hào)標(biāo)識(shí),REDIS在RDB導(dǎo)入或者全量同步執(zhí)行rdbLoad時(shí)會(huì)先檢測RDB VERSION是否符合向下兼容,如果不符合則會(huì)拋出 Can’t handle RDB format version   錯(cuò)誤。
  • syncer跨版本實(shí)現(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. 載入時(shí)間:loading_start_time = now_time
# c. 載入大小:loading_total_bytes = filename.size
startLoading(rio)
# 1.檢查該文件是否為RDB文件(即文件開頭前5個(gè)字符是否為"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)等時(shí)候,當(dāng)數(shù)據(jù)大小超過閥值后RedisSyncer會(huì)通過迭代器的形式將key拆分成多個(gè)子命令寫入目標(biāo)庫。防止部分超大key一次性讀入內(nèi)存導(dǎo)致程序產(chǎn)生oom并提高同步的速度。而對于不存在大key的命令同步工具會(huì)通過序列化逆序列化的形式寫入目標(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 版本號(hào)
接下來的 4 個(gè)字節(jié)存儲(chǔ) rdb 格式的版本號(hào)。這 4 個(gè)字節(jié)被解釋為 ascii 字符,然后使用字符串到整數(shù)轉(zhuǎn)換轉(zhuǎn)換為整數(shù)。
00 00 00 03 # Version = 3

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

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

1.密鑰到期時(shí)間戳。
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 客戶端中實(shí)現(xiàn)的協(xié)議。 RESP 實(shí)際上是一種序列化協(xié)議,它支持以下數(shù)據(jù)類型:簡單字符串、錯(cuò)誤、整數(shù)、批量字符串和數(shù)組。

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

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

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

  • 對于簡單字符串,回復(fù)的第一個(gè)字節(jié)是“+”
  • 對于錯(cuò)誤,回復(fù)的第一個(gè)字節(jié)是“-”
  • 對于整數(shù),回復(fù)的第一個(gè)字節(jié)是“:”
  • 對于批量字符串,回復(fù)的第一個(gè)字節(jié)是“$”
  • 對于數(shù)組,回復(fù)的第一個(gè)字節(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只是一個(gè) CRLF 終止的字符串,代表一個(gè)整數(shù),以“:”字節(jié)為前綴。 例如

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

Bulk Strings

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

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

實(shí)際的字符串?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

格式:

  • 一個(gè)'*'字符作為第一個(gè)字節(jié),然后是數(shù)組中元素的數(shù)量作為十進(jìn)制數(shù),然后是 CRLF。
  • Array 的每個(gè)元素的附加 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

當(dāng)前文章:RedisSyncer同步引擎的設(shè)計(jì)與實(shí)現(xiàn)
文章轉(zhuǎn)載:http://www.5511xx.com/article/coecjoh.html