分布式从mysql查数据_技术分享 | 从库数据的查找和参数 slave_rows_search_algorithms...
作者:高鵬
文章末尾有他著作的《深入理解MySQL主從原理 32講》,深入透徹理解MySQL主從,GTID相關技術知識。
本文節選自《深入理解MySQL主從原理》第24節
注意:本文分為正文和附件兩部分,都是圖片格式,如果正文有圖片不清晰可以將附件的圖片保存到本地查看。
我們前面已經知道了對于 DML 語句來講其數據的更改將被放到對應的 Event 中。比如‘Delete’語句會將所有刪除數據的 before_image放到DELETE_ROWS_EVENT 中,從庫只要讀取這些 before_image 進行數據查找,然后調用相應的‘Delete’的操作就可以完成數據的刪除了。下面我們來討論一下從庫是如何進行數據查找的。
本節我們假定參數 binlog_row_image 設置為‘FULL’也就是默認值,關于 binlog_row_image 參數的影響在第11節已經描述過了。
一、從一個列子出發
在開始之前我們先假定參數‘slave_rows_search_algorithms’為默認值,即:
TABLE_SCAN,INDEX_SCAN
因為這個參數會直接影響到對索引的利用方式。
我們還是以‘Delete’操作為例,實際上對于索引的選擇‘Update’操作也是一樣的,因為都是通過 before_image 去查找數據。我測試的表結構、數據和操作如下:
mysql> show create table tkkk \G
*************************** 1. row ***************************
Table: tkkk
Create Table: CREATE TABLE `tkkk` (
`a` int(11) DEFAULT NULL,
`b` int(11) DEFAULT NULL,
`c` int(11) DEFAULT NULL,
KEY `a` (`a`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec)
mysql> select * from tkkk;
+------+------+------+
| a | b | c |
+------+------+------+
| 1 | 1 | 1 |
| 2 | 2 | 2 |
| 3 | 3 | 3 |
| 4 | 4 | 4 |
| 5 | 5 | 5 |
| 6 | 6 | 6 |
| 7 | 7 | 7 |
| 8 | 8 | 8 |
| 9 | 9 | 9 |
| 10 | 10 | 10 |
| 11 | 11 | 11 |
| 12 | 12 | 12 |
| 13 | 13 | 13 |
| 15 | 15 | 15 |
| 15 | 16 | 16 |
| 15 | 17 | 17 |
+------+------+------+
16 rows in set (2.21 sec)
mysql> delete from tkkk where a=15;
Query OK, 3 rows affected (6.24 sec)
因為我做了debug索引這里時間看起來很長
對于這樣一個‘Delete’語句來講主庫會利用到索引 KEY a,刪除的三條數據我們實際上只需要一次索引的定位(參考btr_cur_search_to_nth_level函數),然后順序掃描接下來的數據進行刪除就可以了。大概的流程如下圖:
這條數據刪除的三條數據的 before_image 將會記錄到一個 DELETE_ROWS_EVENT 中。從庫應用的時候會重新評估應該使用哪個索引,優先使用主鍵和唯一鍵。對于 Event 中的每條數據都需要進行索引定位操作,并且對于非唯一索引來講第一次返回的第一行數據可能并不是刪除的數據,還需要需要繼續掃描下一行,在函數 Rows_log_event::do_index_scan_and_update 中有如下代碼:
while (record_compare(m_table, &m_cols))//比較每一個字段 如果不相等 掃描下一行
{
while((error= next_record_scan(false)))//掃描下一行
{
/* We just skip records that has already been deleted */
if (error == HA_ERR_RECORD_DELETED)
continue;
DBUG_PRINT("info",("no record matching the given row found"));
goto end;
}
}
這些代價是比主庫更大的。在這個列子中沒有主鍵和唯一鍵,因此依舊使用的是索引KEY a,大概流程如下圖:
但是如果我們在從庫增加一個主鍵,那么在從庫進行應用的時候流程如下:
我們從上面的流程來看,主庫‘Delete’操作和從庫‘Delete’操作主要的區別在于:
從庫每條數據都需要索引定位查找數據。
從庫在某些情況下通過非唯一索引查找的數據第一條數據可能并不是刪除的數據,因此還需要繼續進行索引定位和查找。
對于主庫來講一般只需要一次數據定位查找即可,接下來訪問下一條數據就好了。其實對于真正的刪除操作來講并沒有太多的區別。如果合理的使用了主鍵和唯一鍵可以將上面提到的兩點影響降低。在造成從庫延遲的情況中,沒有合理的使用主鍵和唯一鍵是一個比較重要的原因。
最后如果表上一個索引都沒有的話,那么情況變得更加嚴重,簡單的圖如下:
我們可以看到每一行數據的更改都需要進行全表掃描,這種問題就非常嚴重了。這種情況使用參數‘slave_rows_search_algorithms’的HASH_SCAN選項也許可以提高性能,下面我們就來進行討論。
二、確認查找數據的方式
前面的例子中我們接觸了參數‘slave_rows_search_algorithms’,這個參數主要用于確認如何查找數據。其取值可以是下面幾個組合(來自官方文檔),源碼中體現為一個位圖:
TABLE_SCAN,INDEX_SCAN(默認值)
INDEX_SCAN,HASH_SCAN
TABLE_SCAN,HASH_SCAN
TABLE_SCAN,INDEX_SCAN,HASH_SCAN
在源碼中有如下的說明,當然官方文檔也有類似的說明:
/*
Decision table:
- I --> Index scan / search
- T --> Table scan
- Hi --> Hash over index
- Ht --> Hash over the entire table
|--------------+-----------+------+------+------|
| Index\Option | I , T , H | I, T | I, H | T, H |
|--------------+-----------+------+------+------|
| PK / UK | I | I | I | Hi |
| K | Hi | I | Hi | Hi |
| No Index | Ht | T | Ht | Ht |
|--------------+-----------+------+------+------|
*/
實際上源碼中會有三種數據查找的方式,分別是:
ROW_LOOKUP_INDEX_SCAN
對應函數接口:Rows_log_event::do_index_scan_and_update
ROW_LOOKUP_HASH_SCAN
對應函數接口:Rows_log_event::do_hash_scan_and_update
它又包含:
(1) Hi --> Hash over index
(2) Ht --> Hash over the entire table
后面討論
ROW_LOOKUP_TABLE_SCAN
對應函數接口:Rows_log_event::do_table_scan_and_update
在源碼中如下:
switch (m_rows_lookup_algorithm)//根據不同的算法決定使用哪個方法
{
case ROW_LOOKUP_HASH_SCAN:
do_apply_row_ptr= &Rows_log_event::do_hash_scan_and_update;
break;
case ROW_LOOKUP_INDEX_SCAN:
do_apply_row_ptr= &Rows_log_event::do_index_scan_and_update;
break;
case ROW_LOOKUP_TABLE_SCAN:
do_apply_row_ptr= &Rows_log_event::do_table_scan_and_update;
break;
決定如何查找數據以及通過哪個索引查找正是通過參數‘slave_rows_search_algorithms’的設置和表中是否有合適的索引共同決定的,并不是完全由‘slave_rows_search_algorithms’參數決定。
下面這個圖就是決定的過程,可以參考函數decide_row_lookup_algorithm_and_key(圖24-1,高清原圖包含在文末原圖中)。
三、ROW_LOOKUP_HASH_SCAN方式的數據查找
總的來講這種方式和 ROW_LOOKUP_INDEX_SCAN和ROW_LOOKUP_TABLE_SCAN 都不同,它是通過表中的數據和 Event 中的數據進行比對,而不是通過 Event 中的數據和表中的數據進行比對,下面我們將詳細描述這種方法。
假設我們將參數‘slave_rows_search_algorithms’設置為 INDEX_SCAN,HASH_SCAN ,且表上沒有主鍵和唯一鍵的話,那么上圖的流程將會把數據查找的方式設置為 ROW_LOOKUP_HASH_SCAN。
在 ROW_LOOKUP_HASH_SCAN 又包含兩種數據查找的方式:
Hi --> Hash over index
Ht --> Hash over the entire table
對于 ROW_LOOKUP_HASH_SCAN 來講,其首先會將 Event 中的每一行數據讀取出來存入到 HASH 結構中,如果能夠使用到 Hi 那么還會額外維護一個集合(set),將索引鍵值存入集合,作為索引掃描的依據。如果沒有索引這個集合(set)將不會維護直接使用全表掃描,即Ht。
Ht --> Hash over the entire table 會全表掃描,其中每行都會查詢 hash 結構來比對數據。Hi --> Hash over index 則會通過前面我們說的集合(set)來進行索引定位掃描,每行數據也會去查詢 hash 結構來比對數據。
需要注意一點這個過程的單位是 Event,我們前面說過一個 DELETE_ROWS_EVENT 可能包含了多行數據,Event 最大為 8K 左右。因此使用 Ht --> Hash over the entire table 的方式,將會從原來的每行數據進行一次全表掃描變為每個 Event 才進行一次全表掃描。
但是對于 Hi --> Hash over index 來講效果就沒有那么明顯了,因為如果刪除的數據重復值很少的情況下,依然需要足夠多的索引定位查找才行,但是如果刪除的數據重復值較多那么構造的集合(set)元素將會大大減少,也就減少了索引查找定位的開銷。
考慮另外一種情況,如果我的每條 delete 語句一次只刪除一行數據而不是 delete 一條語句刪除大量的數據,那這種情況每個 DELETE_ROWS_EVENT 只有一條數據存在,那么使用 ROW_LOOKUP_HASH_SCAN 方式并不會提高性能,因為這條數據還是需要進行一次全表掃描或者索引定位才能查找到數據,和默認的方式沒什么區別。
整個過程參考如下接口:
Rows_log_event::do_hash_scan_and_update:總接口,調用下面兩個接口。
Rows_log_event::do_hash_row:將數據加入到hash結構,如果有索引還需要維護集合(set)。
Rows_log_event::do_scan_and_update:查找并且進行刪除操作,會調用Rows_log_event::next_record_scan進行數據查找。
Rows_log_event::next_record_scan:具體的查找方式實現了Hi --> Hash over index和Ht --> Hash over the entire table的查找方式
下面我們還是用最開始的列子,我們刪除了三條數據,因此 DELETE_ROW_EVENT 中包含了三條數據。假設我們參數‘slave_rows_search_algorithms’設置為 INDEX_SCAN,HASH_SCAN。因為我的表中沒有主鍵和唯一鍵,因此會最終使用 ROW_LOOKUP_HASH_SCAN 進行數據查找。但是因為我們有一個索引 key a,因此會使用到 Hi --> Hash over index。為了更好的描述 Hi 和 Ht 兩種方式,我們也假定另一種情況是表上一個索引都沒有,我將兩種方式放到一個圖中方便大家發現不同點,如下圖(圖24-2,高清原圖包含在文末原圖中):
四、總結
我記得以前有位朋友問我主庫沒有主鍵如果我在從庫建立一個主鍵能降低延遲嗎?這里我們就清楚了答案是肯定的,因為從庫會根據 Event 中的行數據進行使用索引的選擇。那么總結一下:
slave_rows_search_algorithms 參數設置了 HASH_SCAN 并不一定會提高性能,只有滿足如下兩個條件才會提高性能:
(1)(表中沒有任何索引)或者(有索引且本條 update/delete 的數據關鍵字重復值較多)。
(2) 一個 update/delete 語句刪除了大量的數據,形成了很多個 8K 左右的 UPDATE_ROW_EVENT/DELETE_ROW_EVENT。update/delete 語句只修改少量的數據(比如每個語句修改一行數據)并不能提高性能。
從庫索引的利用是自行判斷的,順序為主鍵->唯一鍵->普通索引。
如果 slave_rows_search_algorithms 參數沒有設置 HASH_SCAN ,并且沒有主鍵/唯一鍵那么性能將會急劇下降造成延遲。如果連索引都沒有那么這個情況更加嚴重,因為更改的每一行數據都會引發一次全表掃描。
因此我們發現在 MySQL 中強制設置主鍵又多了一個理由。
第24節結束
總結
以上是生活随笔為你收集整理的分布式从mysql查数据_技术分享 | 从库数据的查找和参数 slave_rows_search_algorithms...的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: mysql 取消主从复制_MySQL:第
- 下一篇: tomcat连接不上本地mysql_my