復現MySQL的索引選擇失誤以及透過OPTIMIZER_TRACE分析過程
驗證環境:MySQL 5.7.39 windows-pc
一、構造資料(生成150萬資料)
構建一張賬戶表,帶有一級部門id和二級部門id,並且建立有索引。比較典型的業務場景,根據部門id進行各類查詢。
CREATE TABLE `TM_ACCOUNT` (
`account_id` bigint(20) NOT null ,
`name` varchar(32) DEFAULT '',
`address` varchar(32) DEFAULT '',
`org_first_id` int(10) DEFAULT 0,
`org_second_id` int(10) DEFAULT 0,
`biz_date` date DEFAULT null,
`last_modify_dt` datetime DEFAULT null,
PRIMARY KEY (`account_id`),
KEY IDX_org_id_combine(org_first_id,org_second_id),
KEY IDX_last_modify_dt_org_first_id_name(last_modify_dt,org_first_id,org_second_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
1. 構造資料
此處直接透過jdbc批次插入資料。
資料分佈,保證資料無傾斜,索引資料均勻:
- org_first_id和org_second_id欄位都是在1-100間隨機分佈
- last_modify_dt在25天間隨機分佈
程式碼可以直接使用,詳情見附件3
二、透過explain驗證語句的索引使用
檢視錶的基本情況
show index from TM_ACCOUNT ; -- 看索引
執行結果,可以看到org_first_id/org_second_id的區分度,都很不錯。
Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
---|---|---|---|---|---|---|---|---|---|---|---|---|
tm_account | 0 | PRIMARY | 1 | account_id | A | 1408599 | BTREE | |||||
tm_account | 1 | IDX_org_id_combine | 1 | org_first_id | A | 101 | YES | BTREE | ||||
tm_account | 1 | IDX_org_id_combine | 2 | org_second_id | A | 10611 | YES | BTREE | ||||
tm_account | 1 | IDX_last_modify_dt_org_first_id_name | 1 | last_modify_dt | A | 24 | YES | BTREE | ||||
tm_account | 1 | IDX_last_modify_dt_org_first_id_name | 2 | org_first_id | A | 2497 | YES | BTREE | ||||
tm_account | 1 | IDX_last_modify_dt_org_first_id_name | 3 | org_second_id | A | 251724 | YES | BTREE |
show table status like '%TM_ACCOUNT%'; -- 看錶狀態,有資料大小、索引大小、大概行數
可看到使用了InnoDB引擎,大概行數是1408599,實際行數是1500000整。
Name | Engine | Version | Row_format | Rows | Avg_row_length | Data_length | Max_data_length | Index_length | Data_free | Auto_increment | Create_time | Update_time | Check_time | Collation | Checksum | Create_options | Comment |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
tm_account | InnoDB | 10 | Dynamic | 1408599 | 83 | 118128640 | 0 | 128253952 | 7340032 | 2022-09-13 10:49:36 | utf8mb4_general_ci |
常規的查詢
explain SELECT * from TM_ACCOUNT where ACCOUNT_ID = '10'; -- 典型的主鍵欄位查詢,非常快,type=const
explain SELECT * from TM_ACCOUNT where ACCOUNT_ID = '10'; -- 典型的主鍵欄位查詢,非常快,type=const
explain SELECT * from TM_ACCOUNT where NAME = 'name-11'; -- 典型的非索引欄位查詢,全表掃描
explain SELECT * from TM_ACCOUNT where ADDRESS = 'QR3xHEOpaLAVNFCtAKXY'; -- 典型的非索引欄位查詢,全表掃描
explain SELECT * from TM_ACCOUNT where LAST_MODIFY_DT = '2100-09-13 00:00:00' and ACCOUNT_ID > 100 LIMIT 2; -- 典型的範圍查詢,掃描索引。單速度也很快
透過改變查詢條件,引導MySQL最佳化器,選擇錯誤的索引、規則
下面透過3個SQL查詢的結果對比,來複現MySQL最佳化器如何選錯最佳化場景。(這裡不討論為何不換種寫法,直接規避劣化SQL。往往出現這類SQL時,一是業務場景複雜,二是開發時資料量少並未發現,在生產環境才能出現)
-- SQL-1
explain
SELECT * from TM_ACCOUNT where org_first_id >= 99 and org_second_id in (1,2,3,60) and BIZ_DATE in ('2100-09-01','2100-09-02')and ACCOUNT_ID > '120306' order by ACCOUNT_ID desc LIMIT 5000;
查詢結果:可見使用了IDX_org_id_combine索引,並用到索引範圍掃描、回表查詢、臨時檔案排序。不算是一個很好的查詢語句,但實際業務中的查詢條件,只會更復雜。直接查詢耗時140ms。
id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
---|---|---|---|---|---|---|---|---|---|---|---|
1 | SIMPLE | TM_ACCOUNT | range | PRIMARY,IDX_org_id_combine | IDX_org_id_combine | 18 | 33942 | 4.0 | Using index condition; Using where; Using filesort |
-- SQL-2 壞案例-全表掃描;
explain
SELECT * from TM_ACCOUNT where org_first_id >= 90 and org_second_id in (1,2,3,60) and BIZ_DATE in ('2100-09-01','2100-09-02') and ACCOUNT_ID > '120306' order by ACCOUNT_ID desc LIMIT 5000;
查詢結果:改變org_first_id條件,擴大查詢範圍,結果變成了主鍵索引的大範圍掃描,預估掃描行數70萬行,幾乎是表總數的一半。直接查詢耗時3900ms。
id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
---|---|---|---|---|---|---|---|---|---|---|---|
1 | SIMPLE | TM_ACCOUNT | range | PRIMARY,IDX_org_id_combine | PRIMARY | 8 | 704299 | 1.68 | Using where |
-- SQL-3 與SQL-1基本相同,但limit數量減少。
explain
SELECT * from TM_ACCOUNT where org_first_id >= 99 and org_second_id in (1,2,3,60) and BIZ_DATE in ('2100-09-01','2100-09-02') and ACCOUNT_ID > '120306' order by ACCOUNT_ID desc LIMIT 500;
查詢結果:與SQL-1基本相同,但limit數量減少,即查詢條件範圍縮小,劣化成主鍵大範圍掃描。 直接查詢耗時1210ms。
id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
---|---|---|---|---|---|---|---|---|---|---|---|
1 | SIMPLE | TM_ACCOUNT | range | PRIMARY,IDX_org_id_combine | PRIMARY | 8 | 704299 | 0.19 | Using where |
三、復現索引選擇劣化、並嘗試分析OPTIMIZER_TRACE
執行相關命令,獲取OPTIMIZER_TRACE過程。
/* 開啟optimizer_trace,只對本執行緒有效 */
SET optimizer_trace='enabled=on';
#你的sql
-- select ......;
SELECT * from TM_ACCOUNT where org_first_id >= 90 and org_second_id in (1,2,3,60) and BIZ_DATE in ('2100-09-01','2100-09-02') and ACCOUNT_ID > '120306' order by ACCOUNT_ID desc LIMIT 5000;
#檢視最佳化器追蹤鏈
select * from INFORMATION_SCHEMA.OPTIMIZER_TRACE;
#關閉最佳化器追蹤
SET optimizer_trace='enabled=off';
關鍵過程:透過對潛在查詢方式的預估,分別對PRIMARY/IDX_org_id_combine的開銷進行評估,這裡開銷並不僅看掃描行數,還會看排序等情況。可以看到雖然走主鍵索引的行數更多,但總開銷更小。由此可知在【預估】過程,誤導了整個最佳化器。
共有2個潛在選項,分別標出了rowid是否排序、行數rows、預估開銷cost
- PRIMARY,範圍是"120306 < account_id"
- IDX_org_id_combine,範圍是"90 <= org_first_id"
擷取部分OPTIMIZER_TRACE結果,完整json參考附錄1
// 分析可供選擇的範圍條件
"analyzing_range_alternatives": {
"range_scan_alternatives": [
{
"index": "PRIMARY",
"ranges": [
"120306 < account_id"
],
"index_dives_for_eq_ranges": true,
"rowid_ordered": true,
"using_mrr": false,
"index_only": false,
"rows": 704299,
"cost": 141880,
"chosen": true
},
{
"index": "IDX_org_id_combine",
"ranges": [
"90 <= org_first_id"
],
"index_dives_for_eq_ranges": true,
"rowid_ordered": false,
"using_mrr": false,
"index_only": false,
"rows": 295138,
"cost": 354167,
"chosen": false,
"cause": "cost"
}
],
"analyzing_roworder_intersect": {
"usable": false,
"cause": "too_few_roworder_scans"
}
},
// 最終選擇的路徑
"chosen_range_access_summary": {
"range_access_plan": {
"type": "range_scan",
"index": "PRIMARY",
"rows": 704299,
"ranges": [
"120306 < account_id"
]
},
"rows_for_plan": 704299,
"cost_for_plan": 141880,
"chosen": true
}
這裡懷疑是order by ACCOUNT_ID
影響了最佳化器選擇,但通測試發現,即使移除了'order by ACCOUNT_ID desc LIMIT 5000',explain結果仍然是走PRIMARY索引。由此可見,還有些隱藏的資訊,OPTIMIZER_TRACE沒有展示全。這裡暫不深入討論。
explain
SELECT * from TM_ACCOUNT where org_first_id >= 90 and org_second_id in (1,2,3,60) and BIZ_DATE in ('2100-09-01','2100-09-02') and ACCOUNT_ID > '120306' ;
id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
---|---|---|---|---|---|---|---|---|---|---|---|
1 | SIMPLE | TM_ACCOUNT | range | PRIMARY,IDX_org_id_combine | PRIMARY | 8 | 704299 | 1.68 | Using where |
結果:實際查詢耗時912ms。在【## 附錄2 OPTIMIZER_TRACE原始資訊2】中也能看到選擇實際索引,仍然是PRIMARY,與explain結果一致。
四、如何最佳化?
改寫SQL:
- 透過配置、distinct org_first_id等方式,將org_first_id的範圍固定下來,並快取
- 改寫SQL,將
org_first_id >= 90
改寫為org_first_id IN (xxxxx)
下面來看效果
explain
SELECT * from TM_ACCOUNT where org_first_id in ('90','91','92','93','94','95','96','97','98','99') and org_second_id in (1,2,3,60) and BIZ_DATE in ('2100-09-01','2100-09-02') and ACCOUNT_ID > '120306' order by ACCOUNT_ID desc LIMIT 5000;
id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
---|---|---|---|---|---|---|---|---|---|---|---|
1 | SIMPLE | TM_ACCOUNT | range | PRIMARY,IDX_org_id_combine | IDX_org_id_combine | 18 | 5543 | 20.0 | Using index condition; Using where; Using filesort |
結果:實際查詢耗時59ms。explain結果可看到雖然也用了IDX_org_id_combine索引,但仍然是range查詢、回表、filesort,好在掃描行數較少,最終耗時很小。
思考,改寫SQL是最佳解決方案嗎?
隨著資料量的增大,無論多麼簡單的SQL,最終仍然會變慢。
其他方式:
- 資料歸檔。 建立歷史表、大資料抽數歸檔冷資料。
- 引入專門的OLAP系統,不在OLTP系統做複雜的業務查詢。引入ES、hive、HBASE等元件,專業的事交給專業的人去做。
其他
- 開啟optimizer_trace,只對本執行緒有效。建議使用命令列視窗,直連db。透過Navicat等客戶端,可能會記錄失敗。
- 一般optimizer_trace只在root使用者下才能使用
- mariadb直到10.4版本才有Optimizer Trace, 之前的版本執行'SET optimizer_trace='enabled=on'; '會返回錯誤 。官網連結https://mariadb.com/resources/blog/optimizer-trace-in-mariadb-server-10-4/
附錄1 OPTIMIZER_TRACE原始資訊1
以下語句的執行最佳化過程
SELECT * from TM_ACCOUNT where org_first_id >= 90 and org_second_id in (1,2,3,60) and BIZ_DATE in ('2100-09-01','2100-09-02') and ACCOUNT_ID > '120306' order by ACCOUNT_ID desc LIMIT 5000;
{
"steps": [
{
"join_preparation": {
"select#": 1,
"steps": [
{
"IN_uses_bisection": true
},
{
"IN_uses_bisection": true
},
{
"expanded_query": "/* select#1 */ select `tm_account`.`account_id` AS `account_id`,`tm_account`.`name` AS `name`,`tm_account`.`address` AS `address`,`tm_account`.`org_first_id` AS `org_first_id`,`tm_account`.`org_second_id` AS `org_second_id`,`tm_account`.`biz_date` AS `biz_date`,`tm_account`.`last_modify_dt` AS `last_modify_dt` from `tm_account` where ((`tm_account`.`org_first_id` >= 90) and (`tm_account`.`org_second_id` in (1,2,3,60)) and (`tm_account`.`biz_date` in ('2100-09-01','2100-09-02')) and (`tm_account`.`account_id` > 120306)) order by `tm_account`.`account_id` desc limit 5000"
}
]
}
},
{
"join_optimization": {
"select#": 1,
"steps": [
{
"condition_processing": {
"condition": "WHERE",
"original_condition": "((`tm_account`.`org_first_id` >= 90) and (`tm_account`.`org_second_id` in (1,2,3,60)) and (`tm_account`.`biz_date` in ('2100-09-01','2100-09-02')) and (`tm_account`.`account_id` > 120306))",
"steps": [
{
"transformation": "equality_propagation",
"resulting_condition": "((`tm_account`.`org_first_id` >= 90) and (`tm_account`.`org_second_id` in (1,2,3,60)) and (`tm_account`.`biz_date` in ('2100-09-01','2100-09-02')) and (`tm_account`.`account_id` > 120306))"
},
{
"transformation": "constant_propagation",
"resulting_condition": "((`tm_account`.`org_first_id` >= 90) and (`tm_account`.`org_second_id` in (1,2,3,60)) and (`tm_account`.`biz_date` in ('2100-09-01','2100-09-02')) and (`tm_account`.`account_id` > 120306))"
},
{
"transformation": "trivial_condition_removal",
"resulting_condition": "((`tm_account`.`org_first_id` >= 90) and (`tm_account`.`org_second_id` in (1,2,3,60)) and (`tm_account`.`biz_date` in ('2100-09-01','2100-09-02')) and (`tm_account`.`account_id` > 120306))"
}
]
}
},
{
"substitute_generated_columns": {}
},
{
"table_dependencies": [
{
"table": "`tm_account`",
"row_may_be_null": false,
"map_bit": 0,
"depends_on_map_bits": []
}
]
},
{
"ref_optimizer_key_uses": []
},
{
// 行數預估
"rows_estimation": [
{
"table": "`tm_account`",
"range_analysis": {
"table_scan": {
"rows": 1408599,
"cost": 288932
},
"potential_range_indexes": [
{
"index": "PRIMARY",
"usable": true,
"key_parts": [
"account_id"
]
},
{
"index": "IDX_org_id_combine",
"usable": true,
"key_parts": [
"org_first_id",
"org_second_id",
"account_id"
]
},
{
"index": "IDX_last_modify_dt_org_first_id_name",
"usable": false,
"cause": "not_applicable" // 直接標明不適用
}
],
"setup_range_conditions": [],
"group_index_range": {
"chosen": false,
"cause": "not_group_by_or_distinct"
},
// 分析可供選擇的範圍條件
"analyzing_range_alternatives": {
"range_scan_alternatives": [
{
"index": "PRIMARY",
"ranges": [
"120306 < account_id"
],
"index_dives_for_eq_ranges": true,
"rowid_ordered": true,
"using_mrr": false,
"index_only": false,
"rows": 704299,
"cost": 141880,
"chosen": true
},
{
"index": "IDX_org_id_combine",
"ranges": [
"90 <= org_first_id"
],
"index_dives_for_eq_ranges": true,
"rowid_ordered": false,
"using_mrr": false,
"index_only": false,
"rows": 295138,
"cost": 354167,
"chosen": false,
"cause": "cost"
}
],
"analyzing_roworder_intersect": {
"usable": false,
"cause": "too_few_roworder_scans"
}
},
"chosen_range_access_summary": {
"range_access_plan": {
"type": "range_scan",
"index": "PRIMARY",
"rows": 704299,
"ranges": [
"120306 < account_id"
]
},
"rows_for_plan": 704299,
"cost_for_plan": 141880,
"chosen": true
}
}
}
]
},
{
"considered_execution_plans": [
{
"plan_prefix": [],
"table": "`tm_account`",
"best_access_path": {
"considered_access_paths": [
{
"rows_to_scan": 704299,
"access_type": "range",
"range_details": {
"used_index": "PRIMARY"
},
"resulting_rows": 11806,
"cost": 282740,
"chosen": true
}
]
},
"condition_filtering_pct": 100,
"rows_for_plan": 11806,
"cost_for_plan": 282740,
"chosen": true
}
]
},
{
"attaching_conditions_to_tables": {
"original_condition": "((`tm_account`.`org_first_id` >= 90) and (`tm_account`.`org_second_id` in (1,2,3,60)) and (`tm_account`.`biz_date` in ('2100-09-01','2100-09-02')) and (`tm_account`.`account_id` > 120306))",
"attached_conditions_computation": [
{
"table": "`tm_account`",
"rechecking_index_usage": {
"recheck_reason": "low_limit",
"limit": 5000,
"row_estimate": 11806,
"range_analysis": {
"table_scan": {
"rows": 1408599,
"cost": 1690000
},
"potential_range_indexes": [
{
"index": "PRIMARY",
"usable": true,
"key_parts": [
"account_id"
]
},
{
"index": "IDX_org_id_combine",
"usable": false,
"cause": "not_applicable"
},
{
"index": "IDX_last_modify_dt_org_first_id_name",
"usable": false,
"cause": "not_applicable"
}
],
"setup_range_conditions": [],
"group_index_range": {
"chosen": false,
"cause": "cannot_do_reverse_ordering"
},
"analyzing_range_alternatives": {
"range_scan_alternatives": [
{
"index": "PRIMARY",
"ranges": [
"120306 < account_id"
],
"index_dives_for_eq_ranges": true,
"rowid_ordered": true,
"using_mrr": false,
"index_only": false,
"rows": 704299,
"cost": 141880,
"chosen": true
}
]
},
"chosen_range_access_summary": {
"range_access_plan": {
"type": "range_scan",
"index": "PRIMARY",
"rows": 704299,
"ranges": [
"120306 < account_id"
]
},
"rows_for_plan": 704299,
"cost_for_plan": 141880,
"chosen": true
}
}
}
}
],
"attached_conditions_summary": [
{
"table": "`tm_account`",
"attached": "((`tm_account`.`org_first_id` >= 90) and (`tm_account`.`org_second_id` in (1,2,3,60)) and (`tm_account`.`biz_date` in ('2100-09-01','2100-09-02')) and (`tm_account`.`account_id` > 120306))"
}
]
}
},
{
"clause_processing": {
"clause": "ORDER BY",
"original_clause": "`tm_account`.`account_id` desc",
"items": [
{
"item": "`tm_account`.`account_id`"
}
],
"resulting_clause_is_simple": true,
"resulting_clause": "`tm_account`.`account_id` desc"
}
},
{
"reconsidering_access_paths_for_index_ordering": {
"clause": "ORDER BY",
"steps": [],
"index_order_summary": {
"table": "`tm_account`",
"index_provides_order": true,
"order_direction": "desc",
"index": "PRIMARY",
"plan_changed": false
}
}
},
{
"refine_plan": [
{
"table": "`tm_account`"
}
]
}
]
}
},
{
"join_execution": {
"select#": 1,
"steps": []
}
}
]
}
附錄2 OPTIMIZER_TRACE原始資訊2
SELECT * from TM_ACCOUNT where org_first_id >= 90 and org_second_id in (1,2,3,60) and BIZ_DATE in ('2100-09-01','2100-09-02') and ACCOUNT_ID > '120306' ;
{
"steps": [
{
"join_preparation": {
"select#": 1,
"steps": [
{
"IN_uses_bisection": true
},
{
"IN_uses_bisection": true
},
{
"expanded_query": "/* select#1 */ select `tm_account`.`account_id` AS `account_id`,`tm_account`.`name` AS `name`,`tm_account`.`address` AS `address`,`tm_account`.`org_first_id` AS `org_first_id`,`tm_account`.`org_second_id` AS `org_second_id`,`tm_account`.`biz_date` AS `biz_date`,`tm_account`.`last_modify_dt` AS `last_modify_dt` from `tm_account` where ((`tm_account`.`org_first_id` >= 90) and (`tm_account`.`org_second_id` in (1,2,3,60)) and (`tm_account`.`biz_date` in ('2100-09-01','2100-09-02')) and (`tm_account`.`account_id` > 120306))"
}
]
}
},
{
"join_optimization": {
"select#": 1,
"steps": [
{
"condition_processing": {
"condition": "WHERE",
"original_condition": "((`tm_account`.`org_first_id` >= 90) and (`tm_account`.`org_second_id` in (1,2,3,60)) and (`tm_account`.`biz_date` in ('2100-09-01','2100-09-02')) and (`tm_account`.`account_id` > 120306))",
"steps": [
{
"transformation": "equality_propagation",
"resulting_condition": "((`tm_account`.`org_first_id` >= 90) and (`tm_account`.`org_second_id` in (1,2,3,60)) and (`tm_account`.`biz_date` in ('2100-09-01','2100-09-02')) and (`tm_account`.`account_id` > 120306))"
},
{
"transformation": "constant_propagation",
"resulting_condition": "((`tm_account`.`org_first_id` >= 90) and (`tm_account`.`org_second_id` in (1,2,3,60)) and (`tm_account`.`biz_date` in ('2100-09-01','2100-09-02')) and (`tm_account`.`account_id` > 120306))"
},
{
"transformation": "trivial_condition_removal",
"resulting_condition": "((`tm_account`.`org_first_id` >= 90) and (`tm_account`.`org_second_id` in (1,2,3,60)) and (`tm_account`.`biz_date` in ('2100-09-01','2100-09-02')) and (`tm_account`.`account_id` > 120306))"
}
]
}
},
{
"substitute_generated_columns": {}
},
{
"table_dependencies": [
{
"table": "`tm_account`",
"row_may_be_null": false,
"map_bit": 0,
"depends_on_map_bits": []
}
]
},
{
"ref_optimizer_key_uses": []
},
{
"rows_estimation": [
{
"table": "`tm_account`",
"range_analysis": {
"table_scan": {
"rows": 1408599,
"cost": 288932
},
"potential_range_indexes": [
{
"index": "PRIMARY",
"usable": true,
"key_parts": [
"account_id"
]
},
{
"index": "IDX_org_id_combine",
"usable": true,
"key_parts": [
"org_first_id",
"org_second_id",
"account_id"
]
},
{
"index": "IDX_last_modify_dt_org_first_id_name",
"usable": false,
"cause": "not_applicable"
}
],
"setup_range_conditions": [],
"group_index_range": {
"chosen": false,
"cause": "not_group_by_or_distinct"
},
"analyzing_range_alternatives": {
"range_scan_alternatives": [
{
"index": "PRIMARY",
"ranges": [
"120306 < account_id"
],
"index_dives_for_eq_ranges": true,
"rowid_ordered": true,
"using_mrr": false,
"index_only": false,
"rows": 704299,
"cost": 141880,
"chosen": true
},
{
"index": "IDX_org_id_combine",
"ranges": [
"90 <= org_first_id"
],
"index_dives_for_eq_ranges": true,
"rowid_ordered": false,
"using_mrr": false,
"index_only": false,
"rows": 295138,
"cost": 354167,
"chosen": false,
"cause": "cost"
}
],
"analyzing_roworder_intersect": {
"usable": false,
"cause": "too_few_roworder_scans"
}
},
"chosen_range_access_summary": {
"range_access_plan": {
"type": "range_scan",
"index": "PRIMARY",
"rows": 704299,
"ranges": [
"120306 < account_id"
]
},
"rows_for_plan": 704299,
"cost_for_plan": 141880,
"chosen": true
}
}
}
]
},
{
"considered_execution_plans": [
{
"plan_prefix": [],
"table": "`tm_account`",
"best_access_path": {
"considered_access_paths": [
{
"rows_to_scan": 704299,
"access_type": "range",
"range_details": {
"used_index": "PRIMARY"
},
"resulting_rows": 704299,
"cost": 282740,
"chosen": true
}
]
},
"condition_filtering_pct": 100,
"rows_for_plan": 704299,
"cost_for_plan": 282740,
"chosen": true
}
]
},
{
"attaching_conditions_to_tables": {
"original_condition": "((`tm_account`.`org_first_id` >= 90) and (`tm_account`.`org_second_id` in (1,2,3,60)) and (`tm_account`.`biz_date` in ('2100-09-01','2100-09-02')) and (`tm_account`.`account_id` > 120306))",
"attached_conditions_computation": [],
"attached_conditions_summary": [
{
"table": "`tm_account`",
"attached": "((`tm_account`.`org_first_id` >= 90) and (`tm_account`.`org_second_id` in (1,2,3,60)) and (`tm_account`.`biz_date` in ('2100-09-01','2100-09-02')) and (`tm_account`.`account_id` > 120306))"
}
]
}
},
{
"refine_plan": [
{
"table": "`tm_account`"
}
]
}
]
}
},
{
"join_execution": {
"select#": 1,
"steps": []
}
}
]
}
附錄3 java構造資料
public final class JdbcUtils {
private static String url = "jdbc:mysql://localhost:3306/xxxx?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&serverTimezone=GMT%2B8";
private static String user = "root";
private static String password = "123";
private JdbcUtils() {
}
static {
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
throw new ExceptionInInitializerError(e);
}
}
public static void main(String args[]) {
insertBatch();
}
public static void insertBatch() {
Connection conn = null;
PreparedStatement pst = null;
ResultSet rs = null;
try {
String sql = "INSERT into TM_ACCOUNT values(?,?,?,?,?,?,?);";
// 1. 獲取連結,預處理語句
conn = getConnection();
conn.setAutoCommit(false);
pst = conn.prepareStatement(sql);
// 2. 開始插入,總插入150萬
Random random = new Random();
int a_id_start = 1;
for (int i = 0; i < 5 * 150; i++) {
// 每2000條執行一次批次插入
for (int loop = 0; loop < 2000; loop++) {
a_id_start++;
pst.setInt(1, a_id_start);
pst.setString(2, "name-" + a_id_start);
pst.setString(3, RandomString.make(20));
pst.setInt(4, random.nextInt(100));
pst.setInt(5, random.nextInt(100));
pst.setDate(6, new Date(200, 8, random.nextInt(25) + 1));
pst.setDate(7, new Date(200, 8, random.nextInt(25) + 1));
pst.addBatch();
}
pst.executeBatch();
conn.commit();
System.out.println(" done !!!!!!" + i);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
free(rs, pst, conn);
}
}
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(url, user, password);
}
public static void free(ResultSet rs, Statement st, Connection conn) {
try {
if (rs != null)
rs.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (st != null)
st.close();
} catch (Exception e2) {
e2.printStackTrace();
} finally {
try {
conn.close();
} catch (Exception e3) {
e3.printStackTrace();
}
}
}
}
}