天天看點

TairRedisLevelDBMDBTair聯系

我們知道,Tair的存儲引擎主要有RDB、LDB、MDB三種,我們首先分别詳細介紹這三種存儲引擎的結構及操作,然後再介紹Tair。

Redis

1.資料結構及對象

1.1資料結構

  • 簡單動态字元串
TairRedisLevelDBMDBTair聯系
TairRedisLevelDBMDBTair聯系
  • 雙端連結清單
TairRedisLevelDBMDBTair聯系
TairRedisLevelDBMDBTair聯系
  • 字典
TairRedisLevelDBMDBTair聯系
TairRedisLevelDBMDBTair聯系
  • 整數集合
TairRedisLevelDBMDBTair聯系
TairRedisLevelDBMDBTair聯系

contents的值是從小到大有序存儲的。

  • 跳躍表
TairRedisLevelDBMDBTair聯系
TairRedisLevelDBMDBTair聯系
TairRedisLevelDBMDBTair聯系
  • 壓縮清單

壓縮清單是清單鍵和哈希鍵的底層實作之一,當清單隻包含少量清單項,并且每個清單項要麼就是小整數,要麼是短字元串時,redis就會适應壓縮清單來做清單鍵的底層實作。

TairRedisLevelDBMDBTair聯系
TairRedisLevelDBMDBTair聯系
TairRedisLevelDBMDBTair聯系

1.2對象

Redis并不是基于上述資料結構實作鍵值對資料庫,而是基于上述資料結建構立了一套對象系統,每種對象都可以使用上述結構中的一種或多種存儲結構來實作,資料庫系統使用redisObject來表示資料庫中的鍵對象和值對象:

TairRedisLevelDBMDBTair聯系

其中,對象系統的對象類型有:

  • 字元串,REDIS_STRING
  • 清單,REDIS_LIST
  • 集合,REDIS_SET
  • 有序集合,REDIS_ZSET
  • 哈希,REDIS_HASH

通過這五種對象類型,Redis在執行指令前就可以根據對象的類型來判斷是否可以對該對象執行給定的指令。

此外,Redis的對象系統還實作了基于引用計數技術的記憶體回收機制和對象共享機制。

最後,Redis的對象帶有通路時間記錄資訊,該資訊可以用于計算資料庫鍵的空轉時長,在伺服器啟用了maxmemory功能時,空轉時長較大的那些可能會優先被删除。

可以通過以下指令來查詢變量的類型:

TYPE 鍵名
           

其次,對象的編碼記錄了對象的底層存儲結構,也就是1.1節結束的那些結構。

TairRedisLevelDBMDBTair聯系

下面列出了每種對象底層使用的編碼類型:

TairRedisLevelDBMDBTair聯系

使用下面的指令可以檢視對象的編碼:

OBJECT ENCODING 鍵名
           

2.單機資料庫

2.1資料庫實作

(1)Redis伺服器

初始化伺服器時,依據dbnum決定建立多少個資料庫,dbnum值由配置決定,預設為16。預設我們都是在0号資料庫上操作。

TairRedisLevelDBMDBTair聯系
TairRedisLevelDBMDBTair聯系

(2)Redis使用者狀态

在伺服器内部,每個redis使用者由下面的結構維護其狀态,其中的db指針就指向了使用者目前操作的資料庫。

TairRedisLevelDBMDBTair聯系
TairRedisLevelDBMDBTair聯系
TairRedisLevelDBMDBTair聯系

下面的指令可以切換資料庫:

select dbname
           

(3)資料庫

資料庫由下面的結構表示,資料庫中的dict字典儲存了資料庫中的所有鍵值對,該字典被稱為鍵空間。

TairRedisLevelDBMDBTair聯系
TairRedisLevelDBMDBTair聯系

(4)RDB

我們将伺服器中的非空資料庫以及它們的鍵值對統稱為資料庫狀态。

Redis是記憶體資料庫,但也提供了RDB持久化功能。RDB持久化将資料庫狀态儲存到一個RDB檔案中,該檔案是一個經過壓縮的二進制檔案,基于該檔案可以還原生成RDB檔案時的資料庫狀态,在伺服器啟動時隻要檢測到該檔案就會自動載入RDB檔案(AOF更新頻率比RDB高,隻有AOF持久化功能關閉,伺服器才會使用RDB檔案來還原資料庫狀态,否則伺服器會優先使用AOF還原資料庫)。

RDB的持久化既可手動執行,也可根據伺服器配置定期執行。

有兩個Redis指令可以用于生成RDB,一個是SAVE,另一個是BGSAVE。

SAVE由伺服器程序執行儲存工作,會阻塞Redis伺服器程序,直到RDB檔案建立完畢,在此期間伺服器不能處理任何指令請求;而BGSAVE則會派生出一個子程序,然後由子程序負責建立RDB檔案,伺服器程序(父程序)繼續處理指令請求。

