为什么 SELECT COUNT(*) FROM table 在 InnoDB 比 MyISAM 慢?

这主要是因为两种存储引擎处理行计数的方式不同。

MyISAM 引擎

  • 存储行计数: MyISAM 存储引擎会在表的元数据中存储行的总数。这意味着当执行 SELECT COUNT(*) FROM table时,MyISAM 只需读取这个预先存储的值,非常快速。
  • 无锁表扫描: 由于 MyISAM 表是非事务性的,它不需要处理事务和锁机制,因此在表扫描时速度较快。

InnoDB 引擎

  • 实时计算行数: InnoDB 引擎不会在表的元数据中存储行的总数。相反,它需要遍历表中的数据行,逐个统计行数,以确保行计数的准确性。这是因为InnoDB 是一个支持事务的存储引擎,行数可能会因为未提交的事务而变化。
  • 锁机制: InnoDB使用行级锁和多版本并发控制(MVCC),以支持高并发和事务。这些特性虽然提高了数据一致性和并发性能,但在执行全表扫描时,可能会带来额外的开销。
  • 缓存机制: InnoDB有自己的缓存机制(缓冲池),它会缓存数据页和索引页,提高查询性能。然而,在进行全表扫描和计数时,这些缓存并不能显著加速计数操作。

事务A只修改数据,只要不提交,事务B就感觉不到数据发生了变化,所以得先处理事务,保证数据的一致性,所以innodb比较慢,而myisam数据是及时更新的,一旦数据发生了变化,在表的元数据中存储的行的总数也就发生变化了。

优化 `select count(*)` 语句查询性能的方法有多种,以下是具体介绍: #### 数据库特性利用 - **MySQL**:MySQL对 `count(*)` 做了很多优化,在MyISAM存储引擎中会直接把表的总行数单独记录下来供 `COUNT(*)` 查询;InnoDB会在扫表时选择最小的索引来降低成本,但这些优化的前提是没有进行 `where` 和 `group` 的条件查询[^1]。 - **PostgreSQL**:在9.2以前全表的 `count` 只能通过扫描全表来得到,即使有主键也必须扫描全表;9.2版本增加了 `index only scan` 的功能,`count(*)` 可以通过仅仅扫描主键就可以得到。若需频繁进行全表 `count` 操作,可以给表建立几个触发器,每次插入、删除、`truncate` 表时触发,将表的记录数更新到一个记录表中。为缓解并发插入和删除操作时的行锁冲突问题,可以使用多条记录来存储表的 `count(*)` 值[^5]。 #### 语句选择 按照效率排序,`count(字段)<count(主键 id)<count(1)≈count (*)`,建议尽量使用 `count (*)` [^4]。 #### 索引优化 在MySQL中,合理的索引能显著提升 `select count(*)` 的性能。例如在某些情况下,走二级索引比主键索引快很多。不过要注意索引并非越多越好,过多的索引会增加写操作的开销和存储空间的占用。 #### 示例代码 以下是PostgreSQL中创建触发器更新记录数的示例: ```sql -- 创建测试表 create table a(id serial4 primary key, info text, crt_time timestamp(0) default now()); -- 创建记录表 create table a_count ( id serial, record_count bigint ); -- 插入初始记录数 insert into a_count (record_count) values (0); -- 创建插入触发器函数 CREATE OR REPLACE FUNCTION update_a_count_insert() RETURNS trigger AS $$ BEGIN UPDATE a_count SET record_count = record_count + 1; RETURN NEW; END; $$ LANGUAGE plpgsql; -- 创建插入触发器 CREATE TRIGGER insert_trigger AFTER INSERT ON a FOR EACH ROW EXECUTE FUNCTION update_a_count_insert(); -- 创建删除触发器函数 CREATE OR REPLACE FUNCTION update_a_count_delete() RETURNS trigger AS $$ BEGIN UPDATE a_count SET record_count = record_count - 1; RETURN OLD; END; $$ LANGUAGE plpgsql; -- 创建删除触发器 CREATE TRIGGER delete_trigger AFTER DELETE ON a FOR EACH ROW EXECUTE FUNCTION update_a_count_delete(); ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值