sqlserver-tidb-replay
by @dongdong-bryant
Replay SQL Server slow query logs on TiDB to verify compatibility, compare performance, and generate syntax conversion reports during migration.
clawhub install sqlserver-tidb-replayð About This Skill
SQL Server â TiDB SQL åæŸå·¥å ·
å·¥å ·ç®ä»
åè Bowen-Tang/sql-replayïŒMySQL â TiDB æµéåæŸå·¥å ·ïŒïŒå®ç° SQL Server â TiDB ç SQL åæŸèœåã
æ žå¿çšéïŒ
åèæ¥æºïŒGitHub - Bowen-Tang/sql-replay: mysql slow query replay
å·¥äœæµçš
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
â â éé (collect) â
â SQL Server æ
¢æ¥è¯¢æ¥å¿ â CSVïŒéæ å XE æ ŒåŒïŒ â
â â ïž éèŠèœ¬æ¢äžºæ å SQL æ ŒåŒ â
â ç®æ ïŒsqlserver.sql_statement_completed äºä»¶ â
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
â CSV æä»¶
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
â â¡ CSV 蜬æ å SQL (csv_to_sql) â
â è§£æåå§æ¥å¿ â æ å SQL è¯å¥ â
â åèœïŒå»é€ XE åçŒç¬Šå·ãæå SQL ææ¬ãæ ŒåŒå â
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
â æ å SQL CSV
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
â ⢠解æ (parse) â
â CSV â Python è§£æ â JSON äžéŽæ ŒåŒ â
â åèœïŒå»éãè¿æ»€æ æSQLãçæå¯åæŸJSON â
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
â JSON æä»¶
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
â â£ åæŸ (replay) â
â JSON â Python åæŸèæ¬ â TiDB å¹¶è¡æ§è¡ â
â åèœïŒè¿æ¥æ± ãå¹¶åæ§è¡ãæ conn_id äž²è¡ â
â èŸåºïŒresults_{task_name}.json â
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
â ç»æ JSON
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
â â€ åæ (analyze) â
â ç»æ JSON â åæèæ¬ â
â åèœïŒ â
â - è¯å«è¯æ³äžå
Œå®¹é®é¢ â
â - æ§èœå¯¹æ¯åæïŒTiDB vs SQL ServerïŒ â
â - èªåšçæè¯æ³èœ¬æ¢å»ºè®®ïŒèœ¬æ¢å/å/ç¹ïŒ â
â - HTML æ¥åïŒå«è¯æ³èœ¬æ¢å¯¹ç
§è¡šïŒ â
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
å眮åå€
ç¯å¢èŠæ±
TiDB è¿æ¥ä¿¡æ¯
# æ¹åŒäžïŒé
眮ç¯å¢åé
export TIDB_HOST="your-tidb-host"
export TIDB_PORT="4000"
export TIDB_USER="root"
export TIDB_PASSWORD="your-password"
export TIDB_DATABASE="test_db"æ¹åŒäºïŒåœä»€è¡åæ°äŒ å
¥ïŒäŒå
级æŽé«ïŒ
æ žå¿åèœè¯Žæ
CSV 蜬æ å SQLïŒå ³é®æ¥éª€ïŒ
SQL Server æ ¢æ¥è¯¢æ¥å¿å¯Œåºç CSV äžæ¯æ åç XE æ ŒåŒïŒå å«å€§é XE åçŒç¬Šå·åæ··åæ ŒåŒïŒéèŠå 蜬æ¢äžºæ å SQL è¯å¥ã
åå§ CSV åžžè§é®é¢ïŒ
sql_statement_completedãsp_statement_completedïŒ@1, @2, N'' ç Unicode åçŒïŒèœ¬æ¢èæ¬ïŒcsv_to_sql.py
èªåšå€çïŒ
äœ¿çšæ¥éª€
â éé SQL Server æ ¢æ¥å¿
åš Windows æå¡åšäžè¿è¡ PowerShell èæ¬ïŒ
# æ¹åŒäžïŒExtended EventsïŒæšèïŒç»ææž
æ°ïŒ
.\collect_xe.ps1 -SessionName "slow_query_capture" -ThresholdMs 1000 -OutputPath "C:\slow_logs\slow_20260412.csv"æ¹åŒäºïŒä» SQL Server Management Studio å¯Œåºæ
¢æ¥è¯¢æ¥å¿
å³é® â å¯Œåº â CSVïŒå« statementãdurationãcpu çåæ®µïŒ
ééçåæ®µïŒ | åæ®µ | 诎æ | |------|------| | statement | SQL ææ¬ | | duration_us | æ§è¡æ¶éŽïŒåŸ®ç§ | | cpu_us | CPU æ¶éŽïŒåŸ®ç§ | | physical_reads | ç©çè¯»æ¬¡æ° | | logical_reads | é»èŸè¯»æ¬¡æ° | | row_count | è¿åè¡æ° | | start_time | åŒå§æ§è¡æ¶éŽ | | database_name | æå±æ°æ®åº | | session_id | è¿æ¥ ID |
> 泚æïŒéééèŠ VIEW SERVER STATE æéãå»ºè®®åšæµè¯ç¯å¢è¿è¡ã
â¡ CSV 蜬æ å SQL æ ŒåŒ
python3 scripts/csv_to_sql.py \
--input /path/to/raw_slow_query.csv \
--output /path/to/standard_sql.csv \
--normalize \
--remove-prefix
åæ°è¯ŽæïŒ
| åæ° | 诎æ |
|------|------|
| --input | åå§ CSV è·¯åŸ |
| --output | æ å SQL CSV èŸåºè·¯åŸ |
| --normalize | è§èå SQL æ ŒåŒïŒå»é€å€äœç©ºçœïŒ |
| --remove-prefix | å»é€ XE äºä»¶åçŒç¬Šå· |
蜬æ¢ååæ®µïŒ | åæ®µ | 诎æ | |------|------| | sql_text | æ å SQL ææ¬ | | duration_ms | æ§è¡æ¶éŽïŒæ¯«ç§ïŒæ ååïŒ | | database_name | æ°æ®åºå | | session_id | è¿æ¥ ID |
⢠解æ CSV äžºåæŸæ ŒåŒ
python3 scripts/parse_csv.py \
--input /path/to/standard_sql.csv \
--output /path/to/slow_20260412.format.json \
--filter-type select,insert,update,delete \
--filter-duration-ms 1000 \
--remove-admin \
--lang cn
è§£æèŸåº JSON æ ŒåŒïŒ
[
{
"conn_id": "52",
"start_time": "2026-04-12T10:00:01.123",
"sql": "SELECT c FROM sbtest1 WHERE id=250438",
"sql_type": "select",
"duration_us": 380,
"database": "test_db"
}
]
â£ åæŸè³ TiDB
python3 scripts/replay_tidb.py \
--input /path/to/slow_20260412.format.json \
--host $TIDB_HOST \
--port $TIDB_PORT \
--user $TIDB_USER \
--password $TIDB_PASSWORD \
--database $TIDB_DATABASE \
--speed 1.0 \
--workers 4 \
--output-dir ./replay_results \
--task-name "test_migration"
åæŸèŸåºïŒ
replay_results/
âââ test_migration_conn_52.json # æ¯äžª conn_id äžäžªæä»¶
âââ test_migration_conn_88.json
âââ test_migration_summary.json # æ±æ»ä¿¡æ¯
æ±æ»æä»¶æ ŒåŒïŒ
{
"task_name": "test_migration",
"total_sqls": 1523,
"total_errors": 12,
"avg_duration_us": 4523,
"max_duration_us": 128500,
"compatibility_rate": "99.21%"
}
†çæåææ¥åïŒå«è¯æ³èœ¬æ¢ïŒ
python3 scripts/analyze_results.py \
--input-dir ./replay_results \
--task-name "test_migration" \
--output ./replay_report.html \
--source-db "SQL Server 2022" \
--target-db "TiDB v8.0"
æ¥åå 容ïŒ
è¯æ³èœ¬æ¢åèœ
èªåšè¯å«çäžå Œå®¹æš¡åŒ
| ç±»å« | SQL Server åæ³ | TiDB æ¹å | 蜬æ¢è¯Žæ |
|------|----------------|-----------|---------|
| OUTPUT clause | INSERT INTO t OUTPUT inserted.id VALUES(...) | INSERT INTO t VALUES(...); SELECT LAST_INSERT_ID(); | TiDB äžæ¯æ OUTPUT clauseïŒçš LAST_INSERT_ID() æ¿ä»£ |
| NVARCHAR | N'Unicodeå笊䞲' | CAST('å笊䞲' AS CHAR) | TiDB å笊éå·®åŒïŒéæŸåŒ CAST |
| OPENJSON | SELECT * FROM OPENJSON(@json) | JSON_EXTRACT(@json, '$') | TiDB äžæ¯æ OPENJSONïŒçš JSON åœæ°æ¿ä»£ |
| Sequence | NEXT VALUE FOR seq_name | NEXTVAL('seq_name') | TiDB åºåè¯æ³äžå |
| OFFSET | OFFSET 10 ROWS FETCH NEXT 5 ROWS | LIMIT 5 OFFSET 10 | TiDB LIMIT...OFFSET 顺åºçžå |
| TOP | SELECT TOP 10 * FROM t | SELECT * FROM t LIMIT 10 | TiDB çš LIMIT æ¿ä»£ TOP |
| INTO | SELECT * INTO #temp FROM t | CREATE TEMPORARY TABLE t AS SELECT... | TiDB äžæ¯æ SELECT INTO 䞎æ¶è¡š |
| MERGE | MERGE INTO t USING... | INSERT...ON DUPLICATE KEY UPDATE | TiDB äžæ¯æ MERGEïŒçš IODU æ¿ä»£ |
æ¥åäžçè¯æ³èœ¬æ¢è¡šç€ºäŸ
ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
â è¯æ³èœ¬æ¢è®°åœ #12 â
ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ€
â æº SQL ServerïŒ â
â INSERT INTO orders (name, amount) â
â OUTPUT inserted.order_id â
â VALUES ('ååA', 100) â
ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ€
â 蜬æ¢å TiDBïŒ â
â INSERT INTO orders (name, amount) â
â VALUES ('ååA', 100); â
â SELECT LAST_INSERT_ID() AS order_id; â
ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ€
â 蜬æ¢ç¹ïŒ â
â 1. OUTPUT inserted.order_id â SELECT LAST_INSERT_ID() â
â 2. åå ïŒTiDB äžæ¯æ OUTPUT clause â
â 3. 圱åïŒéåºçšå±è°æŽè·åèªå¢ ID çæ¹åŒ â
ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
宿Žäœ¿çšæ¡äŸ
åºæ¯ïŒæé¶è¡æ žå¿ç³»ç» SQL Server â TiDB è¿ç§»éªè¯
èæ¯ïŒé¶è¡ä¿¡èŽ·ç³»ç»ä» SQL Server 2019 è¿ç§»è³ TiDBïŒéèŠéªè¯ 2000+ æ¡æ žå¿ SQL çå Œå®¹æ§ã
Step 1ïŒçäº§åºæ ¢æ¥å¿éé
åš SQL Server ç产åºäžè¿è¡ïŒéåŒè®Ÿäžº 500msïŒïŒ
.\collect_xe.ps1 -SessionName "credit_slow" -ThresholdMs 500 -OutputPath "C:\logs\credit_slow_20260412.csv"
ééç»æïŒçºŠ 3800 æ¡æ
¢æ¥è¯¢ïŒèŠç 3 倩çäžå¡é«å³°æãStep 2ïŒCSV 蜬æ å SQL
python3 scripts/csv_to_sql.py \
--input credit_slow_20260412.csv \
--output credit_slow_standard.csv \
--normalize \
--remove-prefix
Step 3ïŒè§£æå¹¶è¿æ»€
python3 scripts/parse_csv.py \
--input credit_slow_standard.csv \
--output credit_slow.format.json \
--filter-type select,insert,update,delete \
--filter-duration-ms 500 \
--remove-admin
è§£æåïŒ2156 æ¡ææ SQLãStep 4ïŒåæŸè³ TiDB æµè¯ç¯å¢
python3 scripts/replay_tidb.py \
--input credit_slow.format.json \
--host 192.168.1.100 \
--port 4000 \
--user root \
--password "Xszyh@315315" \
--database credit_db \
--speed 2.0 \
--workers 8 \
--output-dir ./replay_results \
--task-name "credit_migration"
Step 5ïŒçææ¥å
python3 scripts/analyze_results.py \
--input-dir ./replay_results \
--task-name "credit_migration" \
--output credit_migration_report.html \
--source-db "SQL Server 2019" \
--target-db "TiDB v8.0"
æ¥åç»æç€ºäŸïŒ
è¿ç§»å
Œå®¹æ§è¯äŒ°æ¥å
===============================
æºåºïŒSQL Server 2019
ç®æ åºïŒTiDB v8.0
ééæ¶éŽïŒ2026-04-12
æ»SQLæ°ïŒ2156
å
Œå®¹æ§ïŒ98.6%ïŒé误SQL 30æ¡ïŒé误ååžïŒ
- è¯æ³äžå
Œå®¹ïŒ8æ¡ïŒäž»èŠæ¯ OUTPUT clauseïŒ
- åœæ°äžååšïŒ12æ¡ïŒDB2å
Œå®¹åœæ°ïŒ
- å笊éé®é¢ïŒ5æ¡ïŒNCHAR/NVARCHAR vs CHAR/VARCHARïŒ
- æé¡¹é误ïŒ5æ¡
è¯æ³èœ¬æ¢è®°åœïŒ
- #1: OUTPUT clause â SELECT LAST_INSERT_ID()ïŒ12æ¡ïŒ
- #2: N'' UnicodeåçŒ â CAST() 蜬æ¢ïŒ8æ¡ïŒ
- #3: OFFSET...FETCH â LIMIT...OFFSETïŒ15æ¡ïŒ
- #4: OPENJSON â JSON_EXTRACTïŒ5æ¡ïŒ
é«é£é©SQLïŒéæåšæ¹åïŒïŒ
1. proc_audit_insert (è¡12) - OUTPUT clause
2. proc_statement_get (è¡89) - OPENJSON åœæ°
...
æ§èœå¯¹æ¯ïŒæåSQLïŒïŒ
- TiDB å¹³åååºïŒ12.3ms
- SQL Server å¹³åååºïŒ45.2ms
- TiDB å 鿝ïŒ3.7x
åžžè§é®é¢
Q: CSV æ¯æ ¢æ¥è¯¢æ¥å¿ïŒäžæ¯æ å XE æ ŒåŒæä¹åïŒ
AïŒè¿æ¯æ£åžžæ åµãSQL Server æ ¢æ¥è¯¢æ¥å¿å¯Œåºæ¶æ ŒåŒäžç»äžïŒéçšcsv_to_sql.py è¿è¡æ åå蜬æ¢ãè¯¥èæ¬äŒèªåšå€çæ¢è¡ç¬ŠãXE åçŒãåéåæ°çé®é¢ãQ: Extended Events äŒè¯å¯¹çäº§åºæ§èœåœ±å倧åïŒ
AïŒXE 讟计䞺äœåŒéïŒå»ºè®®éåŒè®Ÿé«äžäºïŒâ¥500msïŒé¿å é倪倧ãåŠæ CPU çŽ§åŒ ïŒå¯äœ¿çšhistogram ç®æ å
çååžãQ: åæŸæ¶åºç°å€§éè¿æ¥é误ïŒ
AïŒæ£æ¥ TiDB è¿æ¥æ°éå¶ïŒmax_connectionsïŒïŒåæ¶è°æŽ --workers åæ°ãSQL Server å TiDB çè¿æ¥æ± æš¡åäžåïŒå»ºè®®ä» 4 åŒå§éæ¥å åãQ: è¯æ³èœ¬æ¢æ¯èªåšçåïŒ
AïŒåææ¥åäŒèªåšè¯å«åžžè§çè¯æ³äžå Œå®¹æš¡åŒïŒå¹¶ç»åºèœ¬æ¢å»ºè®®ãäœæç»æ¹åé人工确讀ïŒç¹å«æ¯æ¶åäžå¡é»èŸçéšåãQ: æ§èœå¯¹æ¯æä¹åçïŒ
AïŒåæŸæ¶äŒè®°åœ TiDB æ§è¡æ¶éŽïŒæ±æ»æä»¶äžäŒäžå SQL Server çduration_us è¿è¡å¯¹æ¯ã泚æïŒçžåæ°æ®éäžå¯¹æ¯æææä¹ãQ: èœåæŸååšè¿çšåïŒ
AïŒå¯ä»¥ïŒäœééçæ¯sp_statement_completed äºä»¶ãéèŠç¡®ä¿ TiDB 端æå¯¹åºçååšè¿çšïŒååšè¿çšäžäŒè¢«èªåšç¿»è¯ïŒéæåšè¿ç§»ïŒãèŸåºæä»¶è¯Žæ
| æä»¶ | 诎æ |
|------|------|
| *_raw.csv | åå§ééæ°æ® |
| *_standard.csv | æ å SQL CSVïŒå·²èœ¬æ¢ïŒ |
| *.format.json | è§£æåçåæŸäžéŽæ ŒåŒ |
| replay_results/*.json | åæŸæ§è¡ç»æ |
| replay_report.html | æç»å
Œå®¹æ§æ¥åïŒå«è¯æ³èœ¬æ¢å¯¹ç
§è¡šïŒ |
äž MySQLâTiDB åæŸå·¥å ·çåºå«
| 绎床 | Bowen-Tang/sql-replay | æ¬å·¥å · | |------|----------------------|--------| | æºæ°æ®åº | MySQL slow log | SQL Server æ ¢æ¥è¯¢æ¥å¿ | | æ¥å¿æ ŒåŒ | MySQL slow log text | Extended Events CSVïŒéæ åïŒ | | CSV æ åå | äžéèŠ | éèŠïŒcsv_to_sql.pyïŒ | | è¯æ³èœ¬æ¢ | æ | èªåšè¯å«å¹¶çæèœ¬æ¢å¯¹ç §è¡š | | æ§èœå¯¹æ¯ | åºç¡ | 宿Žå¯¹æ¯åæ | | åæŸç®æ | TiDB | TiDB |
*Skill çæ¬ïŒ1.1.0 | åèïŒsql-replay v0.3.4 | éçšïŒSQL Server 2008+ â TiDB 5.0+*
ð Tips & Best Practices
Q: CSV æ¯æ ¢æ¥è¯¢æ¥å¿ïŒäžæ¯æ å XE æ ŒåŒæä¹åïŒ
AïŒè¿æ¯æ£åžžæ åµãSQL Server æ ¢æ¥è¯¢æ¥å¿å¯Œåºæ¶æ ŒåŒäžç»äžïŒéçšcsv_to_sql.py è¿è¡æ åå蜬æ¢ãè¯¥èæ¬äŒèªåšå€çæ¢è¡ç¬ŠãXE åçŒãåéåæ°çé®é¢ãQ: Extended Events äŒè¯å¯¹çäº§åºæ§èœåœ±å倧åïŒ
AïŒXE 讟计䞺äœåŒéïŒå»ºè®®éåŒè®Ÿé«äžäºïŒâ¥500msïŒé¿å é倪倧ãåŠæ CPU çŽ§åŒ ïŒå¯äœ¿çšhistogram ç®æ å
çååžãQ: åæŸæ¶åºç°å€§éè¿æ¥é误ïŒ
AïŒæ£æ¥ TiDB è¿æ¥æ°éå¶ïŒmax_connectionsïŒïŒåæ¶è°æŽ --workers åæ°ãSQL Server å TiDB çè¿æ¥æ± æš¡åäžåïŒå»ºè®®ä» 4 åŒå§éæ¥å åãQ: è¯æ³èœ¬æ¢æ¯èªåšçåïŒ
AïŒåææ¥åäŒèªåšè¯å«åžžè§çè¯æ³äžå Œå®¹æš¡åŒïŒå¹¶ç»åºèœ¬æ¢å»ºè®®ãäœæç»æ¹åé人工确讀ïŒç¹å«æ¯æ¶åäžå¡é»èŸçéšåãQ: æ§èœå¯¹æ¯æä¹åçïŒ
AïŒåæŸæ¶äŒè®°åœ TiDB æ§è¡æ¶éŽïŒæ±æ»æä»¶äžäŒäžå SQL Server çduration_us è¿è¡å¯¹æ¯ã泚æïŒçžåæ°æ®éäžå¯¹æ¯æææä¹ãQ: èœåæŸååšè¿çšåïŒ
AïŒå¯ä»¥ïŒäœééçæ¯sp_statement_completed äºä»¶ãéèŠç¡®ä¿ TiDB 端æå¯¹åºçååšè¿çšïŒååšè¿çšäžäŒè¢«èªåšç¿»è¯ïŒéæåšè¿ç§»ïŒã