在 InnoDB 引擎上运行时确实会很慢。如section 14.24 of the MySQL 5.7 Reference Manual, “InnoDB Restrictions and Limitations” 中所述,第三个要点:
InnoDB InnoDB 不保留表中的内部行数,因为并发事务可能同时“看到”不同数量的行。因此,SELECT COUNT(*) 语句只计算当前事务可见的行数。
有关 InnoDB 如何处理 SELECT COUNT(*) 语句的信息,请参阅第 12.20.1 节“聚合函数描述”中的 COUNT() 描述。
建议的解决方案是柜台。这是一个单独的表,具有一行和一列,具有当前记录数。它可以通过触发器保持更新。像这样的:
create table big_table_count (rec_count int default 0);
-- one-shot initialisation:
insert into big_table_count select count(*) from big_table;
create trigger big_insert after insert on big_table
for each row
update big_table_count set rec_count = rec_count + 1;
create trigger big_delete after delete on big_table
for each row
update big_table_count set rec_count = rec_count - 1;
您可以在此处看到fiddle,您应该在其中更改构建部分中的insert/delete 语句以查看效果:
select rec_count from big_table_count;
您可以通过为每个表创建这样的表,或者在上面的计数器表中为每个表保留一行,将其扩展到多个表。然后它将由列 "table_name" 键入。
提高并发性
如果你有很多并发会话插入或删除记录,上述方法确实会产生影响,因为它们需要相互等待才能完成计数器的更新。
一种解决方案是不要让触发器更新相同的单条记录,而是让它们插入一条新记录,如下所示:
create trigger big_insert after insert on big_table
for each row
insert into big_table_count (rec_count) values (1);
create trigger big_delete after delete on big_table
for each row
insert into big_table_count (rec_count) values (-1);
那么获取计数的方式就变成了:
select sum(rec_count) from big_table_count;
然后,偶尔(例如每天)你应该重新初始化计数器表以保持它的小:
truncate table big_table_count;
insert into big_table_count select count(*) from big_table;