Ⅰ. 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官網說明] //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 tables, the row count is only a rough estimate used in SQL optimization. (This is also true if the 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條件),雖然結果準確,但會導致性能問題。
- 緩存系統的存儲計數雖然簡單效率高,但是無法保證數據的一致性。
- 數據庫保存計數很簡單,也能保證數據的一致性,建議使用。