64-MySQL其它查询优化策略
一、 EXISTS 和 IN
不太理解哪种情况下应该使用 EXISTS,哪种情况应该用 IN。选择的标准是看能否使用表的索引吗?
索引是个前提,其实选择与否还是要看表的大小。可以将选择的标准理解为小表驱动大表
。这种方式下效率是最高的
1.1、EXISTS
- SQL
SELECT *
FROM A
WHERE EXISTS(SELECT cc FROM B WHERE A.cc = B.cc);
- 当 A 小于 B 时,用 EXISTS 。因为 EXISTS 的实现,相当于
外表循环
,实现的逻辑类似于
for i in A
for j in B
if i.cc == j.cc
then . . .
1.2、IN
- SQL
SELECT *
FROM A
WHERE cc IN (SELECT cc FROM B);
- 当 B 小于 A 时用 IN,因为实现的逻辑类似于
for i in B
for j in A
if i.cc == j.cc
then . . .
二、 COUNT(*)与COUNT(具体字段)效率
在 MySQL 中统计数据表的行数,可以使用三种方式:
SELECT COUNT(*)
、SELECT COUNT(1)
和SELECT COUNT(具体字段)
,使用这三者之间的查询效率是怎样的?如果你要统计的是某个字段的非空数据行数
2.1、SELECT COUNT(*)与SELECT COUNT(1)比较
SELECT COUNT(*)
和SELECT COUNT(1)
都是对所有结果进行COUNT
,COUNT(*)
和COUNT(1)
本质上并没有区别。如果有WHERE
子句,则是对所有符合筛选条件的数据行进行统计;如果没有WHERE
子句,则是对数据表的数据行数进行统计
2.2、不同存储引擎
- MyISAM存储引擎
MyISAM存储引擎统计数据表的行数只需要O(1)的复杂度,这是因为每张
MyISAM
的数据表都有一个meta
信息存储了row_count
值,而一致性则由表级锁
来保证
- InnoDB存储引擎
InnoDB存储引擎因为 InnoDB 支持事务,采用
行级锁
和MVCC机制
,所以无法想MyISAM
一样,维护一个row_count
变量,因此需要采用全表扫描
,是O(n)的复杂度,进行循环 + 计数的方式来完成统计
2.3、COUNT(具体字段)
在
InnoDB
引擎中,如果采用COUNT(具体字段)
来统计数据行数,要尽量采用二级索引
。因为主键
采用的索引是聚簇索引
,聚簇索引
包含的信息多,明显会大于二级索引
。对于COUNT(*)
和COUNT(1)
来说,它们不需要查找具体的行,只是统计行数,系统会自动
采用占用空间更小的二级索引
来进行统计。如果有多个二级索引
,会使用key_len
小的二级索引
进行扫描。当没有二级索引
的时候,才会采用主键索引
来进行统计
三、于SELECT(*)
在表查询中,建议
明确字段
,不要使用*
作为查询的字段列表,推荐使用SELECT <字段列表> 查询
- 原因1: MySQL 在解析的过程中,会通过
查询数据字典
将"*"按序转换成所有列名,这会大大的耗费资源和时间。 - 原因2:无法使用
覆盖索引
四、 LIMIT 1 对优化的影响
针对的是会
扫描全表的 SQL 语句
,如果你可以确定结果集只有一条,那么加上LIMIT 1
的时候,当找到一条结果的时候就不会继续扫描了,这样会加快查询速度。
如果数据表已经对字段建立了唯一索引
,那么可以通过索引进行查询,不会全表扫描的话,就不需要加
上LIMIT 1
了。
五、多使用COMMIT
只要有可能,在程序中尽量多使用 COMMIT,这样程序的性能得到提高,需求也会因为 COMMIT 所释放
的资源而减少
- COMMIT 所释放的资源:
- 回滚段上用于恢复数据的信息
- 被程序语句获得的锁
- redo / undo log buffer 中的空间
- 管理上述 3 种资源中的内部花费