TairRedisLevelDBMDBTair聯系
TairRedisLevelDBMDBTair聯系

因為BGSAVE不會阻塞伺服器程序,是以Redis允許使用者通過設定伺服器配置的save選項,讓伺服器周期性地自動執行一次BGSAVE指令。

#滿足下面條件之一BGSAVE就會被執行,如下設定是Redis的預設設定
save 900 1    #900秒内對資料庫執行了至少一次修改
save 300 10   #300秒内對資料庫執行了至少十次修改
save 60 10000 #60秒内對資料庫執行了至少10000次修改
           

Redis伺服器儲存save選項的方式如下如:

TairRedisLevelDBMDBTair聯系
TairRedisLevelDBMDBTair聯系

Redis伺服器儲存dirty和lastsave屬性的方式如下如:

TairRedisLevelDBMDBTair聯系
TairRedisLevelDBMDBTair聯系

Redis伺服器的周期性操作函數saverCron預設每隔100号碼就會執行一次,它的其中一項工作就是檢查save選項的條件是否已經滿足,滿足就執行GBSAVE指令。

(5)AOF(Append Only File)

與RDB儲存資料庫的鍵值對來持久化資料庫不同,AOF持久化是通過儲存Redis伺服器所執行的寫指令來記錄資料庫狀态的。

AOF持久化功能的實作可以分為指令追加(append)、檔案寫入、檔案同步(sync)三個步驟。

  • 指令追加:當AOF功能打開時,伺服器在執行完一個寫指令之後,會以協定格式将該指令追加到伺服器的aof_buf緩沖區末尾:
TairRedisLevelDBMDBTair聯系
  • 寫入和同步:Redis的伺服器程序就是一個事件循環,其中的檔案事件用于處理使用者指令和發送指令恢複,因為檔案事件可能會執行寫指令,使得一些内容被追加到aof_buf中,是以每次伺服器程序在結束一個事件循環前,都會調用flushAppendOnlyFile函數,考慮是否要将aof_buf中的内容寫入或同步AOF檔案。
TairRedisLevelDBMDBTair聯系

       flushAddpendOnlyFile的行為由appendfsync的值來決定,預設為everysec:

TairRedisLevelDBMDBTair聯系

當伺服器載入AOF檔案時,它會建立一個僞用戶端,然後不斷讀取指令并發送指令給伺服器。

由于AOF檔案會越來越長,Redis提供了AOF重寫的功能,通過讀取資料庫狀态來重新AOF檔案,将原來的N條指令合并為一條寫指令。

Redis的AOF重寫是由子程序來執行的,是以在AOF重寫期間伺服器仍然在處理指令請求,是以伺服器提供了一個AOF重寫緩沖區,當子程序開始重寫檔案時,資料庫将Redis伺服器執行的寫指令發送給AOF緩沖區和重寫緩沖區,在子程序完成AOF重寫工作後,會向父程序發送一個信号,父程序接到信号後,會調用一個信号處理函數,将AOF重寫緩沖區的指令寫入新AOF檔案,并将新的AOF檔案改名,原子地覆寫現有AOF檔案。

(6)事件

檔案事件:Redis為檔案事件編寫了多個處理器,分别用于實作不同網絡通信請求。檔案事件處理器基于React模式處理請求。

TairRedisLevelDBMDBTair聯系

時間事件:定時事件和周期事件(redis.c/serverCron函數)

Redis有兩個事件,何時處理哪個事件,花多少時間處理都由下面流程決定:

TairRedisLevelDBMDBTair聯系

2.多機資料庫

2.1複制

在Redis中,使用者可以通過執行SLAVEOF指令或設定slaveof選項來讓一個伺服器(slave)去複制另一個伺服器(master)。複制後二者将儲存相同的資料,稱為“資料庫狀态一緻”。下面分别介紹2.8版本前的複制實作,說明其存在的問題,然後介紹2.8版本開始新的複制功能。

(1)舊版複制功能原理

複制功能分為同步和指令傳播兩個操作。其中同步用于将從伺服器的資料庫狀态更新至主伺服器目前的狀态;指令傳播則用于在主伺服器的資料庫狀态被修改,導緻主從伺服器的資料庫狀态不一緻時,讓從伺服器的資料庫重新回到一緻狀态。

