Ⅰ. MySQL
1.1、count的实现方式
- 在Mysql中的不同的存储引擎对count函数有不同的实现方式。
- MyISAM引擎把一个表的总行数存在了磁盘上,因此执行count(*)的时候会直接返回这个数,效率很高(没有where查询条件)。
- InnoDB引擎并没有直接将总数存在磁盘上,在执行count(*)函数的时候需要一行一行的将数据读出来,然后累计总数。(全表扫描)
1.2、show table status
一、使用方法
语法:SHOW TABLE STATUS [FROM db_name] [LIKE 'pattern' | WHERE expr]
参数:[FROM db_name] 可选,表示查询哪个数据库下面的表信息。
[LIKE 'pattern' | WHERE expr] 可选,表示查询哪些具体的表名。
示例:show table status from db_name LIKE 'uc%' 查询db_name 数据库里表名以uc开头的表的信息
二、show table status 查询结果中各列的说明
返回列 |
说明 |
Name |
表名称 |
Engine |
表的存储引擎 |
Version |
版本 |
Row_format |
行格式 |
Rows |
表中的行数。对于非事务性表,这个值是精确的,对于事务性引擎,这个值通常是估算的 |
Avg_row_length |
平均每行的大下(字节) |
Data_length |
表的数据量(单位:字节) |
Max_data_length |
表可以容纳的最大数据量 |
Index_length |
索引占用磁盘的空间大小 |
Data_free |
标识已分配,但现在未使用的空间,并且包含了已被删除行的空间。 |
Auto_increment |
下一个Auto_increment的值 |
Create_time |
表的创建时间 |
Update_time |
表的最近更新时间 |
Check_time |
最近一次使用 check table 或myisamchk工具检查表的时间 |
Collation |
表的字符集和字符排序规则 |
Checksum |
如果启用,则对整个表的内容计算时的校验和 |
Create_options |
表创建时的其它 |
Comment |
表在创建是添加的注释说明 |
三、总结
[MySQL官网说明] https://dev.mysql.com/doc/refman/8.0/en/show-table-status.html
The number of rows. Some storage engines, such as , store the exact count. For other storage engines, such as , this value is an approximation, and may vary from the actual value by as much as 40% to 50%. In such cases, use to obtain an accurate count. MyISAM InnoDB SELECT COUNT(*)
The value is for tables.Rows NULL INFORMATION_SCHEMA
For InnoDB tables, the row count is only a rough estimate used in SQL optimization. (This is also true if the InnoDB table is partitioned.)
- show table status这个命令能够很快的查询出数据库中每个表的行数,但是真的能够替代count(*)吗?
- 答案是不能。原因很简单,这个命令统计出来的值是一个「估值」,因此是不准确的,官方文档说误差大概在40%-50%。
四、测试论证
数据库 192.168.173.45:4306
-- 结果:7890 耗时:0.002s
SELECT COUNT(*) FROM sys_log
-- 结果:rows 4081 耗时:0.002s
SHOW TABLE STATUS FROM data_platform_v3_0_1_police_single WHERE NAME = 'sys_log'
-- 结果:table_rows 4081 耗时:0.001s
select * from information_schema.tables where TABLE_SCHEMA='data_platform_v3_0_1_police_single' and TABLE_NAMe='sys_log'
-- 误差:4081/7890=0.517,误差大概 48%
本地数据库
-- 结果:2758450 耗时:52.007s
SELECT COUNT(*) FROM sys_log;
-- 结果:rows 2396159 耗时:0.015s
SHOW TABLE STATUS FROM data_platform_v3_0_police_single WHERE NAME = 'sys_log';
-- 结果:table_rows 2396159 耗时:0.058s
select * from information_schema.tables where TABLE_SCHEMA='data_platform_v3_0_police_single' and TABLE_NAMe='sys_log';
-- 误差:2396159/2758450=0.867,误差大概 13%
1.3、缓存系统存储总数
这种方法也是最容易想到的,增加一行就+1,删除一行就-1,并且缓存系统读取也是很快,既简单又方便的为什么不用?
缓存系统和Mysql是两个系统,比如redis和Mysql这两个是典型的比较。两个系统最难的就是在高并发下无法保证数据的一致性。
在并发系统里面,我们是无法精确控制不同线程的执行时刻的,所以,我们说即使Redis正常工作,这个计数值还是逻辑上不精确的。
1.4、在数据库保存计数
直接使用数据库来保存,有了「事务」的支持,也就保证了数据的一致性了。
如何使用呢?很简单,直接将计数保存在一张表中(table_name,total)。
至于执行的逻辑只需要将缓存系统中redis计数+1改成total字段+1即可。
由于在同一个事务中,保证了数据在逻辑上的一致性。
1.5、总结
- MyISAM表虽然count(*)很快,但是不支持事务;
- show table status命令虽然返回很快,但是不准确;
- InnoDB直接count(*)会遍历全表(没有where条件),虽然结果准确,但会导致性能问题。
- 缓存系统的存储计数虽然简单效率高,但是无法保证数据的一致性。
- 数据库保存计数很简单,也能保证数据的一致性,建议使用。