Percona-Toolkit 之 pt-table-checksum 總結

来源:https://www.cnblogs.com/dbabd/archive/2019/04/04/10653408.html
-Advertisement-
Play Games

pt table checksum Verify MySQL replication integrity. pt table checksum performs an online replication consistency check by executing checksum queries ...


pt-table-checksum - Verify MySQL replication integrity.

pt-table-checksum performs an online replication consistency check by executing checksum queries on the master, which produces different results on replicas that are inconsistent with the master.

pt-table-checksum是Percona-Toolkit工具集中的一個組件,是一個檢查主從複製資料庫數據一致性的工具,通過在主庫上執行檢查語句可以線上檢查MySQL主從資料庫數據的一致性。

pt-table-checksum通過指定選項連接指定伺服器實例,可以通過指定條件過濾到庫級別和表級別。每次檢查一張表,所以並不需要大量的記憶體和多餘的操作,即使是非常大的表也可以良好運行。根據官方文檔的描述,之所以在檢查大表上也能很好運行,主要是因為pt-table-checksum將表劃分多個chunk,根據checksum query運行時間來調整chunk的大小,這樣就減少pt-table-checksum的運行對主從複製延遲和主機負載的影響。為了保證對資料庫主機運行儘可能少的影響,pt-table-checksum會自動探測從庫併進行連接,如果連接失敗,可以通過選項--recursion-method指定從庫連接方式。

本文是關於之前有關pt-table-checksum工具使用的學習筆記進行重新整理,使用最新版本的工具同時也進行原理知識的梳理。

關於獲取和安裝Percona-Toolkit工具集可以參考我另一篇博文:Percona-Toolkit 之 pt-online-schema-change 總結中的安裝部分。

基本說明

pt-table-checksum [OPTIONS] DSN

常用選項(OPTIONS)

--ask-pass
命令行提示密碼輸入,保護密碼安全,前提需安裝模塊perl-TermReadKey。

--[no]check-binlog-format
預設值:yes
指定檢查所有伺服器上的binlog_format系統參數是否相同。

--check-interval
預設值:1s
指定因為選項'--max-lag'檢查之間休眠時間。

--[no]check-replication-filters
預設值:yes
指定檢測主從複製是否有設置複製過濾器。預設如果有設置複製過濾器,則工具不進行檢查校驗操作。

--check-slave-lag
指定主從複製延遲大於選項'--max-lag'指定的值之後暫停檢查校驗操作。預設情況下,工具會檢查所有的從庫,但該選項只作用於指定的從庫(通過DSN連接方式)。

--chunk-index
指定使用哪個索引對錶進行chunk分塊操作。預設情況下會選擇最優的索引,工具會在SQL語句中添加FORCE INDEX子句。

--chunk-index-columns
指定使用選項'--chunk-index'的索引使用最左首碼幾個索引欄位,只適用於複合索引。

--chunk-size
預設值:1000
指定表分塊的chunk大小,每個chunk需要校驗的表行數,允許的尾碼單位為k、M、G。
當指定了這個選項會覆蓋工具預設動態調整chunk塊大小以便在選項'--chunk-time'指定時間內完成行的校驗。所以在大多數情況下不建議使用該選項,應該使用選項'--chunk-time'來調整。

--chunk-size-limit
預設值:2.0
指定chunk的行數最多可以超過選項'--chunk-size'指定的行數的多少倍。最小值是1,表示chunk的行數不能超過選項'--chunk-size'指定的值。由於行數是通過explain估算的,所以不建議指定為1。當參數值為0時,則不會檢查是否超過指定的行數。

--chunk-time
預設值:0.5
動態調整每個chunk的大小使相應的表行數都在指定的時間內完成校驗操作。
如果該選項值設置為0,則不會動態調整chunk的大小,就有可能造成每次校驗操作的時間不同,但每個chunk大小還是一致的。

--columns,-c
指定只需要校驗的欄位,如有多個則用','(逗號)隔開。該選項一般只針對檢驗一張表時有效,除非有多張表具有相同的欄位。

--[no]create-replicate-table
預設值:yes
創建選項'--replicate'指定的資料庫和表。表結構與選項'--replicate'指定的結構相同。

