數據存儲方式
. 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變成可能。
- 為什么比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 先同步到文件系統緩存中,然后再刷寫到磁盤
數據一致性保證/容災
mysql
單節點:
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協議實現了數據節點的強同步,保證了所有節點都可以寫數據,並且所有節點讀到的也是最新的數據。
ES
單節點也是通過translog的方式恢復,多節點通過增加replica shard解決。
primary shard首先接收client端發送過來的數據,然后將數據同步到replica shard中,當replica shard也寫入成功后,才會告知client數據已正確寫入,這樣就防止數據還沒寫入replica shard時,primary掛掉導致的數據丟失。
分布式
mysql
master-slave和group replication
Redis
- 主從同步,讀寫分離。
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哨兵查詢主節點的地址。
成功后再將新的主節點列表緩存到客戶端中。
等故障主節點恢復后會作為一個新的只讀從節點加入集群。
ES
自己實現的分布式算法,類似raft。
提供分片功能,並且每個分片都有replica。寫入時,replica shard會同步數據。
一台機器down機,replica shard會變成primary shard。
如果master down機,重新選主。