資料存儲方式
. mysql:
行存儲, 存儲結構分為聚簇索引(innodb)和非聚簇索引(myisam),均是采用b+樹結構。
聚簇索引:
必有主鍵索引,主鍵索引的葉子節點存儲了表的資料。非葉子節點都是索引關鍵字,但是不是記錄資料或者資料位址。
可能會有二級索引,二級索引的葉子節點存儲的是主鍵值(而不是行指針)。
(這樣可以減少目前行移動時,二級索引的維護,但會讓二級索引占用更多的空間)。
非聚簇索引:
主鍵索引和二級索引存儲上沒有任何差別,所有的節點都是索引,葉子節點存儲的是索引+索引對應的記錄的資料。
差別:
- 聚簇索引讀一定範圍的資料比較快。
- 聚簇索引主鍵和行資料會緩存到buffer中,用主鍵查資料更快
- 聚簇索引資料變更減少二級索引的維護工作。
- 插入速度嚴重依賴于插入順序,聚簇索引更新主鍵代價高。
- 聚簇索引二級索引占用一些空間
redis
redis整個資料庫統統加載在記憶體當中進行操作,定期通過異步操作把資料庫資料flush到硬碟上進行儲存。
持久化分為AOF和RDB兩種,RDB生成快照檔案,AOF日志記錄指令。
提供了write和fsync兩種方式寫入記憶體。
es
https://zhuanlan.zhihu.com/p/33671444
反向索引的方式存儲。
主要是通過建立一個term index(trie樹),這棵樹不會包含所有的term,它包含的是term的一些字首。通過term index可以快速地定位到term dictionary的某個offset,然後從這個位置再往後順序查找(二分法查找term dictionary)。再加上一些壓縮技術(搜尋 Lucene Finite State Transducers) term index 的尺寸可以隻有所有term的尺寸的幾十分之一,使得用記憶體緩存整個term index變成可能。
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLicmbw5iN2gTO3YTMyYTMtEzM4kDNwQTNxgDM1ADMyAjMtQTM3IDN48CX1ADMyAjMvwFNxcjM0gzLcd2bsJ2Lc12bj5ycn9Gbi52YuAjMwIzZtl2Lc9CX6MHc0RHaiojIsJye.png)
-
為什麼比mysql快?
因為比mysql多了term index(存在記憶體中,以FST(finite state transducers)的形式儲存)加快檢索,從term index查到對應的term dictionary的block位置之後,再去磁盤上找term,大大減少了磁盤的random access次數。
對于mysql來說,如果你給age和gender兩個字段都建立了索引,查詢的時候隻會選擇其中最selective的來用,然後另外一個條件是在周遊行的過程中在記憶體中計算之後過濾掉。
- es聯合索引利用Skip List / bitset 合并。
每一行叫document,每個單詞叫term,單詞清單叫dictionary。存儲的doc-id list叫posting-list(int的數組,存儲了所有符合某個term的文檔id)。
讀寫
mysql
-
讀
二級索引查找到主鍵索引-查找到相關資料
-
寫
如果主鍵連續,innodb順序寫io。如果亂序需要取出每條記錄對應的實體block,會引起大量的随機io。(innodb提供了insert buffer,合并插入操作,改亂序為順序)
Redis
- 讀記憶體
-
寫入到記憶體中,如果開了aof,aof會以日志的形式記錄每個寫操作。
觸發方式:有寫操作就寫、每秒定時寫(也會丢資料)。
ES
- 查詢反向索引
-
先寫入buffer,在buffer裡的時候資料是搜尋不到的;同時将資料寫入translog日志檔案。
2)如果buffer快滿了,或者到一定時間,就會将buffer資料refresh到一個新的segment file中,但是此時資料不是直接進入segment file的磁盤檔案的,而是先進入os cache的。這個過程就是refresh。
每隔1秒鐘,es将buffer中的資料寫入一個新的segment file,每秒鐘會産生一個新的磁盤檔案,segment file,這個segment file中就存儲最近1秒内buffer中寫入的資料。
但是如果buffer裡面此時沒有資料,那當然不會執行refresh操作咯,每秒建立換一個空的segment file,如果buffer裡面有資料,預設1秒鐘執行一次refresh操作,刷入一個新的segment file中。
作業系統裡面,磁盤檔案其實都有一個東西,叫做os cache,作業系統緩存,就是說資料寫入磁盤檔案之前,會先進入os cache,先進入作業系統級别的一個記憶體緩存中去。
隻要buffer中的資料被refresh操作,刷入os cache中,就代表這個資料就可以被搜尋到了
-
資料寫入 --> 進入ES記憶體 buffer (同時記錄到translog)--> 生成反向索引分片(segment)
2、将 buffer 中的 segment 先同步到檔案系統緩存中,然後再刷寫到磁盤
資料一緻性保證/容災
單節點:
https://sq.163yun.com/blog/article/172546631668785152
mysql存在redo日志和undo日志。通過redo日志和checkpoint保證單機資料不丢失。
redo log記錄了對實際資料檔案的實體變更(資料檔案的什麼位置資料做了如何的變更)。
InnoDB也是采用了WAL(日志優先落盤)。
資料庫down機回放log檔案恢複。
多節點:
MySQL提供了master-slave和group replication 叢集級别的容災方案。
- Master-Slave架構主要思路是:master負責業務的讀寫請求,然後通過binlog複制到slave節點.
- 主從架構存在資料不一緻的問題,是以MySQL5.7出現了Mysql Group Replication方案,mgr采用paxos協定實作了資料節點的強同步,保證了所有節點都可以寫資料,并且所有節點讀到的也是最新的資料。
單節點也是通過translog的方式恢複,多節點通過增加replica shard解決。
primary shard首先接收client端發送過來的資料,然後将資料同步到replica shard中,當replica shard也寫入成功後,才會告知client資料已正确寫入,這樣就防止資料還沒寫入replica shard時,primary挂掉導緻的資料丢失。
分布式
master-slave和group replication
-
主從同步,讀寫分離。
Master會将資料同步到slave,而slave不會将資料同步到master。Slave啟動時會連接配接master來同步資料。缺點是資料量很大的情況下,叢集的擴充能力還是受限于單個節點的存儲能力
-
資料分片模型
可以将每個節點看成都是獨立的master,然後通過業務實作資料分片。
結合上面兩種模型,可以将每個master設計成由一個master和多個slave組成的模型。
-
RedisCluster,存儲單元化
将所有存儲區域劃分為16384個slots(槽位),每個節點負責一部分槽位,槽位的資訊存儲于每個節點中。當用戶端請求進來時候會拉去一份槽位資訊清單緩存在本地,RedisCluster的每個節點會将叢集的配置資訊持久化到自己的配置檔案中,是以需要引入一套可維護的配置檔案管理方案,盡量做到自動化。
槽位算法:
RedisCluster 預設會根據key使用crc32算法進行hash得到一個整數,然後用這個整數對16384取模定位key所在的槽位。它還運作使用者在key字元串裡面嵌入tag将key強制寫入指定的槽位。
遷移:
- 首先使用CLUSTER GETKEYSINSLOT 指令擷取該slot中所有的key, 然後每個key依次用MIGRATE指令轉移資料。
- 資料轉移完畢之後,正式将slot指派給新的節點
當有新的節點加入或者斷開節點時,就會觸發Redis槽位遷移。
當一個槽位正在遷移時候在原節點的狀态為migrating,在目标節點的狀态為importing。
原節點的單個key執行dump指令得到序列化内容,再向目标節點發送restore攜帶序列化内容作為參數的指令,目标節點接收到内容後反序列化複制到記憶體中,響應給原節點成功。原節點收到成功響應後把目前節點的key删掉就完成了節點資料遷移。
-
Redis主庫的災備模式(Redis Sentinel)
主節點down機的時候隻能手動切換機器。是以redis引入了自動切換機器的哨兵架構模式。
前提:
首先哨兵服務單獨部署,需要保證高可用。然後引入zookeeper等分布式協調元件,保證哨兵可以感覺redis叢集的狀态。
作用:
哨兵負責監控主節點的監控狀态,當主節點不可用時,自動選一個從節點切換為主節點。
觸發:
用戶端在請求主節點時通路失敗,會通過Redis哨兵查詢主節點的位址。
成功後再将新的主節點清單緩存到用戶端中。
等故障主節點恢複後會作為一個新的隻讀從節點加入叢集。
自己實作的分布式算法,類似raft。
提供分片功能,并且每個分片都有replica。寫入時,replica shard會同步資料。
一台機器down機,replica shard會變成primary shard。
如果master down機,重新選主。