同步:當我們向從伺服器發送SLAVEOF指令時,從伺服器需要先對主伺服器發送SYNC指令來同步主伺服器的資料庫狀态,SYNC指令的執行步驟如下:

  • 從伺服器向主伺服器發送SYNC指令;
  • 收到SYNC指令的主伺服器執行BGSAVE指令,在背景生成一個RDB檔案,然後用一個緩沖區記錄從現在開始執行的所有寫指令;
  • 當主伺服器的BGSAVE指令執行完畢,主伺服器會将RDB檔案發送給從伺服器,從伺服器接收并載入這個RDB檔案,将自己的資料庫狀态更新至主伺服器執行BGSAVE指令時的資料庫狀态;
  • 主伺服器将緩沖區的所有寫指令發送給從伺服器,從伺服器執行這些寫指令,将自己的資料庫狀态更新至主伺服器資料庫目前所處的狀态。

指令傳播:在同步完後,當主伺服器接到寫指令時,會将該指令發送給從伺服器執行,主從伺服器将再次回到一緻狀态。

缺陷:主從複制分為兩種情況,即初次複制和斷線後重複制。舊版複制功能能夠很好地完成初次複制,但是對于斷線重複制來說卻效率非常低,因為從伺服器斷線再重連上主伺服器時,從伺服器和主伺服器狀态将不一緻,需要從新向主伺服器發送SYNC指令,從伺服器之前複制的内容都廢棄了。其實仔細考慮會發現,這麼做其中真的沒必要,從伺服器隻需要補上缺失的資料即可。

(2)新版複制功能原理

為了解決舊版複制功能處理斷線重複制的低效問題,Redis2.8使用了PSYNC代替SYNC指令來執行複制時的同步操作。

PSYNC指令具有完整重同步(full resync)和部分重同步(partial resync)兩種模式。其中完整重同步用于初次複制,步驟和舊版複制功能一樣;部分重同步則用于處理斷線後重複制。

主伺服器維護一個複制積壓緩沖區,先進先出,大小固定,當主伺服器接到一條寫指令,就會同時發送給從伺服器和複制積壓緩沖區,若複制積壓緩沖區滿,就将前面存儲的寫指令彈出。此外,主伺服器和從伺服器都維護一個複制偏移量。

部分重同步的步驟如下:

  • 從伺服器向主伺服器發送PSYNC指令和自己的複制偏移量;
  • 主伺服器接到PSYNC指令,看複制偏移量後面的寫指令是否還在複制積壓緩沖區,若在,就傳回+CONTINUE讓從伺服器執行偏移量後面的寫指令,若不在了,就傳回+FULLRESYNC執行完全重同步步驟。
  • 複制完後,更新自己的複制偏移量。

(3)複制的實作

  • 用戶端向從伺服器發送SLAVEOF指令:
127.0.0.1:12345> SLAVEOF 127.0.0.1 7788
           
  • 從伺服器将主伺服器的主機号和端口号記錄在自己的伺服器狀态中
redisServer{
    char* masterhost;
    int masterport;
}
           
  • 建立套接字:從伺服器基于主機号和端口号建立連向主伺服器的套接字,主伺服器成功建立連接配接後會建立從伺服器的用戶端狀态,并傳回OK。
  • PING:從伺服器發送PING指令,測試和主伺服器的連接配接狀态,收到主伺服器傳回的PONG響應即為連接配接正常。
  • 驗證身份:若從伺服器的masterauth選項有值,則從伺服器需要驗證身份,将發送下面的指令給主伺服器進行身份驗證,若主伺服器的requirepass有值,且值為從伺服器的密碼,則驗證通過,如果與從伺服器密碼不同,則傳回invalid password。
AUTH password
           
  • 發送監聽的端口号
REPLCONF listening-port <listening-port>
           
  • 同步PSYNC
  • 指令傳播。指令傳播過程中,從伺服器會以每秒一次的頻率向主伺服器測試連接配接狀态
REPLCONF ACK <replication_offset>
           

2.2Sentinel

Sentinel是Redis的一種可靠性機制。由一個或多個Sentinel執行個體組成的Sentinel系統可以監視主伺服器和其下面的從伺服器,并在主伺服器出現故障或下線超過使用者設定的時間時,自動為主伺服器實作故障轉移。

TairRedisLevelDBMDBTair聯系

故障轉移主要包含三步:

  • 從主伺服器下的從伺服器中挑選一個,作為新的主伺服器;
  • 給剩下的從伺服器發送複制指令,讓它們複制新的主伺服器,當所有從伺服器複制新的主伺服器時,故障轉移實作完畢;
  • sentinel系統會繼續監視已下線的主伺服器,當其上線時,再将它作為新主伺服器的從伺服器。

(1)啟動Sentinel

