4-1.测试MySql数据量优化

By youfang

测试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(*) 会遍历全表,虽然结果准确,但会导致性能问题。