先说结论,若是你的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
实在太过耗时,这里只测试了三轮。