redis-sentinel path/to/your/sentinel.conf
或
redis-server path/to/your/sentinel.conf --sentinel
           
  • 初始化Sentinel:Sentinel本身其實就是一個Redis伺服器,是以初始化過程和Redis伺服器差不多,差別在于Sentinel不會使用鍵值對和資料庫操作指令、持久化指令、事務指令、腳本指令等,其SLAVEOF、釋出訂閱指令、檔案事件處理器、時間事件處理器都隻能Sentinel内部使用。
  • 将redis伺服器使用的代碼改為Sentinel專用的代碼:redis和Sentinel使用的代碼不同,如端口号(redis.h/REDIS_SERVERPORT=6379和sentinel.c/REDIS_SENTINEL_PORT=26279)、指令清單(指令清單的不同也解釋了為什麼用戶端不能向sentinel發送set、get等指令)等。
  • 初始化Sentinel狀态:Sentinel中一般狀态仍然由redis.h/redisServer變量儲存,而和Sentinel功能相關的狀态則由sentinel.c/sentinelState儲存。
TairRedisLevelDBMDBTair聯系
  • 初始化sentinelSate的masters屬性:masters字典記錄了所有被Sentinel監視的主伺服器的相關資訊,其中字典的鍵是被監視的主伺服器名字,值則是該主伺服器對應的sentinel.c/sentinelRedisInstance結構。每個sentinelRedisInstance結構代表一個被Sentinel監視的Redis伺服器執行個體,這個執行個體既可以是主伺服器、從伺服器,也可以是另一個Sentinel。masters字典的初始化是根據被載入的sentinel配置檔案來進行的。
TairRedisLevelDBMDBTair聯系
TairRedisLevelDBMDBTair聯系
  • 連接配接主伺服器:對于每個被監視的主伺服器,Sentinel會建立兩個連向主伺服器的異步連接配接(因為需要與多個執行個體建立多個連接配接),即指令連接配接和訂閱連接配接,指令連接配接用于向主伺服器收發指令,訂閱連接配接用于接收__sentinel__:hello頻道的資訊。

(2)監視實作

  • 擷取主伺服器資訊:Sentinel會以10秒一次的頻率通過指令連接配接向監視的主伺服器發送INFO指令,傳回分析傳回資料以獲得主伺服器的資訊,如主伺服器的run_id、該主伺服器下的從伺服器資訊,将其記錄在SentinelRedisInstance結構中。
TairRedisLevelDBMDBTair聯系
TairRedisLevelDBMDBTair聯系
  • 擷取從伺服器資訊:Sentinel在給主伺服器發送資訊,并得知新增了從伺服器後,将在Sentinel中建立該從伺服器的SentinelRedisInstance執行個體,然後向該從伺服器建立指令連接配接和訂閱連接配接,最後也以10秒一次的頻率通過指令連接配接向從伺服器發送INFO指令,擷取從伺服器的資訊。
  • 向主、從伺服器發送資訊:Sentinel會以2秒一次的頻率,通過指令連接配接向所有被監視的主、從伺服器的__sentinel__:hello頻道發送以下格式的指令:
TairRedisLevelDBMDBTair聯系
  • 接收伺服器的頻道資訊:當Sentinel和伺服器建立訂閱連接配接後,将向伺服器發送下面的指令,從伺服器的__sentinel__:hello頻道接收資訊。
TairRedisLevelDBMDBTair聯系

       多個Sentinel可以同時監視一個伺服器,當Sentinel發送資訊給伺服器時,其他的sentinel将都會收到這些資訊,然後用這些資訊來更新對該sentinel和該伺服器的認識。

       當Sentinel發現它監視的伺服器出現新的Sentinel時,将建立該Sentinel的SentinelRedisInstance執行個體,并建立與該Sentinel的指令連接配接,該Sentinel也會建立與目前Sentinel的指令連接配接,是以所有Sentinel将形成一個網絡。

TairRedisLevelDBMDBTair聯系
  • 檢測主觀下線:Sentinel會每秒向伺服器發送一個Ping指令,并依據其回複時長是否超過使用者設定值進而判斷伺服器是否主觀下線。
  • 檢測客觀下線狀态:當Sentinel判斷伺服器為主觀下線狀态時,會用下面指令詢問監視該伺服器的其他Sentinel對該伺服器的狀态判斷,若它們也認為該伺服器已下線,就将該伺服器判定為客觀下線狀态,接下來就開始實施故障轉移。
TairRedisLevelDBMDBTair聯系
  • 選舉領頭Sentinel:當一個Sentinel被判定為客觀下線,監視該伺服器的所有Sentinel就開始選舉,通過少數服從多數的方式選舉出領頭Sentinel,由領頭Sentinel執行故障轉移操作。

(3)故障轉移實作

  • 選舉新主伺服器:删除連接配接不正常的從伺服器,剩下的從伺服器按優先級、複制偏移量排序,選出主主伺服器,向它發送SLAVEOF no one指令,将其設為主伺服器。
  • 将從伺服器的複制目标換為新的主伺服器:通過SLAVEOF實作。
  • 将舊主伺服器設為新主伺服器的從伺服器:通過SLAVEOF實作。

