Mysql要做到主從復制,就是A服務把自己所做的增刪改的操作全都記錄在日志中,B數據庫就根據這份日志上面的操作在自己身上再操作一遍,這樣就實現了主從復制;
-
當 master 主服務器上的數據發生改變時,則將其改變寫入二進制日志文件(binlog)中;
-
salve 從服務器會在一定時間間隔內對 master 主服務器上的二進制日志進行探測,探測其是否發生過改變;
-
如果探測到 master 主服務器的二進制日志發生了改變,則開始一個 I/O Thread 請求 master 二進制事件;
-
同時 master 主服務器為每個 I/O Thread 啟動一個dump Thread,用於向其發送二進制事件;
-
slave 從服務器將接收到的二進制事件保存至自己本地的中繼日志文件(relay log)中;
-
salve 從服務器將啟動 SQL Thread 從中繼日志relay log中讀取二進制日志,在本地重放,使得其數據和主服務器保持一致;
-
最后 I/O Thread 和 SQL Thread 將進入睡眠狀態,等待下一次被喚醒;
MySql高並發的處理方案就是多主多從,可以極大地提高數據庫的容災能力.
二.MyCat中間件管理Mysql集群原理
Mycat已經不是一個單純的MySQL代理了,可以支持MySQL、SQL Server、Oracle、DB2、PostgreSQL等主流數據庫,也支持MongoDB這種新型NoSQL存儲,未來還會支持更多類型的存儲 Mycat主要作用
-
數據庫的讀寫分離 (自動實現)
當主出現故障后,mycat自動切換到另一個主上,進而提供高可用的數據庫服務,當然我需要部署多主多從的模式
-
數據庫分庫分表
-
Mycat的水平拆分:拆表,一張表中的數據拆分到多個服務器上水平拆分適合數據量巨大的單表,一般的項目比較少的會使用這個,因為一張表就有數十千萬的數據量不多見
-
Mycat的垂直拆分:不同的表不同庫
-
三、事務的核心屬性:ACID
-
原子性(A):要么不執行,要么全部執行
-
一致性(C):各個時候的狀態都是一致的,是目的。
-
隔離性(I):事務之間不能相互影響
-
持久性(D):保證不丟失。 Redo log 保證
1.事務並發導致的問題
-
臟讀:讀未提交
-
不可重復讀:同一個事務中,同一條記錄的兩次查詢結果不一樣。
-
幻讀:同一個事務中,統計的記錄數不同。(因為另外一個事務insert和update)
-
丟失更新:兩個事務同時修改,其中一次的更新被覆蓋。 個人認為其實這個並不算是問題,只是業務上經常遇到的問題。
2.更新丟失的解決思路
舉例:兩個事務都是A+100 ,可能出現只加了100的情況,解決思路
select a where id = 1 ;
a1 = a + 100
update a = a1 where id = 1 ;
-
單條語句的原子性:update T set a = a + 50 where id = 1 ;
-
這個案例是可以的,但是應對比較復雜的場景時,可能不可以。
-
-
悲觀鎖
select a where id = 1 for update
update T set a = a + 50 where id = 1
commit-
缺點:不利於並發、且容易出現數據庫死鎖。
-
-
樂觀鎖:利用CAS(compare and set)
while(!result){
select a,version where id = 1;
a1 = a + 100
update T set a = a1 where id = 1 and version = version0;
}
-
分布式鎖(復雜場景推薦)
-
如果非常復雜的場景,涉及到很多表的結果,那么使用分布式鎖。
-
-
常見死鎖的場景
順序相反的Update語:
事務1 update T1 where id =1;
update T1 where id =5;
事務2 update T1 where id =5;
update T1 where id =1; -
隔離級別
-
讀未提交:相當於什么都沒做
-
-
讀已提交:解決了臟讀問題,可能出現、不可重復讀、幻讀、丟失更新
-
Oracle的事務隔離級別是讀已提交。
-
-
可重復讀:解決了臟讀、不可重復讀
-
注意:Mysql的事務隔離級別是可重復讀,但是它通過技術手段解決了幻讀問題。
-
串行化:事務串行執行,能保證強隔離、但是性能最低。
-
四、事務的實現
-
問題:修改數據后同步刷新到磁盤性能低下。
-
需求:為了獲得好的效率,不管對於已提交的事務還是未提交的事務,可以立即刷盤,也可以延遲刷盤。
-
解決思路:內存操作數據+ Write-Ahea Log技術實現,即提交事務時只保證Write-Ahea Log文件刷盤,內存操作數據異步刷盤。如果宕機了可以通過Write-Ahea Log恢復數據。
-
InnoDB中的實現:Redo log + Undo log
-
1.Master Thread 線程(刷臟頁等)
-
Master Thread 每秒一次的操作
-
日志緩沖刷新到磁盤,即使這個事務還沒有提交(總是)
-
合並插入緩沖(可能)
-
前一秒內發生的IO次數是否小於5,那么合並插入
-
-
至多刷新100個InnoDB的緩沖池中的臟頁到磁盤(可能)
-
緩存池臟頁比例 buf_get_modified_ratio_pct >innodb_max_dirty_pages_pct(默認90%)時,將100臟頁寫入磁盤中。
-
-
如果當前沒有用戶活動,則切換到background loop(可能)
-
-
Master Thread 10秒的操作
-
刷新100個臟頁到磁盤(可能的情況下)
-
合並至多5個插入緩沖(總是) 合並插入緩沖的時機是,刷臟頁之后
-
將日志緩沖刷新到磁盤(總是)
-
刪除無用的undo頁(總是)
-
刷新100個或者10個臟頁到磁盤(總是)。 緩存池臟頁比例 buf_get_modified_ratio_pct有超過50%的臟頁面,則刷新100個臟頁到磁盤,如果臟頁的比例小於70%,則只需要刷新10%的臟頁面到磁盤
-
-
Master Thread 若當前沒有用戶活動(數據庫空閑)或者數據庫關閉(shutdown),就會切換到這個循環。background loop會執行以下操作:
-
刪除無用的undo頁(總是)
-
合並20個插入緩存(總是)
-
跳回到主循環(總是)
-
不斷刷新100個頁面知道服務條件(可能,跳轉到flush loop中完成)
綜上,InnoDB存儲引擎最大只會刷新100個臟頁到磁盤,合並20個插入緩沖。隨着磁盤技術飛速,現在這兩個值可以調整,可以適當調高。
-
在合並插入緩沖時,合並插入緩沖的數量為Innodb_io_capacity值的5%
-
在從緩沖區刷新臟頁時,刷新臟頁的數量為Innodb_io_capacity
-
2.Redo log 持久性原理
和Page數據一樣,Redo log先寫到Redo log cache內存中,再刷盤。
-
Redo log本身也可以是異步的,由innodb_flush_log_at_trx_commit控制刷盤策略。
-
0:不刷盤
-
1:每提交一次,刷一次盤。
-
2:表示每次事務提交時都只是把redo log寫到文件系統page cache,由innodb_flush_log_at_timeout值決定刷盤策略。
-
除此之外 Master_thread還有兩種情況的刷盤:
-
每秒一次
-
日志緩存大於1/2時,刷一次
-
每10秒一次
-
總結:設置為1能保證不丟失數據,雖然慢了些。
-
3.Redo log 特點
Redo log是一個固定大小的文件,循環使用。
-
日志記錄方法:
-
類似Binlog 的Statemement(邏輯記法):記錄SQL
-
類似Binlog 的Row(邏輯記法),記錄每張表的修改前的值和修改后的值。 (表,行,修改前的值,修改后的值)
-
物理日志記法(邏輯記法):記錄每個Page修改前的值和修改后的值。 (PageID,offset1,length1,修改前的值,修改后的值) (PageID,offset2,length2,修改前的值,修改后的值)
Redo log采用物理日志記法和邏輯日志記法綜合(Physiological Logging): 先以Page為單位記錄日志,每個Page中采用邏輯記法
-
-
由於不同的事務日志在Redo log 中是交叉的,未提交的事務也會在Redo log 中。
-
宕機后重啟,通過重新根據Redo log重放(當然,需要通過一定的算法確認從哪里開始執行,以及冪等性)。
-
因為Redo log也會執行包含的未提交的事務日志,所以對於未完成的日志,需要用Undo log回滾。Undo log只在事務提交過程中有用,一旦提交了,就沒用了,不能再回滾了。
-
Undo log的事務回滾的作用是:轉換成相應的事務語句執行, 比如delete 回滾 變成insert執行。實際執行過程和一般的事務沒區別。即執行時也是順序寫LOG
綜上要點:
-
不管是已提交的事務還是未提交的事務都會寫Redo log,
-
不管是已提交的事務還是未提交的事務的Page數據可能已經刷盤。
-
未提交的事務對於的Page數據在宕機后會回滾。
-
事務的回滾不是“物理回滾”,而是轉換成SQL執行commit
五、Undo log
-
Undo log三大作用
-
宕機未提交回滾
-
實現ACID中的I隔離性
-
多版本並發控制 (Multi Version Concurrency Control MVCC )
-
可重復讀,從當前事務開始的時候,到事務結束,select讀取的是快照。
-
讀提交:當前語句開始的最新快照
-
快照由Undo log計算得到
-
-
高並發
-
-
並發控制的三種讀寫策略
-
互斥鎖:寫寫互斥、讀寫互斥、讀讀互斥
-
讀寫鎖:寫寫互斥、讀寫互斥、讀讀並發
-
CopyOnWrite:讀寫、寫寫、讀讀都可以並發。 如java中的CopyOnWriteArrayList
InnoDB中的CopyOnWrite思想是使用Undo log實現的
-
-
隔離性的實現:每條記錄有多版本
-
快照讀(讀的可能是歷史版本):select語句 快照由Undo log計算得到
快照讀讀取的是每個事務開始的最新版本,所以隨着時間的推移,可能被其他版本修改了,所以可能是歷史版本。
-
當前讀(讀的是最新版本)
-
select for update
-
insert/update/delete
-
-
-
Undo log 和 Redo log 都是每執行一個語句,就會寫入緩存中,而且保證提交后已刷盤。
六、Binlog
-
Binlog是位於存儲引擎的上層,而不在存儲引擎層,主要作用是節點間的復制。注意:Undo log 和 Redo log都是InnoDB存儲引擎的日志。
-
Binlog-format格式Statemement、Row、mixed
-
基於行的復制(Row模式):復制的是真實數據。
-
基於語句的復制(Statemement):復制的是SQL語句。
-
混合模式的復制(mixed):推薦的值。
-
正常情況下(大多數SQL)都是使用自動Statemement模式;
-
對於使用Statemement模式復制不安全場景中自動采用Row模式
-
-
-
寫入邏輯:事務執行過程中,先把日志寫到文件系統binlog cache,事務提交的時候,再把binlog cache寫到binlog文件中。write(寫cache) 和fsync(刷盤)的時機,是由參數sync_binlog控制的:
-
sync_binlog=0的時候,表示每次提交事務都只write,不fsync;
-
sync_binlog=1的時候,表示每次提交事務都會執行fsync;
-
sync_binlog=N(N>1)的時候,表示每次提交事務都write,但累積N個事務后才fsync。
為了不丟失數據,需要設置成1
-
-
Binlog 與 Redo log的二階段提交,和分布式事務的兩階段2PC提交時一樣的。
-
第一階段 : InnoDB prepare :持有prepare_commit_mutex,並且write/sync redo log;將回滾段設置為Prepared狀態,binlog不作任何操作
-
是否刷盤取決於innodb_flush_log_at_trx_commit參數,設置為1時才在此時提交。
-
為提供性能,也支持組提交的刷盤。
-
-
第二階段:包含兩步,
-
write/sync Binlog
-
是否刷盤取決於sync_binlog參數,設置為1時才在此時提交。
-
為提供性能,也支持組提交的刷盤來減少刷盤次數,下面兩個參數可以控制組提交。 binlog_group_commit_sync_delay=N :延遲多少時間(等待數據一起刷盤) binlog_group_commit_sync_no_delay_count=N :等待事務個數一起刷盤
-
-
InnoDB commit (寫入COMMIT標記后釋放prepare_commit_mutex)
-
-
以 binlog 的寫入與否作為事務提交成功與否的標志,innodb commit標志並不是事務成功與否的標志。 因為此時的事務崩潰恢復過程如下:
-
崩潰恢復時,掃描最后一個Binlog文件,提取其中的xid;
-
InnoDB維持了狀態為Prepare的事務鏈表,將這些事務的xid和Binlog中記錄的xid做比較,
-
如果在Binlog中存在,則提交,否則回滾事務。
-
如果在寫入innodb commit標志時崩潰,則恢復時,會重新對commit標志進行寫入;
-
在prepare階段崩潰,則會回滾,在write/sync binlog階段崩潰,也會回滾。
-
-
七、主從
-
主從形式
-
一主一從
-
主主復制(互為主從)
-
一主多從(常用於擴展系統讀取性能,因為讀是在從庫讀取的)
-
多主一從(5.7開始支持)
-
聯級復制
-
-
主從模式:
-
腦裂:主機網絡連接短時間丟失,后又重新聯機,出現兩個主機。
-
處理方式:確保掛掉的服務器真的掛了,使用kill -9 殺。
-
-
主從復制的三種模式:
-
異步模式(默認):slave主動拉,
-
同步模式(Master主動push):必須所有從節點返回成功才commit。
-
半同步模式:主節點至少收到一台從節點返回成功才commit。否則需要等待直到超時時間然后切換成異步模式再提交
-
-
雙主模式:常用用於為了方便不同地理位置上的用戶。
參數log_slave_updates設置為on,表示備庫還行relay log后生成的binlog
主和主之間互為主備,當從A主機寫入的數據,會在B主機中重放。
為了解決循環復制的問題:保證兩個Master的service id不同。 -
級聯復制(分擔主節點復制的壓力):讓3~5個從節點連接主節點,其它從節點作為二級或者三級與從節點連接。
-
主從延遲來源
-
備機性能比主機差,因為有主備切換,所有一般會購買同規格的機器。
-
備庫壓力大:使用不合理,解決的方法可以多接幾個從庫。
-
大事務:主庫執行完才寫binlog,所以一個10分鍾的語句,到從庫可能延遲10分鍾。 比如一次性delete大量數據,可以改進:分多個事務刪除。
-
備庫的復制能力
-
網絡延遲
-
-
主從延遲的讀寫分離方案(延遲不可避免)
-
-
sleep方案:睡眠幾秒再讀。 聽着也不靠譜
-
判斷主備無延遲方案
-
判斷seconds_behind_master是否等於0
-
比位點方案
Master_Log_File和Read_Master_Lig_Pos表讀到的主庫最新位點
Relay_Master_Log_File和Exec_Master_Lig_Pos表備庫執行的最新位點
-
對比GTID是否確保主備不延遲
但是總是延遲的,因為即使日志是最新的,但是日志文件傳過來到執行也需要時間。
-
-
配合semi-sync方案:確保有一個從庫返回OK了才提交事務。 缺點:性能問題、對多從的情況下,仍然有問題。
-
等主庫位點方案:查找等未點,如果超過1秒,就是轉到從庫去讀。 缺點:超過等待時間,主庫壓力
-
等GTID方案:查找XXX未點,如果超過時間,就是轉到從庫去讀。 缺點:超過等待時間,主庫壓力
-
-
主備切換策略:
-
可靠性優先
-
seconds_behand_master小於某個值(比如5秒)繼續下一步,否則重試。
-
主庫設置為只讀
-
等待seconds_behand_master值為0后,把從庫改成可讀寫。
-
切換到從庫 說明:一般由HA系統完成;系統存在一定的不可用時間,但是保證了可靠性。
-
-
可用性優先:把從庫改成可讀寫,然后直接切換。會丟失數據,不推薦。 一主多從切換策略:(案例中一主多從,還有主機中還有一備節點,主機故障切換到備節點)
-
主機故障切換到備節點: 備機提升為新主機后,從節點需要重新指向新主機。 為了解決確定同步點的難題,MySQL5.6 引入了GTID.
-
八、存儲結構和索引的實現
(1)索引采用B+數的邏輯結構: 主鍵索引采用B+樹,找到的是記錄的指針。 非主鍵索引也是一顆B+數,找到的主鍵的值。再用主鍵的值去查主鍵的B+樹查找結果。 (2)B+樹與磁盤是怎樣對應的?
(3)B+樹
九、高可用方案選型
MySQL的各種高可用方案,大多是基於以下幾種基礎來部署的:
-
基於主從復制
-
基於Galera協議;
-
基於NDB引擎;
-
基於中間件/proxy;
-
基於共享存儲;
-
基於主機高可用;
最常見的就是基於主從復制的方案,其次是基於Galera的方案
-
雙節點主從 + keepalived/heartbeat
-
多節點主從+MHA/MMM :至少需要三節點,優先推薦MHA
-
多節點主從+etcd/zookeeper :至少需要三節點,在較大規模環境下建議選用
-
基於Galera協議的高可用方案
Galera是Codership提供的多主數據同步復制機制,可以實現多個節點間的數據同步復制以及讀寫,並且可保障數據庫的服務高可用及數據一致性。
基於Galera的高可用方案主要有MariaDB Galera Cluster和Percona XtraDB Cluster(簡稱PXC),目前PXC用的會比較多一些。
采用PXC的主要目的是解決數據的一致性問題,高可用是順帶實現的
MySQL Group Replication 官方推薦基於Paxos協議的高可用集群方案。