--replicate
預設值:percona.checksums
指定保存校驗結果的表。創建表的結構如下:
'
CREATE TABLE checksums (
   db             CHAR(64)     NOT NULL,
   tbl            CHAR(64)     NOT NULL,
   chunk          INT          NOT NULL,
   chunk_time     FLOAT            NULL,
   chunk_index    VARCHAR(200)     NULL,
   lower_boundary TEXT             NULL,
   upper_boundary TEXT             NULL,
   this_crc       CHAR(40)     NOT NULL,
   this_cnt       INT          NOT NULL,
   master_crc     CHAR(40)         NULL,
   master_cnt     INT              NULL,
   ts             TIMESTAMP    NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
   PRIMARY KEY (db, tbl, chunk),
   INDEX ts_db_tbl (ts, db, tbl)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
'
因為選項'--[no]create-replicate-table'預設值為true,所以預設情況下如果資料庫和表之前不存在則會自動創建percona庫和checksums表。除非另有選項指定。選項'--replicate'指定的表不會進行校驗操作,指定的表預設加入選項'--ignore-tables'。

--[no]replicate-check
預設值:yes
指定在校驗完每張表後檢查主從當前表是否出現不一致。工具通過連接到從庫執行簡單的SELECT語句查詢校驗結果與主庫的校驗結果進行差異對比,結果顯示在輸出中的DIFF欄位中。

--replicate-check-only
指定僅僅執行檢查主從數據是否一致而不進執行真正的校驗操作(主要通過查詢之前保留的校驗結果)。該選項只適用於同時指定選項'--no-replicate-check'。

--replicate-check-retries
預設值:1
指定當校驗出主從數據不一致重試校驗的次數。

--replicate-database
指定工具在執行校驗操作時在哪個資料庫下進行,相當於執行了語句USE [DB_NAME]。

--resume
指定從最後完成校驗的chunk開始恢覆校驗。適用於還未完成所有表的校驗就工具就中斷的情況。

--retries
預設值:2
指定當出現非嚴重性錯誤時重覆校驗一個塊的次數。非嚴重性錯誤指的是如鎖等待超時或長查詢被kill的情況。

--run-time
指定校驗操作運行的時間。預設情況需要校驗完所有的表數據後停止,可以指定時間單位為:s(秒)、m(分鐘)、h(小時)、d(天)。

--skip-check-slave-lag
DSN類型,可重覆使用
指定DSN連接從庫時跳過主從延遲檢查,可以指定多個從庫檢查。

--set-vars
預設:
    wait_timeout=10000
    innodb_lock_wait_timeout=1
    lock_wait_timeout=60
運行檢查時指定參數值,如有多個用','(逗號)分隔。如'--set-vars=wait_timeout=5000'。

--[no]empty-replicate-table
預設值:yes
指定進行當前校驗之前刪除之前每張表的校驗記錄。該選項並不是對保存校驗結果的表進行truncate,而是在校驗每張表之前刪除當前表之前的校驗結果,因此當校驗操作過早停止,則有可能還有表沒有校驗數據,如果是從之前校驗操作恢復,也不會清空保存校驗結果的表。如果想清空保存校驗結果的表,則在校驗操作進行之前手動對錶執行truncate操作。

--databases,-d
指定只需要校驗的資料庫,如有多個則用','(逗號)隔開。

--engines,-e
指定只需要校驗的指定存儲引擎類型的表。

--explain
指定顯示校驗查詢語句,但不執行真正的校驗操作。該選項會禁用選項'--[no]empty-replicate-table',如果指定兩次,則工具實際使用的是迭代的chunk演算法,列印出每個塊的上邊界和下邊界值,但不執行真正的校驗。

--fail-on-stopped-replication
指定當主從複製停止時,校驗中止操作並提示錯誤而不是等待主從複製恢復之後再進行。

--function
預設值:CRC32
指定校驗操作使用的哈希函數。可選函數有SHA1、MD5等。

--ignore-columns
指定需要忽略校驗的欄位,如有多個則用','(逗號)隔開。

--ignore-databases
指定需要忽略校驗的資料庫,如有多個則用','(逗號)隔開。

--ignore-databases-regex
指定採用正則表達式匹配忽略校驗的資料庫。

--ignore-engines
預設值:FEDERATED,MRG_MyISAM
指定需要忽略校驗的存儲引擎類型的表,如有多個則用','(逗號)隔開。

--ignore-tables
指定需要忽略校驗的表,如有多個則用','(逗號)隔開。表名稱可以使用資料庫名加以限定。

--ignore-tables-regex
指定採用正則表達式匹配忽略校驗的表。

--max-lag
預設值:1s
指定允許主從複製延遲時長的最大值,單位秒。如果在每次校驗查詢之後主從延遲超過指定的值,則校驗操作將暫停執行,暫停休眠時間為選項'--check-interval'指定的值。待休眠時間結束之後再次檢查主從延遲時長,檢查方法是通過從庫查詢的'Seconds_Behind_Master'值來確定。如果主從複製延遲一直大於該參數指定值或者從庫停止複製,則操作將一直等待直到從庫重新啟動並且延遲小於該參數指定值。

--max-load
數組類型,預設值:Threads_running = 25
在校驗要詢完每個chunk數據之後,運行SHOW GLOBAL STATUS檢查所指定變數值高於該參數指定變數的閾值時將暫停校驗操作。如果有多個變數閾值,可以用','(逗號)進行分隔,參數指定形式可以為變數名=MAX_VALUE或變數名:MAX_VALUE。
如果只是指定變數名,沒有為其指定閾值,則檢查當前值並增加20%作為閾值。如:

    --max-load=Threads_running:沒有指定具體值,以當前查詢值增加20%作為閾值,如當前為100,閾值為120;
    --max-load=Threads_running:10:以當前指定值為閾值。

--host,-h
指定連接的資料庫IP地址。

--port,-P
指定連接的資料庫Port埠。

--user,-u
指定連接的資料庫用戶。

--password,-p
指定連接的資料庫用戶密碼。

--database,-d
指定連接的資料庫。

--socket,-S
指定使用SOCKET文件連接。

--progress
列印工具執行過程的進度提示到STDERR。選項值有兩部分組成,用逗號進行分隔,第一部分為百分比,時間和迭代。第二部分為根據第一部分數據更新頻率,也分為百分比,時間和迭代。

--quiet,-q
不列印工具執行過程的信息到STDOUT(禁用'--progress')。但錯誤和警告還是列印到STDERR。

--recurse
指定搜尋從庫的層級,預設無限級。

--recursion-method
預設值:processlist,hosts
指定獲取從庫的方式。pt-table-checksum在執行校驗操作時會執行多次REPLICA CHECKS操作。
METHOD       USES
===========  =============================================
processlist  SHOW PROCESSLIST   
hosts        SHOW SLAVE HOSTS   
cluster      SHOW STATUS LIKE 'wsrep\_incoming\_addresses'
dsn=DSN      DSNs from a table
none         Do not find slaves
==========================================================
processlist:通過SHOW PROCESSLIST方式找到slave,為預設方式,當SHOW SLAVE HOSTS不可用時。一旦實例運行在非3306埠上時,hosts方式就會變為預設方式;
hosts:通過SHOW SLAVE HOSTS方式找到slave,hosts方式要求從庫配置'--report_host'和'--report_port'這兩個參數;
cluster:基於集群版本Galera 23.7.3及更新版本;
dsn:通過讀取表中從庫的DSN信息進行連接。

--tables,-t
指定只需要校驗的表,如有多個則用','(逗號)隔開。表名稱可以使用資料庫名加以限定。

--tables-regex
指定採用正則表達式匹配校驗的表。

--where
指定通過where條件確定表中需要校驗的數據。

--truncate-replicate-table
指定在執行校驗操作之前對保存校驗結果的表執行truncate操作。註意與選項'--[no]empty-replicate-table'的區分。

--version
顯示工具的版本並退出。

--[no]version-check
預設值:yes
檢查Percona Toolkit、MySQL和其他程式的最新版本。

DSN選項(DSN)

可以使用DSN方式來連接資料庫,DSN選項為key=value方式,在等號的兩側不能有空格出現,並且區分大小寫,多個選項之前以','(逗號)隔開,主要選項如下:

  • A
    指定字元集
  • D
    指定DSN表所在資料庫
  • t
    指定DSN表
  • h
    指定要連接的HOST
  • P
    指定要連接的PORT
  • S
    指定連接所使用的SOCKET文件(Unix systems)
  • u
    指定連接的用戶名
  • p
    指定連接的用戶名密碼

示例:
h=192.168.58.3,P=3306,D=employees,t=employees

從庫檢查(REPLICA CHECKS)

預設情況,pt-table-checksum工具會對所有連接到的從庫進行檢查,從庫連接策略由選項--recursion-method--recurse控制,檢查項目如下:

  • --[no]check-replication-filters

工具檢查每個從庫上是否有存在主從複製過濾器,如果發現有複製過濾器,則校驗操作會中止,工具會退出。

  • --replicate指定的表

工具檢查每個從庫上是否都存在選項--replicate指定保存校驗結果的表。如果有從庫沒有這張表時,校驗操作有可能導致主從複製中斷。這項檢查無法被關閉,如果從庫不存在表,則工具會一直等待直到從庫存在該表。

  • 單個chunk大小

工具檢查主庫上表的是否可以在單個chunk範圍內進行校驗。具體說明可以參考官方說明:REPLICA CHECKS

  • 主從複製延遲

工具在每次校驗完一個chunk之後都會檢查每個從庫是否有延遲,或者通過選項--check-slave-lag指定需要檢查的從庫。

  • 校驗塊

工具在主庫校驗完每張表之後都會等待每個從庫最後一個校驗塊的完成,再執行選項--[no]replicate-check指定的操作。

使用限制

  1. pt-table-checksum工具要求主從複製是基於STATEMENT格式進行的複製,並且會在主庫執行binlog_format=STATEMENT語句。由於MySQL的限制,這一設置並不會傳遞到從庫進行。因此當從庫的binlog格式是ROW時,無法直接對從庫的從庫進行校驗操作。工具在進行校驗操作時都會檢查所有從庫的binlog_format,可以使用選項--[no]check-binlog-format進行控制;
  2. 工具假設主從資料庫中SCHEMA和表結構都是一致的。如果從庫上不存在主庫的SCHEMA,或者是從庫上表結構與主庫不一致,則有可能導致主從複製中斷。

用法示例

環境與數據準備

MySQL:5.7.24
MASTER:192.168.58.3:3306
SLAVE:192.168.58.5:3306

本文基於MySQL官方示例資料庫employeeExample Databases進行測試。

創建測試表employees_ptchksum:

-- 表employees_ptchksum信息
mysql [email protected]:employees> create table employees_ptchksum as select * from employees;
Query OK, 300024 rows affected
Time: 2.688s

mysql [email protected]:employees> show create table employees_ptchksum\G;
***************************[ 1. row ]***************************
Table        | employees_ptchksum
Create Table | CREATE TABLE `employees_ptchksum` (
  `emp_no` int(11) NOT NULL,
  `birth_date` date NOT NULL,
  `first_name` varchar(14) NOT NULL,
  `last_name` varchar(16) NOT NULL,
  `gender` enum('M','F') NOT NULL,
  `hire_date` date NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set
Time: 0.007s

-- 從庫更新部分數據,使得主從出現數據不一致
mysql [email protected]:employees> set sql_log_bin = 0;
Query OK, 0 rows affected
Time: 0.002s

mysql [email protected]:employees> update employees_ptchksum set first_name = 'Georgi_ptchk' where first_name = 'Georgi';
Query OK, 252 rows affected
Time: 0.235s

mysql [email protected]:employees> set sql_log_bin = 1;
Query OK, 0 rows affected
Time: 0.002s

執行一致性校驗

在主庫上執行:

# pt-table-checksum h=192.168.58.3,P=3306,u=admin --ask-pass --tables=employees_ptchksum

執行以上命令會報如下錯誤:

Replica dbabd has binlog_format ROW which could cause pt-table-checksum to break replication.  Please read "Replicas using row-based replication" in the LIMITATIONS section of the tool's documentation.  If you understand the risks, specify --no-check-binlog-format to disable this check.

根據錯誤信息需指定選項--no-check-binlog-format,加上選項再次執行:

# pt-table-checksum h=192.168.58.3,P=3306,u=admin --ask-pass --tables=employees_ptchksum --no-check-binlog-format

執行以上命令報如下錯誤:

04-02T15:45:40 Cannot checksum table employees.employees_ptchksum: There is no good index and the table is oversized. at /usr/bin/pt-table-checksum line 6726.

根據錯誤信息提示表employees_ptchksum沒有合適的索引進行chunk操作,為表添加主鍵後再次執行校驗:

-- 添加主鍵
mysql [email protected]:employees> alter table employees_ptchksum add primary key(emp_no);
Query OK, 0 rows affected
Time: 2.271s

-- 再次執行校驗
# pt-table-checksum h=192.168.58.3,P=3306,u=admin --ask-pass --tables=employees.employees_ptchksum --no-check-binlog-format
Enter MySQL password:

Checking if all tables can be checksummed ...
Starting checksum ...
            TS ERRORS  DIFFS     ROWS  DIFF_ROWS  CHUNKS SKIPPED    TIME TABLE
04-02T15:56:21      0      3   300024          0       5       0   0.845 employees.employees_ptchksum

輸出欄位解析

  • TS:校驗完成的時間戳(沒有年份顯示);
  • ERRORS: 校驗報錯的數量;
  • DIFFS:主從之間chunk不同的數量,如果指定--no-replicate-check,則該值總是為0,如果指定--replicate-check-only,則只有校驗結果不同的表會顯示;
  • ROWS:選擇表校驗的行數;
  • CHUNKS:表被分成的chunk數;
  • SKIPPED:跳過的chunk數,主要有以下原因:
    • MySQL not using the --chunk-index
    • MySQL not using the full chunk index (--[no]check-plan)
    • Chunk size is greater than --chunk-size * --chunk-size-limit
    • Lock wait timeout exceeded (--retries)
    • Checksum query killed (--retries)
  • TIME:校驗執行消耗時間(單位:秒)
  • TABLE:校驗的表名

使用選項--replicate-check-only輸出欄位如下:

Checking if all tables can be checksummed ...
Starting checksum ...
Differences on dbabd
TABLE                        CHUNK CNT_DIFF CRC_DIFF CHUNK_INDEX LOWER_BOUNDARY UPPER_BOUNDARY
employees.employees_ptchksum 1     0        1        PRIMARY     10001          11000
employees.employees_ptchksum 2     0        1        PRIMARY     11001          35858
employees.employees_ptchksum 3     0        1        PRIMARY     35859          491954
employees.employees_ptchksum 4     0        1        PRIMARY     491955         499999
  • TABLE:與主庫數據不一致的表;
  • CHUNK:與主庫相應表不同的chunk;
  • CNT_DIFF:從庫上chunk行數減去主庫上chunk行數值;
  • CRC_DIFF:如果從庫上對應的chunk與主庫上不同,則為1,否則為0;
  • CHUNK_INDEX:表使用哪個索引用來進行chunk;
  • LOWER_BOUNDARY:chunk下邊界對應的索引值;
  • UPPER_BOUNDARY:chunk上邊界對應的索引值。

獲取校驗結果

在從庫上執行:

mysql [email protected]:employees> SELECT db, tbl, SUM(this_cnt) AS total_rows, COUNT(*) AS chunks
                                    FROM percona.checksums
                                    WHERE (
                                     master_cnt <> this_cnt
                                     OR master_crc <> this_crc
                                     OR ISNULL(master_crc) <> ISNULL(this_crc))
                                    GROUP BY db, tbl;
+-----------+--------------------+------------+--------+
| db        | tbl                | total_rows | chunks |
+-----------+--------------------+------------+--------+
| employees | employees_ptchksum | 300024     | 4      |
+-----------+--------------------+------------+--------+
1 row in set
Time: 0.021s

工具其他選項

  • 主庫為標準埠(3306),從庫為非標準埠(非3306)

修改從庫埠為3307:

mysql [email protected]:employees> show global variables like 'port';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| port          | 3307  |
+---------------+-------+
1 row in set
Time: 0.027s

執行一致性校驗,會報出如下錯誤:

# pt-table-checksum h=192.168.58.3,P=3306,u=admin --ask-pass --databases=employees --tables=employees_ptchksum --no-check-binlog-format
Enter MySQL password:

Checking if all tables can be checksummed ...
Starting checksum ...
Cannot connect to P=3306,h=192.168.58.5,p=...,u=admin
Diffs cannot be detected because no slaves were found.  Please read the --recursion-method documentation for information.

根據報錯信息看出,工具找不到從庫,因為現在從庫運行在3307埠上,而工具預設連接的是3306埠,所以需要指定選項--recursion-method,關於選項的使用參考前面說明。

因為主庫運行在標準埠,而從庫在非標準埠,所以選項--recursion-method值只能為hostsdsn,以下分兩種情況討論:

-- 指定--recursion-method=hosts,從庫必須配置參數report_host和report_port
mysql [email protected]:employees> show global variables like '%report%';
+-----------------+--------------+
| Variable_name   | Value        |
+-----------------+--------------+
| report_host     | 192.168.58.5 |
| report_password |              |
| report_port     | 3307         |
| report_user     |              |
+-----------------+--------------+
4 rows in set
Time: 0.035s

-- 執行一致性校驗
# pt-table-checksum h=192.168.58.3,P=3306,u=admin --ask-pass --databases=employees --tables=employees_ptchksum --no-check-binlog-format --recursion-method hosts

------------------------------------------------------------------

-- 指定--recursion-method=dsn,主庫必須先創建dsns表,表結構如下
mysql [email protected]:percona> show create table dsns;
+-------+-------------------------------------------------------+
| Table | Create Table                                          |
+-------+-------------------------------------------------------+
| dsns  | CREATE TABLE `dsns` (                                 |
|       |   `id` int(11) NOT NULL AUTO_INCREMENT,               |
|       |   `parent_id` int(11) DEFAULT NULL,                   |
|       |   `dsn` varchar(255) NOT NULL,                        |
|       |   PRIMARY KEY (`id`)                                  |
|       | ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 |
+-------+-------------------------------------------------------+
1 row in set
Time: 0.037s

-- 表中插入從庫dsn信息
mysql [email protected]:percona> select * from dsns;
+----+-----------+-----------------------+
| id | parent_id | dsn                   |
+----+-----------+-----------------------+
| 1  | 1         | h=192.168.58.5,P=3307 |
+----+-----------+-----------------------+
1 row in set
Time: 0.022s

-- 執行一致性校驗
# pt-table-checksum h=192.168.58.3,P=3306,u=admin --ask-pass --databases=employees --tables=employees_ptchksum --no-check-binlog-format --recursion-method dsn=D=percona,t=dsns
  • 主庫和從庫都為非標準埠(非3306)

修改主庫埠為3307:

mysql [email protected]:employees> show global variables like 'port';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| port          | 3307  |
+---------------+-------+
1 row in set
Time: 0.021s

同樣,如果指定選項--recursion-method hosts,則從庫必須配置參數report_host和report_port,這裡不再贅述。或者選項--recursion-method值為processlistdsn

-- 指定--recursion-method processlist
# pt-table-checksum h=192.168.58.3,P=3307,u=admin --ask-pass --databases=employees --tables=employees_ptchksum --no-check-binlog-format --recursion-method processlist

-- 指定--recursion-method=dsn
# pt-table-checksum h=192.168.58.3,P=3307,u=admin --ask-pass --databases=employees --tables=employees_ptchksum --no-check-binlog-format --recursion-method dsn=D=percona,t=dsns

關於主從埠的總結(選項--recursion-method值的設置):

  1. 對於所有類型的埠設置,dsn方式都可以使用,最為方便,也便於進行管理;
  2. 如果要使用hosts方式,則從庫必須配置參數report_hostreport_port
  3. 主庫如果是標準埠(3306),則只能使用hostsdsn方式;
  4. 主庫如果是非標準埠(非3306),則根據從庫是否配置參數report_hostreport_port來決定:
    有配置可選擇hosts方式;
    沒有配置則只能選擇processlistdsn方式。
  • 執行整個資料庫校驗
# pt-table-checksum h=192.168.58.3,P=3306,u=admin --ask-pass --databases=employees --no-check-binlog-format --recursion-method dsn=D=percona,t=dsns
  • 執行表某個欄位校驗

這裡以employees_ptchksum表的first_name欄位為例。

# pt-table-checksum h=192.168.58.3,P=3306,u=admin --ask-pass --databases=employees --tables=employees_ptchksum --columns=first_name --no-check-binlog-format --recursion-method dsn=D=percona,t=dsns
  • 只列印校驗查詢語句

指定選項--explain

# pt-table-checksum h=192.168.58.3,P=3306,u=admin --ask-pass --databases=employees --tables=employees_ptchksum --no-check-binlog-format --recursion-method dsn=D=percona,t=dsns --explain
Enter MySQL password:

Checking if all tables can be checksummed ...
Starting checksum ...
--
-- employees.employees_ptchksum
--

REPLACE INTO `percona`.`checksums` (db, tbl, chunk, chunk_index, lower_boundary, upper_boundary, this_cnt, this_crc) SELECT ?, ?, ?, ?, ?, ?, COUNT(*) AS cnt, COALESCE(LOWER(CONV(BIT_XOR(CAST(CRC32(CONCAT_WS('#', `emp_no`, `birth_date`, convert(`first_name` using utf8mb4), convert(`last_name` using utf8mb4), convert(`gender` using utf8mb4), `hire_date`)) AS UNSIGNED)), 10, 16)), 0) AS crc FROM `employees`.`employees_ptchksum` FORCE INDEX(`PRIMARY`) WHERE ((`emp_no` >= ?)) AND ((`emp_no` <= ?)) /*checksum chunk*/

REPLACE INTO `percona`.`checksums` (db, tbl, chunk, chunk_index, lower_boundary, upper_boundary, this_cnt, this_crc) SELECT ?, ?, ?, ?, ?, ?, COUNT(*), '0' FROM `employees`.`employees_ptchksum` FORCE INDEX(`PRIMARY`) WHERE ((`emp_no` < ?)) ORDER BY `emp_no` /*past lower chunk*/

REPLACE INTO `percona`.`checksums` (db, tbl, chunk, chunk_index, lower_boundary, upper_boundary, this_cnt, this_crc) SELECT ?, ?, ?, ?, ?, ?, COUNT(*), '0' FROM `employees`.`employees_ptchksum` FORCE INDEX(`PRIMARY`) WHERE ((`emp_no` > ?)) ORDER BY `emp_no` /*past upper chunk*/

SELECT /*!40001 SQL_NO_CACHE */ `emp_no` FROM `employees`.`employees_ptchksum` FORCE INDEX(`PRIMARY`) WHERE ((`emp_no` >= ?)) ORDER BY `emp_no` LIMIT ?, 2 /*next chunk boundary*/
  • 指定校驗分塊(chunk)大小

會覆蓋工具動態調整chunk大小的行為。

# pt-table-checksum h=192.168.58.3,P=3306,u=admin --ask-pass --databases=employees --tables=employees_ptchksum --no-check-binlog-format --recursion-method dsn=D=percona,t=dsns --chunk-size=66666
  • 指定每個分塊(chunk)校驗時間

動態調整chunk大小,使得校驗操作可以在指定的時間內完成。

# pt-table-checksum h=192.168.58.3,P=3306,u=admin --ask-pass --databases=employees --tables=employees_ptchksum --no-check-binlog-format --recursion-method dsn=D=percona,t=dsns --chunk-time=2

工作流程

通過general log來瞭解pt-table-checksum工具如何進行校驗,以及如何進行校驗語句查詢的,以下以校驗employees_ptchksum表為例。

-- 初始的一些檢查資料庫參數、負載信息這裡不再細說
51 Connect  admin@dbabd1 on  using TCP/IP
51 Query    SHOW VARIABLES LIKE 'innodb\_lock_wait_timeout'
51 Query    SET SESSION innodb_lock_wait_timeout=1
51 Query    SHOW VARIABLES LIKE 'wait\_timeout'
51 Query    SET SESSION wait_timeout=10000
51 Query    SELECT @@SQL_MODE
51 Query    SET @@SQL_QUOTE_SHOW_CREATE = 1/*!40101, @@SQL_MODE='NO_AUTO_VALUE_ON_ZERO,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION'*/
51 Query    SELECT @@server_id /*!50038 , @@hostname*/
51 Query    SELECT @@SQL_MODE
51 Query    SET SQL_MODE=',NO_AUTO_VALUE_ON_ZERO,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION'
51 Query    SHOW VARIABLES LIKE 'version%'
51 Query    SHOW ENGINES
51 Query    SHOW VARIABLES LIKE 'innodb_version'

-- 設置會話級binlog row格式為STATEMENT
51 Query    SELECT @@binlog_format
51 Query    /*!50108 SET @@binlog_format := 'STATEMENT'*/

-- 設置會話級隔離級別為RR(REPEATABLE READ)
51 Query    SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ
51 Query    SHOW /*!40103 GLOBAL*/ VARIABLES
51 Query    SELECT VERSION()
51 Query    SHOW ENGINES
51 Query    SHOW VARIABLES LIKE 'wsrep_on'
52 Connect  admin@dbabd1 on percona using TCP/IP
52 Query    SHOW VARIABLES LIKE 'innodb\_lock_wait_timeout'
52 Query    SET SESSION innodb_lock_wait_timeout=1
52 Query    SHOW VARIABLES LIKE 'wait\_timeout'
52 Query    SET SESSION wait_timeout=10000
52 Query    SELECT @@SQL_MODE
52 Query    SET @@SQL_QUOTE_SHOW_CREATE = 1/*!40101, @@SQL_MODE='NO_AUTO_VALUE_ON_ZERO,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION'*/
52 Query    SELECT @@server_id /*!50038 , @@hostname*/
52 Query    SHOW VARIABLES LIKE 'wsrep_on'
52 Query    SELECT dsn FROM `percona`.`dsns` ORDER BY id
52 Quit 
51 Query    SHOW VARIABLES LIKE 'wsrep_on'
51 Query    SELECT @@SERVER_ID
51 Query    SHOW VARIABLES LIKE 'wsrep_on'
51 Query    SELECT @@SERVER_ID

-- 創建存儲校驗結果表percona.checksums
51 Query    SHOW DATABASES LIKE 'percona'
51 Query    CREATE DATABASE IF NOT EXISTS `percona` /* pt-table-checksum */
51 Query    USE `percona`
51 Query    SHOW TABLES FROM `percona` LIKE 'checksums'
51 Query    CREATE TABLE IF NOT EXISTS `percona`.`checksums` (
     db             CHAR(64)     NOT NULL,
     tbl            CHAR(64)     NOT NULL,
     chunk          INT          NOT NULL,
     chunk_time     FLOAT            NULL,
     chunk_index    VARCHAR(200)     NULL,
     lower_boundary TEXT             NULL,
     upper_boundary TEXT             NULL,
     this_crc       CHAR(40)     NOT NULL,
     this_cnt       INT          NOT NULL,
     master_crc     CHAR(40)         NULL,
     master_cnt     INT              NULL,
     ts             TIMESTAMP    NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
     PRIMARY KEY (db, tbl, chunk),
     INDEX ts_db_tbl (ts, db, tbl)
  ) ENGINE=InnoDB DEFAULT CHARSET=utf8

-- 檢查資料庫伺服器運行狀態
51 Query    SHOW GLOBAL STATUS LIKE 'Threads_running'
51 Query    SELECT CONCAT(@@hostname, @@port)
51 Query    SELECT CRC32('test-string')
51 Query    SELECT CRC32('a')
51 Query    SELECT CRC32('a')
51 Query    SHOW VARIABLES LIKE 'wsrep_on'
51 Query    SHOW DATABASES
51 Query    SHOW /*!50002 FULL*/ TABLES FROM `employees`
51 Query    /*!40101 SET @OLD_SQL_MODE := @@SQL_MODE, @@SQL_MODE := '', @OLD_QUOTE := @@SQL_QUOTE_SHOW_CREATE, @@SQL_QUOTE_SHOW_CREATE := 1 */

-- 開始對錶employees_ptchksum進行分析
51 Query    USE `employees`
51 Query    SHOW CREATE TABLE `employees`.`employees_ptchksum`
51 Query    /*!40101 SET @@SQL_MODE := @OLD_SQL_MODE, @@SQL_QUOTE_SHOW_CREATE := @OLD_QUOTE */

-- 獲取表信息,通過主鍵或唯一索引,獲取校驗第一個chunk下邊界起點,這裡為emp_no = 10001
51 Query    EXPLAIN SELECT * FROM `employees`.`employees_ptchksum` WHERE 1=1
51 Query    SELECT /*!40001 SQL_NO_CACHE */ `emp_no` FROM `employees`.`employees_ptchksum` FORCE INDEX(`PRIMARY`) ORDER BY `emp_no` LIMIT 1 /*first lower boundary*/

-- 通過索引獲取校驗表行數
51 Query    SELECT /*!40001 SQL_NO_CACHE */ `emp_no` FROM `employees`.`employees_ptchksum` FORCE INDEX (`PRIMARY`) WHERE `emp_no` IS NOT NULL ORDER BY `emp_no` LIMIT 1 /*key_len*/
51 Query    EXPLAIN SELECT /*!40001 SQL_NO_CACHE */ * FROM `employees`.`employees_ptchksum` FORCE INDEX (`PRIMARY`) WHERE `emp_no` >= '10001' /*key_len*/

-- 清除表percona.checksums中有關employees_ptchksum表的校驗結果信息
51 Query    USE `percona`
51 Query    DELETE FROM `percona`.`checksums` WHERE db = 'employees' AND tbl = 'employees_ptchksum'

-- 確定每個chunk包含的行數,首個塊預設為1000行,可以根據系統狀態調整,調整每個塊校驗完成預設時間為0.5s
51 Query    USE `employees`
51 Query    EXPLAIN SELECT /*!40001 SQL_NO_CACHE */ `emp_no` FROM `employees`.`employees_ptchksum` FORCE INDEX(`PRIMARY`) WHERE ((`emp_no` >= '10001')) ORDER BY `emp_no` LIMIT 999, 2 /*next chunk boundary*/
51 Query    SELECT /*!40001 SQL_NO_CACHE */ `emp_no` FROM `employees`.`employees_ptchksum` FORCE INDEX(`PRIMARY`) WHERE ((`emp_no` >= '10001')) ORDER BY `emp_no` LIMIT 999, 2 /*next chunk boundary*/

-- 確定本次chunk校驗查詢的執行計劃,並通過replace into方式寫入校驗結果表
51 Query    EXPLAIN SELECT COUNT(*) AS cnt, COALESCE(LOWER(CONV(BIT_XOR(CAST(CRC32(CONCAT_WS('#', `emp_no`, `birth_date`, convert(`first_name` using utf8mb4), convert(`last_name` using utf8mb4), convert(`gender` using utf8mb4), `hire_date`)) AS UNSIGNED)), 10, 16)), 0) AS crc FROM `employees`.`employees_ptchksum` FORCE INDEX(`PRIMARY`) WHERE ((`emp_no` >= '10001')) AND ((`emp_no` <= '11000')) /*explain checksum chunk*/
51 Query    REPLACE INTO `percona`.`checksums` (db, tbl, chunk, chunk_index, lower_boundary, upper_boundary, this_cnt, this_crc) SELECT 'employees', 'employees_ptchksum', '1', 'PRIMARY', '10001', '11000', COUNT(*) AS cnt, COALESCE(LOWER(CONV(BIT_XOR(CAST(CRC32(CONCAT_WS('#', `emp_no`, `birth_date`, convert(`first_name` using utf8mb4), convert(`last_name` using utf8mb4), convert(`gender` using utf8mb4), `hire_date`)) AS UNSIGNED)), 10, 16)), 0) AS crc FROM `employees`.`employees_ptchksum` FORCE INDEX(`PRIMARY`) WHERE ((`emp_no` >= '10001')) AND ((`emp_no` <= '11000')) /*checksum chunk*/

-- 再次查看校驗chunk的行數和校驗結果,並更新校驗結果表
51 Query    SHOW WARNINGS
51 Query    SELECT this_crc, this_cnt FROM `percona`.`checksums` WHERE db = 'employees' AND tbl = 'employees_ptchksum' AND chunk = '1'
51 Query    UPDATE `percona`.`checksums` SET chunk_time = '0.007819', master_crc = '4f6eb3dc', master_cnt = '1000' WHERE db = 'employees' AND tbl = 'employees_ptchksum' AND chunk = '1'

-- 查看資料庫伺服器狀態,併進行下一個chunk的檢查校驗
51 Query    SHOW GLOBAL STATUS LIKE 'Threads_running'
51 Query    EXPLAIN SELECT /*!40001 SQL_NO_CACHE */ `emp_no` FROM `employees`.`employees_ptchksum` FORCE INDEX(`PRIMARY`) WHERE ((`emp_no` >= '11001')) ORDER BY `emp_no` LIMIT 63946, 2 /*next chunk boundary*/
51 Query    SELECT /*!40001 SQL_NO_CACHE */ `emp_no` FROM `employees`.`employees_ptchksum` FORCE INDEX(`PRIMARY`) WHERE ((`emp_no` >= '11001')) ORDER BY `emp_no` LIMIT 63946, 2 /*next chunk boundary*/
51 Query    EXPLAIN SELECT COUNT(*) AS cnt, COALESCE(LOWER(CONV(BIT_XOR(CAST(CRC32(CONCAT_WS('#', `emp_no`, `birth_date`, convert(`first_name` using utf8mb4), convert(`last_name` using utf8mb4), convert(`gender` using utf8mb4), `hire_date`)) AS UNSIGNED)), 10, 16)), 0) AS crc FROM `employees`.`employees_ptchksum` FORCE INDEX(`PRIMARY`) WHERE ((`emp_no` >= '11001')) AND ((`emp_no` <= '74947')) /*explain checksum chunk*/
51 Query    REPLACE INTO `percona`.`checksums` (db, tbl, chunk, chunk_index, lower_boundary, upper_boundary, this_cnt, this_crc) SELECT 'employees', 'employees_ptchksum', '2', 'PRIMARY', '11001', '74947', COUNT(*) AS cnt, COALESCE(LOWER(CONV(BIT_XOR(CAST(CRC32(CONCAT_WS('#', `emp_no`, `birth_date`, convert(`first_name` using utf8mb4), convert(`last_name` using utf8mb4), convert(`gender` using utf8mb4), `hire_date`)) AS UNSIGNED)), 10, 16)), 0) AS crc FROM `employees`.`employees_ptchksum` FORCE INDEX(`PRIMARY`) WHERE ((`emp_no` >= '11001')) AND ((`emp_no` <= '74947')) /*checksum chunk*/
51 Query    SHOW WARNINGS
51 Query    SELECT this_crc, this_cnt FROM `percona`.`checksums` WHERE db = 'employees' AND tbl = 'employees_ptchksum' AND chunk = '2'
51 Query    UPDATE `percona`.`checksums` SET chunk_time = '0.148209', master_crc = '48c0faee', master_cnt = '63947' WHERE db = 'employees' AND tbl = 'employees_ptchksum' AND chunk = '2'

……省略……

51 Query    SHOW GLOBAL STATUS LIKE 'Threads_running'
51 Query    EXPLAIN SELECT /*!40001 SQL_NO_CACHE */ `emp_no` FROM `employees`.`employees_ptchksum` FORCE INDEX(`PRIMARY`) WHERE ((`emp_no` >= '474878')) ORDER BY `emp_no` LIMIT 291764, 2 /*next chunk boundary*/
51 Query    SELECT /*!40001 SQL_NO_CACHE */ `emp_no` FROM `employees`.`employees_ptchksum` FORCE INDEX(`PRIMARY`) WHERE ((`emp_no` >= '474878')) ORDER BY `emp_no` LIMIT 291764, 2 /*next chunk boundary*/
51 Query    SELECT /*!40001 SQL_NO_CACHE */ `emp_no` FROM `employees`.`employees_ptchksum` FORCE INDEX(`PRIMARY`) ORDER BY `emp_no` DESC LIMIT 1 /*last upper boundary*/
51 Query    EXPLAIN SELECT COUNT(*) AS cnt, COALESCE(LOWER(CONV(BIT_XOR(CAST(CRC32(CONCAT_WS('#', `emp_no`, `birth_date`, convert(`first_name` using utf8mb4), convert(`last_name` using utf8mb4), convert(`gender` using utf8mb4), `hire_date`)) AS UNSIGNED)), 10, 16)), 0) AS crc FROM `employees`.`employees_ptchksum` FORCE INDEX(`PRIMARY`) WHERE ((`emp_no` >= '474878')) AND ((`emp_no` <= '499999')) /*explain checksum chunk*/
51 Query    REPLACE INTO `percona`.`checksums` (db, tbl, chunk, chunk_index, lower_boundary, upper_boundary, this_cnt, this_crc) SELECT 'employees', 'employees_ptchksum', '4', 'PRIMARY', '474878', '499999', COUNT(*) AS cnt, COALESCE(LOWER(CONV(BIT_XOR(CAST(CRC32(CONCAT_WS('#', `emp_no`, `birth_date`, convert(`first_name` using utf8mb4), convert(`last_name` using utf8mb4), convert(`gender` using utf8mb4), `hire_date`)) AS UNSIGNED)), 10, 16)), 0) AS crc FROM `employees`.`employees_ptchksum` FORCE INDEX(`PRIMARY`) WHERE ((`emp_no` >= '474878')) AND ((`emp_no` <= '499999')) /*checksum chunk*/
51 Query    SHOW WARNINGS
51 Query    SELECT this_crc, this_cnt FROM `percona`.`checksums` WHERE db = 'employees' AND tbl = 'employees_ptchksum' AND chunk = '4'
51 Query    UPDATE `percona`.`checksums` SET chunk_time = '0.036433', master_crc = '6ca4b1c9', master_cnt = '25122' WHERE db = 'employees' AND tbl = 'employees_ptchksum' AND chunk = '4'

-- 上述校驗到chunk = 4其實已經包含整張表的數據,以下的chunk範圍為emp_no < 10001和emp_no > 4999999,這樣做的意圖很明顯,因為工具並不能確認從庫在這兩個chunk所對應的數據範圍內沒有數據存在,為了保證檢查數據的完整性,所以進行這樣的檢查操作,包括了所有的可能性。

'對應emp_no < 10001'
51 Query    SHOW GLOBAL STATUS LIKE 'Threads_running'
51 Query    EXPLAIN SELECT  COUNT(*), '0' FROM `employees`.`employees_ptchksum` FORCE INDEX(`PRIMARY`) WHERE ((`emp_no` < '10001')) ORDER BY `emp_no` /*explain past lower chunk*/
51 Query    REPLACE INTO `percona`.`checksums` (db, tbl, chunk, chunk_index, lower_boundary, upper_boundary, this_cnt, this_crc) SELECT 'employees', 'employees_ptchksum', '5', 'PRIMARY', NULL, '10001', COUNT(*), '0' FROM `employees`.`employees_ptchksum` FORCE INDEX(`PRIMARY`) WHERE ((`emp_no` < '10001')) ORDER BY `emp_no` /*past lower chunk*/
51 Query    SHOW WARNINGS
51 Query    SELECT this_crc, this_cnt FROM `percona`.`checksums` WHERE db = 'employees' AND tbl = 'employees_ptchksum' AND chunk = '5'
51 Query    UPDATE `percona`.`checksums` SET chunk_time = '0.062096', master_crc = '0', master_cnt = '0' WHERE db = 'employees' AND tbl = 'employees_ptchksum' AND chunk = '5'

'對應emp_no > 499999'
51 Query    SHOW GLOBAL STATUS LIKE 'Threads_running'
51 Query    EXPLAIN SELECT  COUNT(*), '0' FROM `employees`.`employees_ptchksum` FORCE INDEX(`PRIMARY`) WHERE ((`emp_no` > '499999')) ORDER BY `emp_no` /*explain past upper chunk*/
51 Query    REPLACE INTO `percona`.`checksums` (db, tbl, chunk, chunk_index, lower_boundary, upper_boundary, this_cnt, this_crc) SELECT 'employees', 'employees_ptchksum', '6', 'PRIMARY', '499999', NULL, COUNT(*), '0' FROM `employees`.`employees_ptchksum` FORCE INDEX(`PRIMARY`) WHERE ((`emp_no` > '499999')) ORDER BY `emp_no` /*past upper chunk*/
51 Query    SHOW WARNINGS
51 Query    SELECT this_crc, this_cnt FROM `percona`.`checksums` WHERE db = 'employees' AND tbl = 'employees_ptchksum' AND chunk = '6'
51 Query    UPDATE `percona`.`checksums` SET chunk_time = '0.002064', master_crc = '0', master_cnt = '0' WHERE db = 'employees' AND tbl = 'employees_ptchksum' AND chunk = '6'

51 Query    SHOW GLOBAL STATUS LIKE 'Threads_running'
51 Query    SHOW MASTER STATUS
51 Quit 

根據general log可以看出,pt-table-checksum主要工作的流程可以總結如下:

  1. 連接主庫和從庫,查詢當前資料庫伺服器信息,包括參數設置,負載信息等;
  2. 根據工具選項設置會話級別參數,設置會話級binlog row formatSTATEMENT
  3. 根據工具選項創建校驗結果表(預設為percona.checksums表),查看當前資料庫伺服器運行狀態;
  4. 獲取校驗的資料庫和表,逐張順序進行檢查校驗;
  5. 開始分析表,根據表的索引(如有),將表分成多個chunk,每個chunk包含多行,預設為1000,chunk對應的表行數可以根據資料庫性能狀態動態調整;
  6. 根據以上分析生成表的校驗語句,並檢查表結構;
  7. 開始進行表的校驗分析,為了保證一致性,這個階段會將當前chuck所包含的行加上行鎖,並將校驗的結果以replace into方式存入校驗結果表;
  8. 再次查詢校驗結果表,並更新master_crcmaster_cnt的值(主庫),如果是從庫則是this_crcthis_cnt
  9. 根據資料庫運行狀態調整下一個chunk所包含行數;
  10. 繼續下一個chunk的校驗檢查,直到表中所有的chunk校驗完成;
  11. 從庫運行完校驗檢查,彙總這張表的結果;
  12. 迴圈完成所有需要校驗的表直到完成所有的表的校驗操作。

總結

  1. pt-table-checksum是當前MySQL主從資料庫數據一致性校驗比較好的工具,但也會對伺服器性能造成影響,儘管影響的範圍有限,在進行每個chunk檢查校驗時會對chunk中涉及的表行加鎖,所以,檢驗還是儘量在業務低峰期進行操作;
  2. pt-table-checksum工具無法對主從表結構不一致的表進行校驗;
  3. 校驗結果只能看出哪些表出現不一致性,並無法得出具體出現多少行數據不一致,哪些數據行不一致等。

參考

https://www.percona.com/doc/percona-toolkit/LATEST/pt-table-checksum.html
http://seanlook.com/2015/12/29/mysql_replica_pt-table-checksum/
http://keithlan.github.io/2016/05/25/pt_table_checksum/

☆〖本人水平有限,文中如有錯誤還請留言批評指正!〗☆


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 如果要批量複製大量的數據,用ado.net或者其他orm框架逐條讀取並寫入,有時會耗時太長,滿足不了要求,此時SqlBulkCopy就可以出來大顯身手了,相信許多人瞭解或使用過它。 但實際使用時,還是會遇上些問題,這裡做些記錄,也許能幫你避開一些“坑”。 column mapping 問題 。 在設 ...
  • 存儲過程 就是一組用於完成特定功能的PL/SQL 具名語句塊,該SQL語句集經過編譯後存儲在資料庫系統中。在使用時候,我們只需要通過指定已經定義的存儲過程名字並給出對應的參數來執行 存儲過程的定義語法 create or replace procedure 過程名(參數名 參數模式 參數類型,參數名 ...
  • 什麼是Elasticsearch的動態映射? 它有什麼作用和優點? 如何自定義使用動態模板? 本篇文章介紹這些內容. ...
  • 環境: 操作系統:Windows7(64位); 資料庫:Oracle 11g R2; 資料庫字元集:UTF-8 一、下載: (參考鏈接:https://blog.csdn.net/u011031430/article/details/76167934) 1.打開 https://edelivery. ...
  • MySQL 是如何解決幻讀的 一、什麼是幻讀 在一次事務裡面,多次查詢之後,結果集的個數不一致的情況叫做幻讀。 而多出來或者少的哪一行被叫做 二、為什麼要解決幻讀 在高併發資料庫系統中,需要保證事務與事務之間的隔離性,還有事務本身的一致性。 三、MySQL 是如何解決幻讀的 如果你看到了這篇文章,那 ...
  • 有關關係資料庫範式的講解,有一篇非常好的文章,推薦給大家,可以幫助大家理解範式的含義、作用。解釋一下關係資料庫的第一第二第三範式? - 知乎用戶的回答 - 知乎https://www.zhihu.com/question/24696366/answer/29189700 ...
  • 1.無論何時只要有多個查詢在同一時刻修改數據,都會產生併發控制的問題 2.討論mysql在兩個層面,伺服器層和存儲引擎層,如何併發控制讀寫 3.舉了個mbox郵箱文件的例子,說如果有多個進程同時對mbox文件寫東西,那麼在文件的末尾會,交叉混亂的添加,比如進程1寫了幾行,進程2也寫了幾行,互相交叉, ...
  • 筆記記錄自林曉斌(丁奇)老師的《MySQL實戰45講》 (本篇內圖片均來自丁奇老師的講解,如有侵權,請聯繫我刪除) 16) --“order by”是怎麼工作的? 在林老師的課程中,第15講是前面問題的答疑,我打算最後將答疑問題統一整理出來,所以就繼續這些內容的筆記了。 全欄位排序: 假設有一個表是 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...