2.3叢集

Redis叢集是Redis提供的分布式資料庫方案,叢集通過分片來進行資料共享,并提供複制和故障轉移功能。

(1)節點

叢集由獨立節點組成,剛開始所有節點互相獨立,通過下面的指令可以将目标節點與目前節點組合到一個叢集:

127.0.0.1:1111> CLUSTER MEET <ip> <port>
           

一個節點就是一個運作在叢集模式下的Redis伺服器,在啟動時根據cluster-enabled配置選項是否為yes決定是否開啟叢集模式。

節點使用clusterNode結構保持其狀态,包括節點的IP、端口、建立時間等。

向一個節點發送MEET指令後,兩個節點的通信過程如下:

TairRedisLevelDBMDBTair聯系

(2)槽指派

叢集通過分片方式儲存資料庫的鍵值對:叢集中的整個資料庫被分為16384個槽,資料庫中的每個鍵都屬于這16384個槽的其中一個,每個節點處理0個或最多16384個槽。當資料庫的所有槽都有節點在處理時,叢集處于上線狀态,否則處于下線狀态。

上一節我們将所有節點放到一個叢集中了,但該叢集仍然處于下線狀态,可以通過下面指令指定節點處理的槽:

CLUSTER ADDSLOTS <slot> [slot ...]
           

節點通過下面的結構記錄它處理的槽資訊:

TairRedisLevelDBMDBTair聯系

節點除了将自己負責處理的槽記錄在上面結構,還會記錄别的節點處理哪些槽,并将自己的slots數組通過消息發送給叢集中的其他節點,以告知它們自己目前負責處理哪些槽。

(3)在叢集執行指令

當叢集上線,就可以從用戶端發送指令給叢集。

處理鍵相關的指令時,由于采用分布式存儲,各個節點均隻處理一部分槽,是以需要進行下面的步驟:

TairRedisLevelDBMDBTair聯系
  • 給定鍵屬于哪個槽的計算方式:
TairRedisLevelDBMDBTair聯系
  • MOVED錯誤格式:
TairRedisLevelDBMDBTair聯系
  • 節點與單機資料庫的不同:節點隻能使用0号資料庫。

3.獨立功能

3.1釋出與訂閱

Redis的釋出與訂閱功能分為兩類:頻道的釋出與訂閱、模式的釋出與訂閱。

兩種類型的釋出指令均為PUBLISH,兩種訂閱指令分别對應于SUBSCRIBE、PSUBSCRIBE。

伺服器将頻道的釋出存儲于狀态的pubsub_channels屬性下,将模式的釋出與訂閱存儲于pubsub_patterns下。

TairRedisLevelDBMDBTair聯系
TairRedisLevelDBMDBTair聯系

當一個用戶端向伺服器的頻道發送資訊時,訂閱該頻道的所有用戶端都将收到該資訊,并且所有訂閱與該頻道相比對的模式的用戶端也都會收到該資訊。

3.2事務

(1)事務

Redis通過MULTI、EXEC、WATCH等實作事務功能。

一個事務的執行過程是以MULTI開頭,然後指令入隊,最後以EXEC送出事務。

事務中的指令都會存放在隊列裡面,每個Redis用戶端都有自己的事務狀态,這個事務狀态儲存在用戶端狀态的mstate屬性裡面:

TairRedisLevelDBMDBTair聯系
TairRedisLevelDBMDBTair聯系

(2)WATCH指令

WATCH是一個樂觀鎖,它可以在EXEC執行前監視任意數量的資料庫鍵,并在EXEC指令執行時,檢查被監視的鍵是否被修改過,如果是,伺服器将拒絕執行事務,并向用戶端傳回代表事務執行失敗的空回複。

具體地,每個資料塊都儲存一個watched_keys字典,其鍵是被監視的鍵,而值則是一個連結清單,連結清單記錄了所有監視相應資料庫鍵的用戶端。

TairRedisLevelDBMDBTair聯系

如果修改了被監視的鍵,那麼Redis将會将監視該鍵的所有用戶端的REDIS_DIRTY_CAS辨別打開。當伺服器接收到一個用戶端發來的EXEC指令時,伺服器會根據這個用戶端是否打開了REDIS_DIRTY_CAS辨別來決定是否執行事務。

(3)事務的ACID性質

原子性:原子性是指,事務中的指令要麼都執行,要麼都不執行。與傳統關系資料庫事務不同之處在于,Redis事務不支援復原(因為Redis追求簡單高效,而事務復原太複雜),Redis事務在事務隊列中的某個指令出現錯誤,整個事務也會繼續執行下去直到所有指令執行完。

