先說結論,若是你的MySQL數據庫運行在一個高併發的環境下,那麼MySQL8優點很大,升級到MySQL8是一個很好的選擇;但若是你的MySQL運行環境是低併發的,那麼MySQL8優點並不明顯,我的建議不要如今就升級到MySQL8,能夠等一等。java
本文針對的是低併發環境下的MySQL8與MySQL5的性能比較。mysql
根據網上一些使用sysbench
作的MySQL8的性能基準測試的結果來看,MySQL8
相對MySQL5
的性能優點更多體如今高併發環境(如鏈接數達到1024甚至2048)下,單位時間處理數量(例如InnoDB
處理行數或處理事務數量)的極大提升。即,高併發下的TPS指標,MySQL8
相對MySQL5
有很大的優點。git
能夠參考這篇文章 : MySQL Performance Benchmarking: MySQL 5.7 vs MySQL 8.0
但實際的生產環境上,也有不少系統並未運行在高併發環境下,它們的數據庫鏈接數每每不會超過默認的最大鏈接數151
,它們甚至不須要獨立的MySQL服務器。對於這種場景,生產環境上是否有必要將MySQL升級到8呢?github
本文針對MySQL5.7.28
與MySQL8.0.22
的docker鏡像版本,在各自都沒有作性能優化配置的基礎上,在相同的宿主機環境下,在相同的表結構與相同的數據量下,對它們進行了一些徹底相同的,單個鏈接上的性能測試,並對其進行數據統計與分析。sql
即,本文考慮的不是高併發環境下的性能表現,而是低併發環境下,單個鏈接上的性能表現。此時主要關注各類SQL操做的耗時和資源消耗。docker
對單個鏈接的性能測試結果進行統計分析以後,得出如下結論:數據庫
hash join
的支持,對於鏈接字段上沒有任何索引的多表鏈接查詢,MySQL8具備壓倒性的性能優點。對於低併發環境來講,MySQL8對性能的最大提高來自於哈希鏈接的支持。但實際上由於索引的存在,實際能用到哈希鏈接的場景並非那麼多。尤爲是已經穩定運行了一段時間的生產環境上,若是鏈接字段上徹底沒有索引且數據量較大的話,性能問題應該早就暴露出來了;並且MySQL8的版本還在不停迭代升級中,一些功能的兼容性還不是很穩定(有些功能在8.0.x較早的版本里支持,後續更高一點版本又不支持了)。安全
所以對於低併發的生產環境,我的建議:性能優化
簡而言之一句話,生產上先等等,等到8.1
版本之後再看看。服務器
至於開發或者測試環境,能夠嘗試一下,作一些技術準備。
本文的性能比較主要看各類操做的耗時(或者說響應時間),以及在操做執行期間的資源(CPU與內存)消耗。
如下耗時統計與資源消耗統計均基於本地測試環境與測試數據,不能表明廣泛的性能表現。只能用於相同環境與數據下Mysql5與8的性能比較。
對MySQL8與MySQL5分別進行了如下操做:
操做 | 操做說明 | mysql8耗時 | mysql5耗時 |
---|---|---|---|
JDBC鏈接 | - | 3毫秒 | 2毫秒 |
大表寫入 | 100萬條記錄分批插入,每批1000條 | 30秒+ | 20秒+ |
大表掃描 | 單表100萬記錄,無條件的全表掃描 | 1秒+ | 1秒+ |
索引查詢 | 單表100萬記錄,普通Btree索引,等值條件查詢,命中率1% | 0.02~0.05秒 | 0.02~0.05秒 |
索引鏈接 | 百萬記錄表與十萬記錄錶鏈接,鏈接字段是惟一索引 | 33秒+ | 28秒+ |
無索引鏈接1 | 百萬記錄表與一萬記錄錶鏈接,鏈接字段無索引 | 2秒+ | 半小時左右 |
無索引鏈接2 | 百萬記錄表與100記錄錶鏈接,鏈接字段無索引 | 1.5秒+ | 17秒+ |
獨立子查詢 | 100記錄表做爲百萬記錄表的IN 條件子查詢 |
0.8秒+ | 14秒+ |
關聯子查詢 | 100記錄表做爲百萬記錄表的EXISTS 條件子查詢 |
0.8秒+ | 18秒+ |
倒序排序 | 百萬記錄表創建正序倒序混合索引,並使用它排序 | 0.4秒+ | 1.3秒+ |
注意:
無索引鏈接1
,無索引鏈接2
,獨立子查詢
以及關聯子查詢
中,mysql8優點明顯的緣由都是哈希鏈接的支持。就是說,這幾個測試案例中的表的鏈接字段都是沒有索引的字段。關聯子查詢
在MySQL8中還多一個半鏈接優化,但優點不明顯。在測試過程當中,對CPU與內存消耗進行了簡單的統計,結果以下:
項目 | mysql8 | mysql5 |
---|---|---|
批量寫入百萬數據過程當中的CPU使用率(%) | 90 | 70 |
各類查詢過程當中的CPU使用率(%) | 100 | 100 |
mysql容器重啓以後內存使用量(M) | 341.2 | 205.9 |
各類操做以後mysql容器內存使用漲幅(M) | 130 | 110 |
由此能夠得出的初步結論:
簡而言之,MySQL8比MySQL5更消耗CPU與內存資源。
本次測試使用docker鏡像,在本地啓動了兩個mysql容器,均沒有資源限制,也沒有特殊的性能優化配置。
[client] default-character-set=utf8mb4 [mysqld] character-set-client-handshake = FALSE character-set-server = utf8mb4 collation-server = utf8mb4_unicode_ci transaction_isolation = READ-COMMITTED [mysql] default-character-set=utf8mb4
[mysqld] pid-file = /var/run/mysqld/mysqld.pid socket = /var/run/mysqld/mysqld.sock datadir = /var/lib/mysql secure-file-priv= NULL default_authentication_plugin = mysql_native_password transaction_isolation = READ-COMMITTED
分別在MySQL5與MySQL8的實例中建立以下數據庫與表:
CREATE DATABASE `db_mysql_test1` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci ; USE `db_mysql_test1`; DROP TABLE IF EXISTS `db_mysql_test1`.`tb_order`; CREATE TABLE IF NOT EXISTS `db_mysql_test1`.`tb_order` ( `id` INT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'ID', `ord_number` varchar(20) NOT NULL COMMENT '訂單編號', `custom_number` varchar(20) NOT NULL COMMENT '客戶編號', `product_number` varchar(20) NOT NULL COMMENT '商品編號', `warehouse_number` varchar(20) NOT NULL COMMENT '倉庫編號', `ord_status` tinyint NOT NULL COMMENT '訂單狀態', `order_time` datetime NOT NULL COMMENT '下單時間', PRIMARY KEY (`id`), UNIQUE KEY `tb_order_unique01` (`ord_number`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT = '訂單表'; DROP TABLE IF EXISTS `db_mysql_test1`.`tb_custom`; CREATE TABLE IF NOT EXISTS `db_mysql_test1`.`tb_custom` ( `id` INT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'ID', `custom_number` varchar(20) NOT NULL COMMENT '客戶編號', `custom_name` varchar(50) NOT NULL COMMENT '客戶姓名', `custom_phone` varchar(20) NOT NULL COMMENT '客戶手機號', `custom_address` varchar(200) NOT NULL COMMENT '客戶地址', PRIMARY KEY (`id`), UNIQUE KEY `tb_custom_unique01` (`custom_number`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT = '客戶表'; DROP TABLE IF EXISTS `db_mysql_test1`.`tb_product`; CREATE TABLE IF NOT EXISTS `db_mysql_test1`.`tb_product` ( `id` INT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'ID', `product_number` varchar(20) NOT NULL COMMENT '商品編號', `product_name` varchar(50) NOT NULL COMMENT '商品名稱', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT = '商品表'; DROP TABLE IF EXISTS `db_mysql_test1`.`tb_warehouse`; CREATE TABLE IF NOT EXISTS `db_mysql_test1`.`tb_warehouse` ( `id` INT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'ID', `warehouse_number` varchar(20) NOT NULL COMMENT '倉庫編號', `warehouse_name` varchar(50) NOT NULL COMMENT '倉庫名稱', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT = '倉庫表';
本文開發了一個簡單的java應用程序用於導入數據,並執行部分查詢操做。
由於大部分應用開發會用到JDBC驅動,而MySQL8相對於MySQL5也提供了一個全新的驅動包。所以咱們也須要考慮不一樣版本驅動包的影響。
測試程序代碼能夠從github或gitee自行拉取:
https://github.com/zhaochunin...
或
https://gitee.com/XiaTangShao...
運行mysql-test
的注意事項:
pom
中的mysql-connector-java
依賴版本修改成對應版本,而後將MySQLPerformanceTest
中的JDBC_URL
,JDBC_USER
與JDBC_PWD
設置爲對應的值。注意不要忘記給JDBC_URL
的參數添加rewriteBatchedStatements=true
,缺乏此參數的話,PreparedStatement
的executeBatch
將沒法生效。truncate
截斷相關表。)分別對MySQL8
和MySQL5
進行了如下性能測試,並統計結果以下:
根據mysql-test
程序測試結果,MySQL8
和MySQL5
的JDBC鏈接時間基本相同。
MySQL8
的JDBC驅動包版本爲8.0.22
,對應的Driver Class是com.mysql.cj.jdbc.Driver
。MySQL5
的JDBC驅動包版本爲5.1.47
,對應的Driver Class是com.mysql.jdbc.Driver
。某些資料上說MySQL8若是用5.X的JDBC驅動會有性能問題。這裏沒有測試這種案例,正常來講,應用程序也應該會升級JDBC驅動,不然會出警告。
參考 mysql-test
程序的insertOrder
方法,向tb_order
表分批插入了100萬條數據,每1000條插入一次。
能夠看到,mysql8在寫入數據時,會消耗更多的CPU,耗時也更多一點。
MySQL8可能須要性能相關配置上作一些優化。
參考 mysql-test
程序的selectOrders
方法,對一張100萬數據的表tb_order
作了一次無條件查詢,MySQL作了全表掃描,mysql8與mysql5的執行計劃是同樣的。
能夠看到,二者耗時和CPU消耗基本相同,MySQL8在耗時上略佔優點。
爲表tb_order
建立一個普通索引,並在一個等值查詢中使用它。
CREATE INDEX `tb_order_idx02` ON `db_mysql_test1`.`tb_order` (`warehouse_number`, `product_number`);
mysql5中執行:
-- 耗時大約0.04秒 SELECT * FROM tb_order where warehouse_number = 'whs_0000000075'; mysql> explain SELECT * FROM tb_order where warehouse_number = 'whs_0000000075'; +----+-------------+----------+------------+------+----------------+----------------+---------+-------+-------+----------+-------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+----------+------------+------+----------------+----------------+---------+-------+-------+----------+-------+ | 1 | SIMPLE | tb_order | NULL | ref | tb_order_idx02 | tb_order_idx02 | 82 | const | 19526 | 100.00 | NULL | +----+-------------+----------+------------+------+----------------+----------------+---------+-------+-------+----------+-------+ 1 row in set, 1 warning (0.01 sec)
mysql8中執行:
-- 耗時大約0.05秒 SELECT * FROM tb_order where warehouse_number = 'whs_0000000075'; mysql> explain SELECT * FROM tb_order where warehouse_number = 'whs_0000000075'; +----+-------------+----------+------------+------+----------------+----------------+---------+-------+-------+----------+-------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+----------+------------+------+----------------+----------------+---------+-------+-------+----------+-------+ | 1 | SIMPLE | tb_order | NULL | ref | tb_order_idx02 | tb_order_idx02 | 82 | const | 19526 | 100.00 | NULL | +----+-------------+----------+------------+------+----------------+----------------+---------+-------+-------+----------+-------+ 1 row in set, 1 warning (0.00 sec)
可見,對於普通索引查詢來講,mysql5與mysql8性能表現基本一致。
參考 mysql-test
程序的selectOrderJoinCustom
方法,對一張100萬數據的表和一張10萬數據的表作鏈接查詢,鏈接字段上創建了惟一索引。此時,不管MySQL8仍是MySQL5,其優化器都會選擇索引鏈接策略。
能夠看到,二者CPU消耗基本相同,但MySQL8在耗時上略多於MySQL5。
執行計劃一致,表結構與數據量也一致,MySQL8卻慢一點,仍是須要在性能相關配置上作一些優化。
查看二者的執行計劃可知,二者都採用了索引鏈接:將tb_order
做爲主表,遍歷其結果集的每條記錄,再使用鏈接字段上的惟一索引tb_custom_unique01
從表tb_custom
中查找對應記錄。即,Nested loop
+ eq_ref
。
mysql8的執行計劃:
mysql> explain SELECT a.ord_number, a.ord_status, a.order_time, b.custom_number, b.custom_name FROM tb_order a inner join tb_custom b on(a.custom_number = b.custom_number); +----+-------------+-------+------------+--------+--------------------+--------------------+---------+--------------------------------+--------+----------+-------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+--------+--------------------+--------------------+---------+--------------------------------+--------+----------+-------+ | 1 | SIMPLE | a | NULL | ALL | NULL | NULL | NULL | NULL | 994365 | 100.00 | NULL | | 1 | SIMPLE | b | NULL | eq_ref | tb_custom_unique01 | tb_custom_unique01 | 82 | db_mysql_test1.a.custom_number | 1 | 100.00 | NULL | +----+-------------+-------+------------+--------+--------------------+--------------------+---------+--------------------------------+--------+----------+-------+ 2 rows in set, 1 warning (0.00 sec) mysql> mysql> explain format=tree SELECT a.ord_number, a.ord_status, a.order_time, b.custom_number, b.custom_name FROM tb_order a inner join tb_custom b on(a.custom_number = b.custom_number); +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | EXPLAIN | +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | -> Nested loop inner join (cost=616793.16 rows=994365) -> Table scan on a (cost=100902.25 rows=994365) -> Single-row index lookup on b using tb_custom_unique01 (custom_number=a.custom_number) (cost=0.42 rows=1) | +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ 1 row in set (0.00 sec)
explain format=tree [SQL語句]
是MySQL8.0.21版本開始新增的語法,能夠查看到一些額外的詳細的執行計劃信息。
mysql5的執行計劃:
mysql> explain SELECT a.ord_number, a.ord_status, a.order_time, b.custom_number, b.custom_name FROM tb_order a inner join tb_custom b on(a.custom_number = b.custom_number); +----+-------------+-------+------------+--------+--------------------+--------------------+---------+--------------------------------+--------+----------+-------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+--------+--------------------+--------------------+---------+--------------------------------+--------+----------+-------+ | 1 | SIMPLE | a | NULL | ALL | NULL | NULL | NULL | NULL | 994365 | 100.00 | NULL | | 1 | SIMPLE | b | NULL | eq_ref | tb_custom_unique01 | tb_custom_unique01 | 82 | db_mysql_test1.a.custom_number | 1 | 100.00 | NULL | +----+-------------+-------+------------+--------+--------------------+--------------------+---------+--------------------------------+--------+----------+-------+ 2 rows in set, 1 warning (0.00 sec)
參考 mysql-test
程序的selectOrderJoinProduct
方法與selectOrderJoinWarehouse
方法,這裏分別對下面兩種數據量的案例作了無索引鏈接的測試:
selectOrderJoinProduct
方法,章節1.3.1 耗時比較
中的無索引鏈接1
。selectOrderJoinWarehouse
方法,章節1.3.1 耗時比較
中的無索引鏈接2
。此時MySQL8的性能優點極大:
爲什麼鏈接字段無索引的狀況下,MySQL8的優點如此巨大?這就是MySQL8開始支持的哈希鏈接的功勞了。
selectOrderJoinProduct
在mysql8的執行計劃:
mysql> explain SELECT a.ord_number, a.ord_status, a.order_time, b.product_number, b.product_name FROM tb_order a inner join tb_product b on(a.product_number = b.product_number); +----+-------------+-------+------------+------+---------------+------+---------+------+--------+----------+--------------------------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+------+---------------+------+---------+------+--------+----------+--------------------------------------------+ | 1 | SIMPLE | b | NULL | ALL | NULL | NULL | NULL | NULL | 9999 | 100.00 | NULL | | 1 | SIMPLE | a | NULL | ALL | NULL | NULL | NULL | NULL | 994365 | 10.00 | Using where; Using join buffer (hash join) | +----+-------------+-------+------------+------+---------------+------+---------+------+--------+----------+--------------------------------------------+ 2 rows in set, 1 warning (0.00 sec) mysql> mysql> explain format=tree SELECT a.ord_number, a.ord_status, a.order_time, b.product_number, b.product_name FROM tb_order a inner join tb_product b on(a.product_number = b.product_number); +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | EXPLAIN | +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | -> Inner hash join (a.product_number = b.product_number) (cost=994283853.13 rows=994265578) -> Table scan on a (cost=2.72 rows=994365) -> Hash -> Table scan on b (cost=1057.73 rows=9999) | +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ 1 row in set (0.00 sec)
selectOrderJoinProduct
在mysql5的執行計劃:
mysql> explain SELECT a.ord_number, a.ord_status, a.order_time, b.product_number, b.product_name FROM tb_order a inner join tb_product b on(a.product_number = b.product_number); +----+-------------+-------+------------+------+---------------+------+---------+------+--------+----------+----------------------------------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+------+---------------+------+---------+------+--------+----------+----------------------------------------------------+ | 1 | SIMPLE | b | NULL | ALL | NULL | NULL | NULL | NULL | 9999 | 100.00 | NULL | | 1 | SIMPLE | a | NULL | ALL | NULL | NULL | NULL | NULL | 994365 | 10.00 | Using where; Using join buffer (Block Nested Loop) | +----+-------------+-------+------------+------+---------------+------+---------+------+--------+----------+----------------------------------------------------+ 2 rows in set, 1 warning (0.00 sec)
咱們能夠清楚的看到,MySQL8使用的是hash join
,而MySQL5使用的是Block Nested Loop
,塊嵌套循環,BNL,該策略從MySQL 8.0.20開始再也不使用。
hash join
就是將較小的那張表的數據集作成哈希數據集,而後遍歷較大的表的數據集,對每條記錄,根據鏈接字段直接從哈希數據集中獲取小表對應記錄。其時間複雜度爲O(m+n)
,m與n分別是大表與小表的數據量。
BNL
就是雙層嵌套循環,一般將小表做爲主表,遍歷其數據集,對每條記錄再遍歷大表數據集查找對應記錄。其時間複雜度爲O(m*n)
。即便鏈接的兩張表有其餘非鏈接字段上的過濾條件,且有索引可使用,大部分狀況下也依然是
hash join
效率更高。
在MySQL中執行如下使用IN
的獨立子查詢,並查看其執行計劃:
SELECT ord_number, warehouse_number from tb_order where warehouse_number in (SELECT warehouse_number from tb_warehouse); explain SELECT ord_number, warehouse_number from tb_order where warehouse_number in (SELECT warehouse_number from tb_warehouse); show warnings;
注意查看完執行計劃以後,要當即執行show warnings;
,否則看不到semi join
半鏈接優化。
查看執行結果,MySQL8優點極大。查看執行計劃會發現,緣由仍是哈希鏈接的使用。
mysql5的執行結果及其執行計劃:
-- 14.69秒 SELECT ord_number, warehouse_number from tb_order where warehouse_number in (SELECT warehouse_number from tb_warehouse); mysql> explain SELECT ord_number, warehouse_number from tb_order where warehouse_number in (SELECT warehouse_number from tb_warehouse); +----+--------------+--------------+------------+------+---------------+------+---------+------+--------+----------+----------------------------------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+--------------+--------------+------------+------+---------------+------+---------+------+--------+----------+----------------------------------------------------+ | 1 | SIMPLE | <subquery2> | NULL | ALL | NULL | NULL | NULL | NULL | NULL | 100.00 | NULL | | 1 | SIMPLE | tb_order | NULL | ALL | NULL | NULL | NULL | NULL | 994365 | 10.00 | Using where; Using join buffer (Block Nested Loop) | | 2 | MATERIALIZED | tb_warehouse | NULL | ALL | NULL | NULL | NULL | NULL | 100 | 100.00 | NULL | +----+--------------+--------------+------------+------+---------------+------+---------+------+--------+----------+----------------------------------------------------+ 3 rows in set, 1 warning (0.00 sec) mysql> show warnings; +-------+------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | Level | Code | Message | +-------+------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | Note | 1003 | /* select#1 */ select `db_mysql_test1`.`tb_order`.`ord_number` AS `ord_number`,`db_mysql_test1`.`tb_order`.`warehouse_number` AS `warehouse_number` from `db_mysql_test1`.`tb_order` semi join (`db_mysql_test1`.`tb_warehouse`) where (`db_mysql_test1`.`tb_order`.`warehouse_number` = `<subquery2>`.`warehouse_number`) | +-------+------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ 1 row in set (0.00 sec)
能夠看到,對於使用IN
的獨立子查詢,MySQL5選擇了Semi-join
半鏈接和Materialization
物化的優化策略,將子查詢改成半鏈接,並物化爲臨時表。但並非說作了半鏈接物化優化就必定更快,優化器會根據具體的表統計信息(表結構與表數據量等)估算並比較不一樣的優化策略,選擇一個估算性能表現最好的策略。
同時咱們應該注意到,雖然
IN
語句作了必定的優化,但tb_order
與物化的臨時表之間鏈接方式依然是Block Nested Loop
,該語句依然較慢的緣由主要是這個。
mysql8的執行結果及其執行計劃:
-- 0.84秒 SELECT ord_number, warehouse_number from tb_order where warehouse_number in (SELECT warehouse_number from tb_warehouse); mysql> explain SELECT ord_number, warehouse_number from tb_order where warehouse_number in (SELECT warehouse_number from tb_warehouse); +----+--------------+--------------+------------+------+---------------+------+---------+------+--------+----------+--------------------------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+--------------+--------------+------------+------+---------------+------+---------+------+--------+----------+--------------------------------------------+ | 1 | SIMPLE | <subquery2> | NULL | ALL | NULL | NULL | NULL | NULL | NULL | 100.00 | NULL | | 1 | SIMPLE | tb_order | NULL | ALL | NULL | NULL | NULL | NULL | 994365 | 10.00 | Using where; Using join buffer (hash join) | | 2 | MATERIALIZED | tb_warehouse | NULL | ALL | NULL | NULL | NULL | NULL | 100 | 100.00 | NULL | +----+--------------+--------------+------------+------+---------------+------+---------+------+--------+----------+--------------------------------------------+ 3 rows in set, 1 warning (0.00 sec) mysql> mysql> show warnings; +-------+------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | Level | Code | Message | +-------+------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | Note | 1003 | /* select#1 */ select `db_mysql_test1`.`tb_order`.`ord_number` AS `ord_number`,`db_mysql_test1`.`tb_order`.`warehouse_number` AS `warehouse_number` from `db_mysql_test1`.`tb_order` semi join (`db_mysql_test1`.`tb_warehouse`) where (`db_mysql_test1`.`tb_order`.`warehouse_number` = `<subquery2>`.`warehouse_number`) | +-------+------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ 1 row in set (0.00 sec)
MySQL8也作了半鏈接semi join
和物化MATERIALIZED
優化,但再也不使用BNL,而是換成了hash join
。
在MySQL中執行如下使用EXISTS
的關聯子查詢,並查看其執行計劃:
SELECT ord_number, warehouse_number from tb_order where EXISTS (SELECT * from tb_warehouse where warehouse_number = tb_order.warehouse_number ); explain SELECT ord_number, warehouse_number from tb_order where EXISTS (SELECT * from tb_warehouse where warehouse_number = tb_order.warehouse_number ); show warnings;
注意查看完執行計劃以後,要當即執行show warnings;
,否則看不到semi join
半鏈接優化。
查看執行結果,MySQL8優點極大。查看執行計劃會發現,緣由主要是對EXISTS
子句進行半鏈接+物化
優化後可使用哈希鏈接。
mysql5中的執行結果和執行計劃:
-- 18.02秒+ SELECT ord_number, warehouse_number from tb_order where EXISTS (SELECT * from tb_warehouse where warehouse_number = tb_order.warehouse_number ); mysql> explain SELECT ord_number, warehouse_number from tb_order where EXISTS (SELECT * from tb_warehouse where warehouse_number = tb_order.warehouse_number ); +----+--------------------+--------------+------------+------+---------------+------+---------+------+--------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+--------------------+--------------+------------+------+---------------+------+---------+------+--------+----------+-------------+ | 1 | PRIMARY | tb_order | NULL | ALL | NULL | NULL | NULL | NULL | 994365 | 100.00 | Using where | | 2 | DEPENDENT SUBQUERY | tb_warehouse | NULL | ALL | NULL | NULL | NULL | NULL | 100 | 10.00 | Using where | +----+--------------------+--------------+------------+------+---------------+------+---------+------+--------+----------+-------------+ 2 rows in set, 2 warnings (0.00 sec) mysql> show warnings; +-------+------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | Level | Code | Message | +-------+------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | Note | 1276 | Field or reference 'db_mysql_test1.tb_order.warehouse_number' of SELECT #2 was resolved in SELECT #1 | | Note | 1003 | /* select#1 */ select `db_mysql_test1`.`tb_order`.`ord_number` AS `ord_number`,`db_mysql_test1`.`tb_order`.`warehouse_number` AS `warehouse_number` from `db_mysql_test1`.`tb_order` where exists(/* select#2 */ select 1 from `db_mysql_test1`.`tb_warehouse` where (`db_mysql_test1`.`tb_warehouse`.`warehouse_number` = `db_mysql_test1`.`tb_order`.`warehouse_number`)) | +-------+------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ 2 rows in set (0.00 sec)
能夠看到,對於使用EXISTS
的關聯子查詢,MySQL5沒有作Semi-join Materialization
優化,相比IN
語句性能略有不如。
mysql8中的執行結果和執行計劃:
-- 0.83秒+ SELECT ord_number, warehouse_number from tb_order where EXISTS (SELECT * from tb_warehouse where warehouse_number = tb_order.warehouse_number ); mysql> explain SELECT ord_number, warehouse_number from tb_order where EXISTS (SELECT * from tb_warehouse where warehouse_number = tb_order.warehouse_number ); +----+--------------+--------------+------------+------+---------------+------+---------+------+--------+----------+--------------------------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+--------------+--------------+------------+------+---------------+------+---------+------+--------+----------+--------------------------------------------+ | 1 | SIMPLE | <subquery2> | NULL | ALL | NULL | NULL | NULL | NULL | NULL | 100.00 | NULL | | 1 | SIMPLE | tb_order | NULL | ALL | NULL | NULL | NULL | NULL | 994365 | 10.00 | Using where; Using join buffer (hash join) | | 2 | MATERIALIZED | tb_warehouse | NULL | ALL | NULL | NULL | NULL | NULL | 100 | 100.00 | NULL | +----+--------------+--------------+------------+------+---------------+------+---------+------+--------+----------+--------------------------------------------+ 3 rows in set, 2 warnings (0.00 sec) mysql> mysql> show warnings; +-------+------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | Level | Code | Message | +-------+------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | Note | 1276 | Field or reference 'db_mysql_test1.tb_order.warehouse_number' of SELECT #2 was resolved in SELECT #1 | | Note | 1003 | /* select#1 */ select `db_mysql_test1`.`tb_order`.`ord_number` AS `ord_number`,`db_mysql_test1`.`tb_order`.`warehouse_number` AS `warehouse_number` from `db_mysql_test1`.`tb_order` semi join (`db_mysql_test1`.`tb_warehouse`) where (`db_mysql_test1`.`tb_order`.`warehouse_number` = `<subquery2>`.`warehouse_number`) | +-------+------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ 2 rows in set (0.00 sec)
性能相比mysql5有極大提高,但咱們要注意,該案例性能提高的最主要緣由是因爲半鏈接優化,致使可以使用
hash join
了。
MySQL8真正支持建立倒序索引,而不是之前那樣僞裝建立倒序索引,但實際仍是正序索引。這使得某些場景下性能有所提高。
好比這樣的案例,對tb_order
表查詢時,使用custom_number
與product_number
排序,其中product_number
須要倒序。正常來講,應該建立下面的索引:
CREATE INDEX `tb_order_idx01` ON `db_mysql_test1`.`tb_order` (`custom_number`, `product_number` DESC);
但一樣的索引,在MySQL8中生效,有效提升了性能;而在MySQL5中並未生效,性能依然不高。
mysql5中執行:
-- 刪除倒序索引 mysql> alter table tb_order drop index tb_order_idx01; mysql> explain SELECT custom_number, product_number from tb_order order by custom_number, product_number DESC ; +----+-------------+----------+------------+------+---------------+------+---------+------+--------+----------+----------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+----------+------------+------+---------------+------+---------+------+--------+----------+----------------+ | 1 | SIMPLE | tb_order | NULL | ALL | NULL | NULL | NULL | NULL | 994365 | 100.00 | Using filesort | +----+-------------+----------+------------+------+---------------+------+---------+------+--------+----------+----------------+ 1 row in set, 1 warning (0.00 sec) -- 建立倒序索引,自己也是組合索引,部分升序,部分降序 CREATE INDEX `tb_order_idx01` ON `db_mysql_test1`.`tb_order` (`custom_number`, `product_number` DESC); mysql> explain SELECT custom_number, product_number from tb_order order by custom_number, product_number DESC ; +----+-------------+----------+------------+-------+---------------+----------------+---------+------+--------+----------+-----------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+----------+------------+-------+---------------+----------------+---------+------+--------+----------+-----------------------------+ | 1 | SIMPLE | tb_order | NULL | index | NULL | tb_order_idx01 | 164 | NULL | 994365 | 100.00 | Using index; Using filesort | +----+-------------+----------+------------+-------+---------------+----------------+---------+------+--------+----------+-----------------------------+ 1 row in set, 1 warning (0.00 sec) -- 查詢100萬條數據須要 1.34秒 mysql> SELECT custom_number, product_number from tb_order order by custom_number, product_number DESC ;
- 建立倒序索引前,使用
filesort
,性能一般比使用index
要低。- 建立倒序索引後,只有正序字段使用
index
,倒序部分依然要使用filesort
,由於MySQL5的倒序索引是假的。
mysql8中執行:
-- 刪除倒序索引 mysql> alter table tb_order drop index tb_order_idx01; mysql> explain SELECT custom_number, product_number from tb_order order by custom_number, product_number DESC ; +----+-------------+----------+------------+------+---------------+------+---------+------+--------+----------+----------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+----------+------------+------+---------------+------+---------+------+--------+----------+----------------+ | 1 | SIMPLE | tb_order | NULL | ALL | NULL | NULL | NULL | NULL | 994365 | 100.00 | Using filesort | +----+-------------+----------+------------+------+---------------+------+---------+------+--------+----------+----------------+ 1 row in set, 1 warning (0.00 sec) -- 建立倒序索引 CREATE INDEX `tb_order_idx01` ON `db_mysql_test1`.`tb_order` (`custom_number`, `product_number` DESC); mysql> explain SELECT custom_number, product_number from tb_order order by custom_number, product_number DESC ; +----+-------------+----------+------------+-------+---------------+----------------+---------+------+--------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+----------+------------+-------+---------------+----------------+---------+------+--------+----------+-------------+ | 1 | SIMPLE | tb_order | NULL | index | NULL | tb_order_idx01 | 164 | NULL | 100000 | 100.00 | Using index | +----+-------------+----------+------------+-------+---------------+----------------+---------+------+--------+----------+-------------+ 1 row in set, 1 warning (0.00 sec) -- 查詢100萬條數據須要 0.44秒 mysql> SELECT custom_number, product_number from tb_order order by custom_number, product_number DESC ;
- 建立倒序索引前,使用
filesort
,性能一般比使用index
要低。- 建立倒序索引後,所有使用
index
,倒序索引生效。
如今咱們知道,哈希鏈接只在鏈接字段上沒有任何索引時起效,大部分業務場景裏,鏈接字段上都是有各類索引的,這時Mysql使用的是索引鏈接
,即,遍歷主表數據結果集,對每一條記錄,使用索引去副表結果集中查找。即,Nested Loop + 索引
。注意,這不是Block Nested Loop
BNL塊嵌套循環,BNL是之前的Mysql在鏈接字段上沒有索引時採用的鏈接策略。
目前mysql在鏈接字段上有索引的狀況下,默認使用索引鏈接。但這並非說索引鏈接就必定比哈希鏈接快。這取決於具體的數據量和表結構。
<!-- ### 4.10.1 MySQL8的哈希鏈接有時比MySQL5的索引鏈接快
mysql5中的表現:
-- tb_order有100萬數據,tb_product有1萬數據,耗時在半小時左右 SELECT a.ord_number, a.ord_status, a.order_time, b.product_number, b.product_name FROM tb_order a inner join tb_product b on(a.product_number = b.product_number); mysql> explain SELECT a.ord_number, a.ord_status, a.order_time, b.product_number, b.product_name FROM tb_order a inner join tb_product b on(a.product_number = b.product_number); +----+-------------+-------+------------+------+---------------+------+---------+------+--------+----------+----------------------------------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+------+---------------+------+---------+------+--------+----------+----------------------------------------------------+ | 1 | SIMPLE | b | NULL | ALL | NULL | NULL | NULL | NULL | 9999 | 100.00 | NULL | | 1 | SIMPLE | a | NULL | ALL | NULL | NULL | NULL | NULL | 994365 | 10.00 | Using where; Using join buffer (Block Nested Loop) | +----+-------------+-------+------------+------+---------------+------+---------+------+--------+----------+----------------------------------------------------+ 2 rows in set, 1 warning (0.00 sec) -- 添加惟一索引 CREATE UNIQUE INDEX `tb_product_unique01` ON `db_mysql_test1`.`tb_product` (`product_number`); mysql> explain SELECT a.ord_number, a.ord_status, a.order_time, b.product_number, b.product_name FROM tb_order a inner join tb_product b on(a.product_number = b.product_number); +----+-------------+-------+------------+--------+---------------------+---------------------+---------+---------------------------------+--------+----------+-------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+--------+---------------------+---------------------+---------+---------------------------------+--------+----------+-------+ | 1 | SIMPLE | a | NULL | ALL | NULL | NULL | NULL | NULL | 994365 | 100.00 | NULL | | 1 | SIMPLE | b | NULL | eq_ref | tb_product_unique01 | tb_product_unique01 | 82 | db_mysql_test1.a.product_number | 1 | 100.00 | NULL | +----+-------------+-------+------------+--------+---------------------+---------------------+---------+---------------------------------+--------+----------+-------+ 2 rows in set, 1 warning (0.00 sec) -- 2.26秒 SELECT a.ord_number, a.ord_status, a.order_time, b.product_number, b.product_name FROM tb_order a inner join tb_product b on(a.product_number = b.product_number);
mysql8中的表現:
-- tb_order有100萬數據,tb_product有1萬數據,耗時在1.3秒左右 SELECT a.ord_number, a.ord_status, a.order_time, b.product_number, b.product_name FROM tb_order a inner join tb_product b on(a.product_number = b.product_number); mysql> explain SELECT a.ord_number, a.ord_status, a.order_time, b.product_number, b.product_name FROM tb_order a inner join tb_product b on(a.product_number = b.product_number); +----+-------------+-------+------------+------+---------------+------+---------+------+--------+----------+--------------------------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+------+---------------+------+---------+------+--------+----------+--------------------------------------------+ | 1 | SIMPLE | b | NULL | ALL | NULL | NULL | NULL | NULL | 9999 | 100.00 | NULL | | 1 | SIMPLE | a | NULL | ALL | NULL | NULL | NULL | NULL | 994365 | 10.00 | Using where; Using join buffer (hash join) | +----+-------------+-------+------------+------+---------------+------+---------+------+--------+----------+--------------------------------------------+ 2 rows in set, 1 warning (0.00 sec)
-- 添加惟一索引 CREATE UNIQUE INDEX `tb_product_unique01` ON `db_mysql_test1`.`tb_product` (`product_number`); -- 鏈接字段上有索引時,默認不使用hash join explain SELECT count(*) FROM tb_order a inner join tb_product b on(a.product_number = b.product_number); -- mysql8.0.19以前的版本應該支持這種強制使用 hashjoin的寫法,但從MySQL 8.0.19開始應該就不支持了。 explain SELECT /*+ HASH_JOIN(a,b) */ count(*) FROM tb_order a inner join tb_product b on(a.product_number = b.product_number); -- 要強制使用hash join的話,必須讓索引不起做用,能夠選用下面任意一種方式: explain SELECT /*+ NO_JOIN_INDEX(b tb_product_unique01) */ count(*) FROM tb_order a inner join tb_product b on(a.product_number = b.product_number); explain SELECT /*+ NO_INDEX(b tb_product_unique01) */ count(*) FROM tb_order a inner join tb_product b on(a.product_number = b.product_number); SELECT count(*) FROM tb_order a inner join tb_product b ignore index(tb_product_unique01) on(a.product_number = b.product_number);
官方文檔說能夠用NO_BNL
強制走Hash Join,可是我試了下,若是鏈接字段沒有索引的話,默認就走hash join
了;若是有索引的話,那麼必須忽略掉該索引纔會走hash join。
BNL
與NO_BNL
原本是用來控制是否使用block nested loop
塊嵌套循環的。官方文檔說從8.0.20開始,Mysql已經再也不使用block nested loop
塊嵌套循環了,而後又不能當即刪除這兩個hint語法,因此就用來強制走不走hash join
了。。。但實際上沒啥用,由於有索引的話優先用的是索引鏈接,沒索引默認用hash join。即,只要索引生效,設置NO_BNL
就是徒勞的,並不會走hash join
。想在有索引的狀況下強制走hash join
的話,就必須讓索引不起做用。
有的案例,哈希鏈接慢一點;有的案例,哈希鏈接快一點。不能一律而論。
hash join
比索引鏈接稍慢:-- 0.44秒 SELECT count(*) FROM tb_order a inner join tb_product b on(a.product_number = b.product_number); -- 0.88秒 SELECT /*+ NO_JOIN_INDEX(b tb_product_unique01) */ count(*) FROM tb_order a inner join tb_product b on(a.product_number = b.product_number); mysql> explain SELECT count(*) FROM tb_order a inner join tb_product b on(a.product_number = b.product_number); +----+-------------+-------+------------+--------+---------------------+---------------------+---------+---------------------------------+--------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+--------+---------------------+---------------------+---------+---------------------------------+--------+----------+-------------+ | 1 | SIMPLE | a | NULL | index | NULL | tb_order_idx01 | 164 | NULL | 994365 | 100.00 | Using index | | 1 | SIMPLE | b | NULL | eq_ref | tb_product_unique01 | tb_product_unique01 | 82 | db_mysql_test1.a.product_number | 1 | 100.00 | Using index | +----+-------------+-------+------------+--------+---------------------+---------------------+---------+---------------------------------+--------+----------+-------------+ 2 rows in set, 1 warning (0.00 sec) mysql> mysql> mysql> explain SELECT /*+ NO_JOIN_INDEX(b tb_product_unique01) */ count(*) FROM tb_order a inner join tb_product b on(a.product_number = b.product_number); +----+-------------+-------+------------+-------+---------------+---------------------+---------+------+--------+----------+---------------------------------------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+-------+---------------+---------------------+---------+------+--------+----------+---------------------------------------------------------+ | 1 | SIMPLE | b | NULL | index | NULL | tb_product_unique01 | 82 | NULL | 9999 | 100.00 | Using index | | 1 | SIMPLE | a | NULL | index | NULL | tb_order_idx01 | 164 | NULL | 994365 | 10.00 | Using where; Using index; Using join buffer (hash join) | +----+-------------+-------+------------+-------+---------------+---------------------+---------+------+--------+----------+---------------------------------------------------------+ 2 rows in set, 1 warning (0.00 sec)
hash join
比索引鏈接稍快一點。-- 去除其餘對性能有加成的索引 alter table tb_order drop index tb_order_idx01; -- tb_custom.custom_number上有惟一索引 tb_custom_unique01,默認使用索引鏈接 -- 1.52秒 SELECT count(*) FROM tb_order a inner join tb_custom b on(a.custom_number = b.custom_number); -- 經過hint語法NO_JOIN_INDEX讓語句執行時,再也不使用目標索引tb_custom_unique01作索引鏈接 -- 1.12秒 SELECT /*+ NO_JOIN_INDEX(b tb_custom_unique01) */ count(*) FROM tb_order a inner join tb_custom b on(a.custom_number = b.custom_number); mysql> explain SELECT count(*) FROM tb_order a inner join tb_custom b on(a.custom_number = b.custom_number); +----+-------------+-------+------------+--------+--------------------+--------------------+---------+--------------------------------+--------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+--------+--------------------+--------------------+---------+--------------------------------+--------+----------+-------------+ | 1 | SIMPLE | a | NULL | ALL | NULL | NULL | NULL | NULL | 994365 | 100.00 | NULL | | 1 | SIMPLE | b | NULL | eq_ref | tb_custom_unique01 | tb_custom_unique01 | 82 | db_mysql_test1.a.custom_number | 1 | 100.00 | Using index | +----+-------------+-------+------------+--------+--------------------+--------------------+---------+--------------------------------+--------+----------+-------------+ 2 rows in set, 1 warning (0.00 sec) mysql> explain SELECT /*+ NO_JOIN_INDEX(b tb_custom_unique01) */ count(*) FROM tb_order a inner join tb_custom b on(a.custom_number = b.custom_number); +----+-------------+-------+------------+-------+---------------+--------------------+---------+------+--------+----------+--------------------------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+-------+---------------+--------------------+---------+------+--------+----------+--------------------------------------------+ | 1 | SIMPLE | b | NULL | index | NULL | tb_custom_unique01 | 82 | NULL | 99522 | 100.00 | Using index | | 1 | SIMPLE | a | NULL | ALL | NULL | NULL | NULL | NULL | 994365 | 10.00 | Using where; Using join buffer (hash join) | +----+-------------+-------+------------+-------+---------------+--------------------+---------+------+--------+----------+--------------------------------------------+ 2 rows in set, 1 warning (0.00 sec)
mysql-test
程序測試結果記錄mysql-test
程序分別對MySQL8
和MySQL5
進行了數輪測試,統計以下:
具體測試項目請參考程序代碼。
輪次 | 1 | 2 | 3 | 4 | 5 | 平均值 |
---|---|---|---|---|---|---|
prepareData 耗時(毫秒) | 10258 | 10892 | 10871 | 10502 | 9413 | 10387 |
truncateTables 耗時(毫秒) | 553 | 569 | 581 | 527 | 552 | 556 |
insertOrder 耗時(毫秒) | 35756 | 31831 | 34257 | 30403 | 34697 | 33389 |
insertCustom 耗時(毫秒) | 3349 | 2781 | 2795 | 2613 | 2944 | 2896 |
insertProduct 耗時(毫秒) | 317 | 231 | 275 | 198 | 294 | 263 |
insertWarehouse 耗時(毫秒) | 6 | 15 | 8 | 14 | 8 | 10 |
selectOrders conn 耗時(毫秒) | 1 | 3 | 3 | 6 | 3 | 3 |
selectOrders executeQuery 耗時(毫秒) | 1399 | 1123 | 1150 | 1057 | 1180 | 1182 |
selectOrders ResultSet遍歷 耗時(毫秒) | 104 | 76 | 80 | 78 | 85 | 85 |
selectOrders 總耗時(毫秒) | 1507 | 1205 | 1234 | 1143 | 1269 | 1272 |
selectOrders 件數 | 1000000 | 1000000 | 1000000 | 1000000 | 1000000 | 1000000 |
selectCustoms conn 耗時(毫秒) | 2 | 2 | 3 | 2 | 2 | 2 |
selectCustoms executeQuery 耗時(毫秒) | 124 | 140 | 115 | 76 | 107 | 112 |
selectCustoms ResultSet遍歷 耗時(毫秒) | 51 | 66 | 74 | 18 | 50 | 52 |
selectCustoms 總耗時(毫秒) | 179 | 210 | 193 | 98 | 161 | 168 |
selectCustoms 件數 | 100000 | 100000 | 100000 | 100000 | 100000 | 100000 |
selectProducts conn 耗時(毫秒) | 4 | 4 | 4 | 2 | 5 | 4 |
selectProducts executeQuery 耗時(毫秒) | 15 | 13 | 12 | 9 | 10 | 12 |
selectProducts ResultSet遍歷 耗時(毫秒) | 5 | 13 | 12 | 2 | 7 | 8 |
selectProducts 總耗時(毫秒) | 25 | 31 | 29 | 15 | 23 | 25 |
selectProducts 件數 | 10000 | 10000 | 10000 | 10000 | 10000 | 10000 |
selectWarehouses conn 耗時(毫秒) | 3 | 3 | 3 | 3 | 3 | 3 |
selectWarehouses executeQuery 耗時(毫秒) | 1 | 1 | 1 | 1 | 1 | 1 |
selectWarehouses ResultSet遍歷 耗時(毫秒) | 0 | 0 | 0 | 0 | 0 | 0 |
selectWarehouses 總耗時(毫秒) | 5 | 5 | 5 | 4 | 5 | 5 |
selectWarehouses 件數 | 100 | 100 | 100 | 100 | 100 | 100 |
selectOrderJoinCustom conn 耗時(毫秒) | 3 | 3 | 3 | 2 | 3 | 3 |
selectOrderJoinCustom executeQuery 耗時(毫秒) | 3586 | 3506 | 3684 | 3084 | 2816 | 3335 |
selectOrderJoinCustom ResultSet遍歷 耗時(毫秒) | 66 | 99 | 102 | 52 | 73 | 78 |
selectOrderJoinCustom 總耗時(毫秒) | 3657 | 3611 | 3791 | 3140 | 2894 | 3419 |
selectOrderJoinCustom 件數 | 1000000 | 1000000 | 1000000 | 1000000 | 1000000 | 1000000 |
selectOrderJoinProduct conn 耗時(毫秒) | 2 | 3 | 3 | 2 | 2 | 2 |
selectOrderJoinProduct executeQuery 耗時(毫秒) | 2424 | 1704 | 1943 | 1709 | 2364 | 2029 |
selectOrderJoinProduct ResultSet遍歷 耗時(毫秒) | 55 | 74 | 69 | 51 | 56 | 61 |
selectOrderJoinProduct 總耗時(毫秒) | 2482 | 1782 | 2016 | 1763 | 2424 | 2093 |
selectOrderJoinProduct 件數 | 1000000 | 1000000 | 1000000 | 1000000 | 1000000 | 1000000 |
selectOrderJoinWarehouse conn 耗時(毫秒) | 2 | 2 | 2 | 2 | 2 | 2 |
selectOrderJoinWarehouse executeQuery 耗時(毫秒) | 1466 | 2269 | 1542 | 1107 | 1529 | 1583 |
selectOrderJoinWarehouse ResultSet遍歷 耗時(毫秒) | 62 | 135 | 74 | 52 | 50 | 75 |
selectOrderJoinWarehouse 總耗時(毫秒) | 1531 | 2411 | 1619 | 1162 | 1582 | 1661 |
selectOrderJoinWarehouse 件數 | 1000000 | 1000000 | 1000000 | 1000000 | 1000000 | 1000000 |
對mysql8進行了5輪測試,取其平均值。
輪次 | 1 | 2 | 3 | 平均值 |
---|---|---|---|---|
prepareData 耗時(毫秒) | 12377 | 9073 | 9204 | 10218 |
truncateTables 耗時(毫秒) | 627 | 475 | 451 | 518 |
insertOrder 耗時(毫秒) | 24152 | 24193 | 21994 | 23446 |
insertCustom 耗時(毫秒) | 1912 | 1916 | 1941 | 1923 |
insertProduct 耗時(毫秒) | 137 | 147 | 156 | 147 |
insertWarehouse 耗時(毫秒) | 4 | 4 | 8 | 5 |
selectOrders conn 耗時(毫秒) | 2 | 3 | 3 | 3 |
selectOrders executeQuery 耗時(毫秒) | 1181 | 1513 | 1238 | 1311 |
selectOrders ResultSet遍歷 耗時(毫秒) | 112 | 96 | 106 | 105 |
selectOrders 總耗時(毫秒) | 1297 | 1614 | 1349 | 1420 |
selectOrders 件數 | 1000000 | 1000000 | 1000000 | 1000000 |
selectCustoms conn 耗時(毫秒) | 2 | 2 | 2 | 2 |
selectCustoms executeQuery 耗時(毫秒) | 82 | 113 | 116 | 104 |
selectCustoms ResultSet遍歷 耗時(毫秒) | 28 | 23 | 31 | 27 |
selectCustoms 總耗時(毫秒) | 114 | 141 | 151 | 135 |
selectCustoms 件數 | 100000 | 100000 | 100000 | 100000 |
selectProducts conn 耗時(毫秒) | 2 | 3 | 4 | 3 |
selectProducts executeQuery 耗時(毫秒) | 13 | 10 | 17 | 13 |
selectProducts ResultSet遍歷 耗時(毫秒) | 3 | 2 | 6 | 4 |
selectProducts 總耗時(毫秒) | 20 | 15 | 29 | 21 |
selectProducts 件數 | 10000 | 10000 | 10000 | 10000 |
selectWarehouses conn 耗時(毫秒) | 2 | 2 | 3 | 2 |
selectWarehouses executeQuery 耗時(毫秒) | 0 | 0 | 1 | 0 |
selectWarehouses ResultSet遍歷 耗時(毫秒) | 0 | 0 | 0 | 0 |
selectWarehouses 總耗時(毫秒) | 4 | 3 | 4 | 4 |
selectWarehouses 件數 | 100 | 100 | 100 | 100 |
selectOrderJoinCustom conn 耗時(毫秒) | 2 | 2 | 2 | 2 |
selectOrderJoinCustom executeQuery 耗時(毫秒) | 3156 | 2548 | 2876 | 2860 |
selectOrderJoinCustom ResultSet遍歷 耗時(毫秒) | 47 | 52 | 61 | 53 |
selectOrderJoinCustom 總耗時(毫秒) | 3207 | 2604 | 2941 | 2917 |
selectOrderJoinCustom 件數 | 1000000 | 1000000 | 1000000 | 1000000 |
selectOrderJoinProduct conn 耗時(毫秒) | 2 | 2 | 2 | 2 |
selectOrderJoinProduct executeQuery 耗時(毫秒) | 1655023 | 1756847 | 1902797 | 1771556 |
selectOrderJoinProduct ResultSet遍歷 耗時(毫秒) | 43 | 51 | 222 | 105 |
selectOrderJoinProduct 總耗時(毫秒) | 1655069 | 1756902 | 1903023 | 1771665 |
selectOrderJoinProduct 件數 | 1000000 | 1000000 | 1000000 | 1000000 |
selectOrderJoinWarehouse conn 耗時(毫秒) | 2 | 2 | 7 | 4 |
selectOrderJoinWarehouse executeQuery 耗時(毫秒) | 16264 | 16030 | 18831 | 17042 |
selectOrderJoinWarehouse ResultSet遍歷 耗時(毫秒) | 35 | 50 | 609 | 231 |
selectOrderJoinWarehouse 總耗時(毫秒) | 16303 | 16083 | 19448 | 17278 |
selectOrderJoinWarehouse 件數 | 1000000 | 1000000 | 1000000 | 1000000 |
mysql5的
selectOrderJoinProduct
實在太過耗時,這裏只測試了三輪。