测试MySQL数据量优化
创建测试表
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| CREATE TABLE `r_sku_storage_goods` ( `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID', `sku_id` bigint NOT NULL COMMENT 'SKU ID', `storage_id` bigint NOT NULL DEFAULT '0' COMMENT '库点ID', `section_id` bigint NOT NULL DEFAULT '0' COMMENT '库区ID', `bin_id` bigint NOT NULL DEFAULT '0' COMMENT '库位ID', `goods_identity_code` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL COMMENT '物品识别码', `processing_plan_receipt_id` bigint NOT NULL COMMENT '加工单ID', `batch_number` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '批号', `expiry_date` date NOT NULL COMMENT '有效期', `production_date` date DEFAULT NULL COMMENT '生产日期', `status` tinyint(1) NOT NULL DEFAULT '1' COMMENT '物品状态:1-正常使用 0-删除', `purchase_price` decimal(10,2) DEFAULT NULL COMMENT '采购价格', `trace_storage_id` bigint NOT NULL DEFAULT '0' COMMENT '追溯库点ID(用来记录出库前所在的库点)', `consume_time` datetime DEFAULT NULL COMMENT '消耗时间', `settlement_time` datetime DEFAULT NULL COMMENT '结算时间', `create_user` bigint NOT NULL COMMENT '创建人ID', `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间,默认当前时间', `update_user` bigint DEFAULT NULL COMMENT '最后修改人ID', `update_time` datetime DEFAULT NULL COMMENT '最后修改时间', `remark` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '备注', PRIMARY KEY (`id`) USING BTREE, UNIQUE KEY `idx_sku_storage_goods_goodsIdentityCode` (`goods_identity_code`), KEY `idx_sku_storage_goods_skuid` (`sku_id`) USING BTREE, KEY `idx_sku_storage_goods_storageid` (`storage_id`) USING BTREE, KEY `idx_sku_storage_goods_expirydate` (`expiry_date`) USING BTREE, KEY `status` (`status`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='仓库物品表';
|
添加索引
1
| ALTER TABLE r_sku_storage_goods ADD INDEX (status);
|
测试
count(*) 还是count(status) 耗时都差不多。
Count慢的原因
COUNT()是一个特殊的函数,有两种非常不同的作用:
- 它可以统计某个列值的数量,也可以统计行数。
- 在统计列值时要求列值非空的(不统计NULL)。
COUNT()的另外一个作用是统计结果集的行数。
当mysql确认括号内的表达式值不可能为空时,实际上就是在统计行数。
最简单的就是当我们使用COUNT(*)
的时候,这种情况下通配符*
并不会像我们猜想的那样扩展成所有的列,
实际上,它会忽略所有的列而直接统计所有的行数。
在括号内指定了一个列却希望统计结果集的行数。如果希望知道的是结果集的行数,最好使用COUNT(*)
,这样写意义清晰,性能也会很好。
MyISAM的神话
一个容易产生的误解就是:MyISAM的COUNT()函数总是非常快,不过这是有前提条件的,即只有没有任何where条件的COUNT(*)
才非常快
区别与联系
innodb为聚簇索引同时支持事物,其在count指令实现上采用实时统计方式。在无可用的二级索引情况下,
执行count会使MySQL扫描全表数据,当数据中存在大字段或字段较多时候,其效率非常低下(每个页只能包含较少的数据条数,需要访问的物理页较多)。
- MyISAM 引擎把一个表的总行数存在了磁盘上,因此执行
count(*)
的时候会直接返回这个数,效率很高;
- 而 InnoDB 引擎就麻烦了,它执行
count(*)
的时候,需要把数据一行一行地从引擎里面读出来,然后累积计数。
是没有过滤条件的 count(*)
,如果加了 where 条件的话,MyISAM 表也是不能返回得这么快的。
这和 InnoDB 的事务设计有关系,可重复读是它默认的隔离级别,在代码上就是通过多版本并发控制,也就是 MVCC 来实现的。每一行记录都要判断自己是否对这个会话可见,因此对于 count(*) 请求来说,InnoDB 只好把数据一行一行地读出依次判断,可见的行才能够用于计算“基于这个查询”的表的总行数。
到这里我们小结一下:
- MyISAM 表虽然
count(*)
很快,但是不支持事务;
show table status
命令虽然返回很快,但是不准确;
- InnoDB 表直接
count(*)
会遍历全表,虽然结果准确,但会导致性能问题。