一緻性:事務的一緻性是指,如果資料庫在執行事務前是一緻的,那麼在事務執行之後,無論事務是否執行成功,資料庫也應該是一緻的。Redis事務可能遇到三種類型的指令錯誤:入隊錯誤、執行錯誤和伺服器停機。對于入隊錯誤,伺服器将拒絕執行事務;對于執行錯誤,伺服器會繼續執行完剩下的指令,也不會影響錯誤産生前的指令執行結果,知識會對執行錯誤的指令進行相應的錯誤處理!!!!這不會影響一緻性??!!!;如果在事務執行過程中伺服器停機,那麼要麼資料庫是空白的(無持久化模式),要麼可以根據RDB或AOF恢複資料庫????!!!!這也不會影響一緻性,,,,怎麼感覺是睜着眼睛說瞎話。

隔離性:隔離性是指,多個事務并發執行,各個事務之間也互不影響。因為Redis使用單線程方式執行事務,并且事務執行過程中不會被打斷,是以Redis事務具有隔離性。

持久性:由配置決定。

===================================================================

LevelDB

LevelDB詳細介紹參考:https://www.cnblogs.com/zhihaowu/p/7884424.html

LevelDB是Google的開源、十億級K/V持久性資料庫,用C++語言實作。

1.1結構

TairRedisLevelDBMDBTair聯系
  • Current:目前版本
  • log:磁盤存儲,順序寫,被分成block,key無序,
TairRedisLevelDBMDBTair聯系
TairRedisLevelDBMDBTair聯系
  • Memtable:記憶體存儲,跳表,key有序
  • ImmutableMemtable:不可讀的記憶體存儲,跳表,key有序
  • SSTable:磁盤存儲,每個level資料被分成.sst檔案,.sst檔案又被分成block,所有.sst檔案内部有序,level0的任意兩個.sst檔案之間可能存在内容重疊。
TairRedisLevelDBMDBTair聯系
TairRedisLevelDBMDBTair聯系

Datablock:

TairRedisLevelDBMDBTair聯系

表索引:

TairRedisLevelDBMDBTair聯系

footer:

TairRedisLevelDBMDBTair聯系
  • Manifest:磁盤存儲,存儲每一層的檔案資訊
TairRedisLevelDBMDBTair聯系

LevelDB主要由Memtable、ImmutableMemtable、log檔案、Manifest檔案、SSTable檔案組成。磁盤中的.log檔案的key是無序的,采用順序寫,磁盤中的SSTable和記憶體中的Memtable中的key是有序的,Memtable内部采用跳表存儲資料。

1.2操作

插入新鍵值對:對于一個插入操作Put(Key,Value)來說,完成插入操作包含兩個具體步驟:首先是将這條KV記錄以順序寫的方式追加到之前介紹過的log檔案末尾,因為盡管這是一個磁盤讀寫操作,但是檔案的順序追加寫入效率是很高的,是以并不會導緻寫入速度的降低;第二個步驟是:如果寫入log檔案成功,那麼将這條KV記錄插入記憶體中的Memtable中,前面介紹過,Memtable隻是一層封裝,其内部其實是一個Key有序的SkipList清單,插入一條新記錄的過程也很簡單,即先查找合适的插入位置,然後修改相應的連結指針将新記錄插入即可。完成這一步,寫入記錄就算完成了,是以一個插入記錄操作涉及一次磁盤檔案追加寫和記憶體SkipList插入操作,這是為何levelDb寫入速度如此高效的根本原因。

删除:對于levelDb來說,并不存在立即删除的操作,而是與插入操作相同的,差別是,插入操作插入的是Key:Value 值,而删除操作插入的是“Key:删除标記”,并不真正去删除記錄,而是背景Compaction的時候才去做真正的删除操作。

讀取: LevelDb首先會去檢視記憶體中的Memtable,如果Memtable中包含key及其對應的value,則傳回value值即可;如果在Memtable沒有讀到key,則接下來到同樣處于記憶體中的Immutable Memtable中去讀取,類似地,如果讀到就傳回,若是沒有讀到,那麼隻能萬般無奈下從磁盤中的大量SSTable檔案中查找。因為SSTable數量較多,而且分成多個Level,是以在SSTable中讀資料是相當蜿蜒曲折的一段旅程。總的讀取原則是這樣的:首先從屬于level 0的檔案中查找,如果找到則傳回對應的value值,如果沒有找到那麼到level 1中的檔案中去找,如此循環往複,直到在某層SSTable檔案中找到這個key對應的value為止(或者查到最高level,查找失敗,說明整個系統中不存在這個Key)。

其他操作:

  • Compaction:Minor、Major compaction
  • Cache:Table cache、Block cache
TairRedisLevelDBMDBTair聯系
TairRedisLevelDBMDBTair聯系

總結:由此可以看出,LevelDB寫非常簡單,而讀非常複雜,是以适用于寫多讀少的場景。如果必須讀,就順序讀,比随機讀快很多。

