很简单的道理但是日常开发人员还有很多不理解
以MySQL为例,其他数据库也是一样。
样品数据如下:
mysql> select * from x;
±-----±-----±-----+
| id | a | b |
±-----±-----±-----+
| 1 | 1 | 1 |
| 2 | 1 | 2 |
| 3 | 1 | 3 |
| 4 | 1 | 4 |
| 5 | 1 | 5 |
| 6 | 2 | 1 |
| 7 | 2 | 2 |
| 8 | 2 | 3 |
| 9 | 2 | 4 |
| 10 | 2 | 5 |
| 11 | 3 | 1 |
| 12 | 3 | 2 |
| 13 | 3 | 3 |
| 14 | 3 | 4 |
| 15 | 3 | 5 |
±-----±-----±-----+
15 rows in set (0.00 sec)
对于一个没有索引的列进行查询,就是全表。
mysql> select * from x where id=1;
±-----±-----±-----+
| id | a | b |
±-----±-----±-----+
| 1 | 1 | 1 |
±-----±-----±-----+
1 row in set (0.00 sec)
Query_time: 0.000832 Lock_time: 0.000011 Rows_sent: 1 Rows_examined: 15
SET timestamp=1732024559;
select * from x where id=1;
可以看出查询全表15行,返回1行。(如果全表是1500万呢?)
mysql> show create table x\G
*************************** 1. row ***************************
Table: x
Create Table: CREATE TABLE x
(id
int DEFAULT NULL,a
int DEFAULT NULL,b
int DEFAULT NULL,
KEY t1
(a
)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
1 row in set (0.01 sec)
日常就是这样,建立索引的列不用,用的就是那些没索引的列。
有索引就一定对吗?
mysql> select * from x where a=1;
±-----±-----±-----+
| id | a | b |
±-----±-----±-----+
| 1 | 1 | 1 |
| 2 | 1 | 2 |
| 3 | 1 | 3 |
| 4 | 1 | 4 |
| 5 | 1 | 5 |
±-----±-----±-----+
5 rows in set (0.00 sec)
Query_time: 0.001034 Lock_time: 0.000011 Rows_sent: 5 Rows_examined: 5
SET timestamp=1732024476;
select * from x where a=1;
可以看出只扫描索引范围的行(5行),返回也是范围所在的行(这里要说,如果索引的范围不是5行而是1000万呢?)。
那么再加一个过滤条件总可以吧?
mysql> select * from x where a=1 and b=1;
±-----±-----±-----+
| id | a | b |
±-----±-----±-----+
| 1 | 1 | 1 |
±-----±-----±-----+
1 row in set (0.00 sec)
Query_time: 0.001114 Lock_time: 0.000016 Rows_sent: 1 Rows_examined: 5
SET timestamp=1732024492;
select * from x where a=1 and b=1;
很明显可以看出只扫描索引范围的行(5行),返回是返回了1行,但是扫描行数还是和上一个场景一样(5行)。(这里要说,如果索引的范围就是1000万呢?)。
正确的做法
以上场景建立一个组合索引
mysql> create index t2 on x (a,b);
Query OK, 0 rows affected (0.05 sec)
mysql> show create table x\G
*************************** 1. row ***************************
Table: x
Create Table: CREATE TABLE x
(id
int DEFAULT NULL,a
int DEFAULT NULL,b
int DEFAULT NULL,
KEY t1
(a
),
KEY t2
(a
,b
)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
1 row in set (0.00 sec)
Query_time: 0.219642 Lock_time: 0.000010 Rows_sent: 1 Rows_examined: 1
SET timestamp=1732024637;
select * from x where a=1 and b=1;
返回和扫描的一致。真正起到了两级过滤。而不是只扫描第一列索引的范围,而是精确到第一列和第二列交集的范围。大大缩小了检索范围。