在 MySQL 運維中,研發同事想對比下兩個不同例項上的資料並找出差異,除主鍵外還需要對比每一個欄位,如何做呢?
第一種方案,寫程式將兩個例項上的每一行資料取出來進行對比,理論可行,但是對比時間較長。
第二種方案,對每一行資料所有欄位合併起來,取 checksum 值,再按照 checksum 值對比,看著可行,嘗試下。
首先要合併所有欄位的值,選用 MySQL 提供的 CONCAT 函式,如果 CONCAT 函式中包含 NULL 值,會導致最終結果為 NULL,因此需要使用 IFNULL 函式來替換 NULL 值,如:
CONCAT(IFNULL(C1,”),IFNULL(C2,”))
加入表有很多行,手動拼個指令碼比較累,別急,可以使用 information_schema.COLUMNS 來處理:
## 獲取列名的拼接串
SELECT
GROUP_CONCAT(‘IFNULL(‘,COLUMN_NAME,’,””)’)
FROM information_schema.COLUMNS
WHERE TABLE_NAME=’table_name’;
假設我們有測試表:
CREATE TABLE t_test01
(
id INT AUTO_INCREMENT PRIMARY KEY,
C1 INT,
C2 INT
)
我們便可以拼接出下面的 SQL:
SELECT
id,
MD5(CONCAT(
IFNULL(id,”),
IFNULL(c1,”),
IFNULL(c2,”),
)) AS md5_value
FROM t_test01
在兩個例項上執行下,然後把結果使用 beyond compare 對比下,就很容易找出不相同的行以及主鍵 ID
對於資料量較大的表,執行出來的結果集也很大,對比起來比較費勁,那就先嚐試縮小結果集,可以將多行記錄的 md5 值合併起來求 MD5 值,如果最後 MD5 值相同,則這些行相同,如果不同,則證明存在差異,再按照這些行進行逐行對比。
假設我們按照 1000 行一組來進行對比,如果需要將分組後的結果合併,需要使用 GROUP_CONCAT 函式,注意在 GROUP_CONCAT 函式中新增排序保證合併資料的順序, SQL 如下:
SELECT
min(id) as min_id,
max(id) as max_id,
count(1) as row_count,
MD5(GROUP_CONCAT(
MD5(CONCAT(
IFNULL(id,”),
IFNULL(c1,”),
IFNULL(c2,”),
)) ORDER BY id
))AS md5_value
FROM t_test01
GROUP BY (id div 1000)
執行結果為:
min_id max_id row_count md5_value
0 999 1000 7d49def23611f610849ef559677fec0c
1000 1999 1000 95d61931aa5d3b48f1e38b3550daee08
2000 2999 1000 b02612548fae8a4455418365b3ae611a
3000 3999 1000 fe798602ab9dd1c69b36a0da568b6dbb
當差異資料較少時,即使需要對比上千萬資料,我們可以輕鬆根據根據 min_id 和 max_id 來快速定位到哪 1000 條資料裡存在差異,再進行逐行 MD5 值對比,最終找到差異行。
最終對比圖:
##=====================================================================##
PS:
在使用 GROUP_CONCAT 時,需要配置 MySQL 變數 group_concat_max_len,預設值為 1024,超出部分會被階段。