作者:楊濤濤來源於客戶的問題:數據庫校驗的方法有哪些,能否給他說說?
我:直接 percona toolkit 不就行了。
客戶:服務器裝任何軟件都得評審,評審一次多長時間不定。而且不準用開源軟件。
我:...好了,來看下 MySQL 自身怎麼對數據進行校驗。
1.checksum table.
checksum table 會對錶一行一行進行計算,直到計算出最終的 checksum 結果。
比如對錶 n4 進行校驗(記錄數 157W,大小爲 4G)
[ytt]>desc n4;
+-------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+--------------+------+-----+---------+-------+
| id | int(11) | YES | | NULL | |
| r1 | char(36) | YES | | NULL | |
| r2 | varchar(100) | YES | | NULL | |
| r3 | datetime | YES | | NULL | |
| r4 | text | YES | | NULL | |
+-------+--------------+------+-----+---------+-------+
5 rows in set (0.00 sec)
[ytt]>select count(*) from n4;
+----------+
| count(*) |
+----------+
| 1572864 |
+----------+
1 row in set (6.89 sec)
[ytt]>checksum table n4;
+--------+-----------+
| Table | Checksum |
+--------+-----------+
| ytt.n4 | 874125175 |
+--------+-----------+
1 row in set (8.24 sec)
我自己筆記本上的測試結果,速度挺快。
不過checksum的限制比較多。羅列如下,
A、不能對視圖進行校驗。
[ytt]>checksum table v_n3;
+----------+----------+
| Table | Checksum |
+----------+----------+
| ytt.v_n3 | NULL |
+----------+----------+
1 row in set, 1 warning (0.00 sec)
[ytt]>show warnings;
+-------+------+------------------------------+
| Level | Code | Message |
+-------+------+------------------------------+
| Error | 1347 | 'ytt.v_n3' is not BASE TABLE |
+-------+------+------------------------------+
1 row in set (0.00 sec)
B、字段順序不同,校驗結果也會不一致。
[ytt]>desc n3;
+-------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+---------+------+-----+---------+-------+
| id | int(11) | NO | | NULL | |
| r1 | int(11) | YES | | NULL | |
+-------+---------+------+-----+---------+-------+
2 rows in set (0.00 sec)
[ytt]>desc n5;
+-------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+---------+------+-----+---------+-------+
| r1 | int(11) | YES | | NULL | |
| id | int(11) | NO | | NULL | |
+-------+---------+------+-----+---------+-------+
2 rows in set (0.00 sec)
[ytt]>checksum table n3,n5;
+--------+------------+
| Table | Checksum |
+--------+------------+
| ytt.n3 | 1795175396 |
| ytt.n5 | 838415794 |
+--------+------------+
2 rows in set (0.00 sec)
C、CHAR(100) 和 VARCHAR(100) 存儲相同的字符,校驗結果也會不一致。
[ytt]>desc n6;
+-------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+--------------+------+-----+---------+-------+
| id | int(11) | NO | | NULL | |
| r1 | int(11) | YES | | NULL | |
| s1 | varchar(100) | YES | | NULL | |
+-------+--------------+------+-----+---------+-------+
3 rows in set (0.00 sec)
[ytt]>desc n3;
+-------+-----------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+-----------+------+-----+---------+-------+
| id | int(11) | NO | | NULL | |
| r1 | int(11) | YES | | NULL | |
| s1 | char(100) | YES | | NULL | |
+-------+-----------+------+-----+---------+-------+
3 rows in set (0.00 sec)
[ytt]>select * from n6;
Empty set (0.00 sec)
[ytt]>insert into n6 select * from n3;
Query OK, 8 rows affected (0.01 sec)
Records: 8 Duplicates: 0 Warnings: 0
[ytt]>checksum table n3,n6;
+--------+------------+
| Table | Checksum |
+--------+------------+
| ytt.n3 | 2202684200 |
| ytt.n6 | 455222236 |
+--------+------------+
2 rows in set (0.00 sec)
D、在執行 checksum 同時,會對錶所有行加共享讀鎖。
E、還有就是 MySQL 版本不同,有可能校驗結果不一致。比如手冊上說的, MySQL 5.6.5 之後的版本對時間類型的存儲格式有變化,導致校驗結果不一致。
那 checksum 的 限制這麼多,我們是不是有其方法來突破所有限制呢? 比如說可以模擬 checksum table 的原理來手工計算。
2.自己計算 checksum 值。
這裏用了 MySQL 自身的幾個特性:session 變量;通用表達式;窗口函數以及 MySQL 的 concat_ws 函數。實現非常簡單。
比如我們用 sha 函數來計算校驗值。
[ytt]>set @crc='';
Query OK, 0 rows affected (0.00 sec)
[ytt]>
[ytt]>with ytt (r,rn) as
-> (
-> select @crc:= sha(concat_ws('#',@crc,id,r1,r2,r3,r4)) as r, row_number() over() as rn
-> from n4
-> )
-> select 'n4' tablename, r checksum from ytt where rn = 1572864 ;
+-----------+------------------------------------------+
| tablename | checksum |
+-----------+------------------------------------------+
| n4 | a9711af93399e0d195a53f4148adea46ab684d30 |
+-----------+------------------------------------------+
1 row in set, 1 warning (16.46 sec)
如果在 MySQL 老版本運行,可以利用 MySQL 的黑洞引擎,改下 SQL 如下:
[ytt]>create table tmp_checksum (checksum varchar(100)) engine blackhole;
Query OK, 0 rows affected (0.08 sec)
[ytt]>
[ytt]>set @crc='';insert into tmp_checksum
Query OK, 0 rows affected (0.00 sec)
-> select @crc:= sha(concat_ws('#',@crc,id,r1,r2,r3,r4)) as r from n4;
Query OK, 1572864 rows affected, 1 warning (20.11 sec)
Records: 1572864 Duplicates: 0 Warnings: 1
[ytt]>select 'n4' tablename,@crc checksum;
+-----------+------------------------------------------+
| tablename | checksum |
+-----------+------------------------------------------+
| n4 | a9711af93399e0d195a53f4148adea46ab684d30 |
+-----------+------------------------------------------+
1 row in set (0.00 sec)
總結
對於表要計算校驗數據一致性的需求,首選第二種自己寫 SQL 的方法。