================================================================================================

MDB

參考:https://www.cnblogs.com/loveis715/p/4681643.html?tdsourcetag=s_pcqq_aiomsg

1.1記憶體管理

由于Memcached是一個記憶體資料庫,是以不可避免的需要考慮記憶體碎片的問題:在長時間的配置設定及回收記憶體後,記憶體将趨向于散落在不連續的空間中,一方面增大了記憶體配置設定失敗的機率,另一方面也使得記憶體配置設定工作變得更為複雜。

為了解決這個問題,Memcached使用一種Slab結構,它将記憶體按1MB大小劃分為頁,然後在将頁分割為相同大小的記憶體塊,不同頁的記憶體塊大小不同,相同頁的記憶體塊大小一緻。

TairRedisLevelDBMDBTair聯系

       是以Memcached并不是直接根據需要記錄的資料的大小來直接配置設定相應大小的記憶體。在一條新的記錄到來時,Memcached會首先檢查該記錄的大小,并根據記錄的大小選擇記錄所需要存儲到的Slab類型。接下來,Memcached就會檢查其内部所包含的該類型Slab。如果這些Slab中有空餘的塊,那麼Memcached就會使用該塊記錄該條資訊。如果已經沒有Slab擁有空閑的具有合适大小的塊,那麼Memcached就會建立一個新的頁,并将該頁按照目标Slab的類型進行劃分。

  一個需要考慮的特殊情況就是對記錄的更新。在對一個記錄進行更新的時候,記錄的大小可能會發生變化。在這種情況下,其所對應的Slab類型也可能會發生變化。是以在更新時,記錄在記憶體中的位置可能會發生變化。隻不過從使用者的角度來說,這并不可見。

  Memcached使用這種方式來配置設定記憶體的好處則在于,其可以降低由于記錄的多次讀寫而導緻的碎片化。反過來,由于Memcached是根據記錄的大小選擇需要插入到的塊類型,是以為每個記錄所配置設定的塊的大小常常大于該記錄所實際需要的記憶體大小,進而造成了記憶體的浪費。當然,您可以通過Memcached的配置檔案來指定各個塊的大小,進而盡可能地減少記憶體的浪費。

  還有一個值得注意的事情就是,由于Memcached在計算到底哪個服務執行個體記錄了具有特定鍵的資料時并不會考慮用來組成緩存系統中各個伺服器的差異性。是以具有較大記憶體的Memcached執行個體将無法被充分利用。我們可以通過在具有較大記憶體的伺服器上部署多個Memcached執行個體來解決這個問題。

  由于緩存系統擁有有限的資源,是以其會在某一時刻被服務所産生的資料填滿。如果此時緩存系統再次接收到一個緩存資料的請求,那麼它就會根據Slab範圍内的LRU(Least recently used)算法以及資料的過期時間來決定需要從緩存系統中移除的資料

=================================================================

Tair

參考:https://www.cnblogs.com/chenny7/p/4875396.html

Tair是淘寶的一個開源分布式K/V存儲架構。作為一個分布式系統,Tair由一個ConfigServer中心控制節點和一系列data server服務節點組成。Tair是用C++語言實作,目前有java、C用戶端,但是它是基于套接字通信,是以也可以使用其他語言的用戶端。

ConfigServer:中心控制節點,管理所有data server并維護dataserver的狀态資訊,tair用戶端通過configserver擷取資料所在節點的位址;為了保證高可靠性,ConfigServer可通過heartbeat以一主一備的形式提供服務。

DataServer:對外提供服務的節點,并以心跳的形式将自身狀況彙報給config server,各節點完全等價,底層資料引擎是用RedisDB或LevelDB或MemoryDB。各節點地位并列。

TairRedisLevelDBMDBTair聯系

1.1基本概念

configID:全局唯一,辨別叢集,存儲于daimond,對應了叢集的configserver id和group name。

namespace:單個叢集範圍唯一,辨別應用,可以認為應用的資料存在自己的namespace中。不同namepace下可以有相同的key,但如果同一個應用中存放相同的key,内容就會受到影響,在簡單K/V形式下,會被覆寫,在rdb等帶有資料結構的存儲引擎下,内容會根據資料結構不同發生不同變化。

quota配額:每個namespace存儲區的大小限制,超過配額後資料将面臨最近最少使用的淘汰。持久化一群(ldb)沒有配額,但其mdb可以設定配額。

expireTime:資料的過期時間,當超過過期時間後,資料對應用不可見,但不同引擎有不同政策清理過期資料。

1.2存儲引擎

Tair的存儲引擎提供了一個抽象層,隻要滿足存儲引擎提供的接口,就可以很友善的替換Tair底層的存儲引擎。Tair主要有如下三種存儲引擎:

  • mdb:定位于cache緩存,支援k/v操作和prefix操作。
  • rdb:定位于cache緩存,支援k/v操作和list、set、sorted set和hash資料結構。
  • ldb:定位于高性能存儲,采用lebelDB作為引擎,并可選擇内嵌mdb cache加速,這種情況下,cache與持久化存儲的資料一緻性由tair進行維護。支援l/v、prefix等資料結構。
TairRedisLevelDBMDBTair聯系

rdb:

TairRedisLevelDBMDBTair聯系
TairRedisLevelDBMDBTair聯系

ldb:

TairRedisLevelDBMDBTair聯系
TairRedisLevelDBMDBTair聯系

MDB:暫時沒看懂

TairRedisLevelDBMDBTair聯系

1.3分布式政策

tair的分布式采用一緻性hash算法,将所有key分到Q個桶中,桶是負載均衡和資料遷移的基本機關。Config Server根據一定政策(位置安全優先、負載均衡化優先)把每個桶指派到不同data server上,用戶端依據key的hash算法将key指派到指定桶上。

具體地,首先用戶端計算hash(key),得到key所對應的bucket;configserver維護了一張由bucket映射到dataserver的對照表,然後在configserver差找該bucket對應的dataserver;用戶端再與相應的dataserver進行通信。

bucket   data server
0        192.168.10.1
1        192.168.10.2
2        192.168.10.1
3        192.168.10.2
4        192.168.10.1
5        192.168.10.2
           

如上圖所示,每個節點負責兩個桶。當一個新節點加入時,configserver會重新build該映射表,并将對照表發送給資料節點,資料節點發現部分桶不屬于自己管理時,會将這些桶發送給目标節點,同時服務不中斷,遷移完成後,用戶端可以從config server同步到新的對照表。

當通路正在遷移的資料時,tair也有一些解決方案,如通路要遷移的A、B和C桶:A桶已經遷移完成,B桶正在遷移,C桶還沒開始遷移。當通路A桶,資料節點會傳回資訊告訴用戶端重新去ConfigServer擷取對照表,然後再通路遷移後的節點;當通路C桶,就和正常資料一樣傳回;當通路B桶,目前資料節點也會正常傳回資料,因為遷移過程中,目前節點和目标節點都會記錄log,隻有資料全部遷移完成,才算遷移結束。

通常用戶端啟動時,會從config server擷取到對照表并緩存到本地,是以通路資料節點前不再需要通路config server,config server也就不會有性能瓶頸問題。ConfigServer維護的對照表有一個版本号,每次更新版本号會增加,後面configServer對照表更新後,會通過心跳将新的對照表同步給資料節點,用戶端通路資料節點時,資料節點會将對照表的版本号放入response傳回給用戶端,用戶端發現自己的版本号與收到的版本号不一緻,則會主動與ConfigServer通信,請求新的對照表。是以即使ConfigServer暫時不可用,也不會對整個叢集造成大的影響。

1.4一緻性和可靠性

分布式系統中的可靠性和一緻性是無法同時保證的,因為可靠性就有複制過程,複制過程就必須允許網絡錯誤的發生。tair采用複制技術以提高可靠性,在沒有錯誤發生時,tair提供強一緻性,但是當有data server發生故障時,可能發生資料丢失。

version

為了防止并發更新導緻的資料覆寫問題,Tair中每個資料都包含版本号,并且在每次更新的時候版本号都會遞增。

我們get資料時傳回的都是DataEntry對象,通過getVersion()可以擷取到版本号,在put的時候将版本号作為參數放進去即可。

version改變的邏輯如下:

          1)如果put新資料且沒有設定版本号,會自動将版本号設定為1;

          2)如果put是更新老資料且沒有版本号,或者put傳來的參數版本與目前版本抑緻,版本号自增1;

          3)如果put是更新老資料且傳來的參數版本與目前版本不一緻,更新失敗,傳回VersionError;

          4)如果put傳入version為0,則強制更新成功,版本号自增1.

=================================================================

聯系

1.1Tair與Redis的關系

Tair是淘寶開源的分布式緩存系統,它将功能子產品化,并将底層存儲細節抽離出來,可以接入不同存儲引擎。Redis是一種KV存儲系統,作為存儲引擎接入Tair,稱為RDB,Tair繼承了Redis的豐富操作,包括set、sortedset、list、hash、

1.2為什麼淘寶用Tair而不用redis

因為redis的叢集,如果想要擷取緩存資料,用戶端需要給出伺服器位址,而叢集的伺服器通常需要變動,是以可維護性很差。Tair就改進了這種不足,它像hadoop那樣,弄了個tair中心節點,将伺服器位址交給中心節點管理,底層再用redis或leveldb。

繼續閱讀