今日推荐: 如何用Redis高效实现点赞功能?用Set?还是Bitmap?
文章地址: https://cloud.tencent.com/developer/article/2464859
推荐语: 这篇文章从实际需求出发,演示了redis的使用场景,并在实现该需求的过程中对比两个不同实现方案的优缺点演示了两个不同数据结构的优缺点,实际项目开发中可以有所借鉴。
下面开始今天对于MySQL索引的一些实践
在一张没有索引的表里,当我们使用SELECT语句的时候MySQL需要从第一行开始扫描整张表来获取所需要的行,可想而知,数据表存储的数据越多,需要的时间越长。
提高SELECT性能最好的方式是创建索引, 索引就像指向所选行的一个指针让MySQL不需要从头扫描就能定位到所需要的行。
但是索引也不是越多越好:
所以,需要在成本和收益之间找到一个平衡点。另外因为在有索引的情况下需要先访问索引再找到所需要的数据行,所以以下情况考虑不使用索引:
如上我们可知索引是提高性能的一个重要手段,该文介绍了在执行某个查询时是否用到了所定义的索引以及不走索引的一些原因。
接下来我们来看几个索引以及如果增加删除索引和索引对查询的性能影响。 以employees数据库中的salaries表为例
mysql> show index from salaries \G
*************************** 1. row ***************************
Table: salaries
Non_unique: 0
Key_name: PRIMARY
Seq_in_index: 1
Column_name: emp_no
Collation: A
Cardinality: 280478
Sub_part: NULL
Packed: NULL
Null:
Index_type: BTREE
Comment:
Index_comment:
Visible: YES
Expression: NULL
*************************** 2. row ***************************
Table: salaries
Non_unique: 0
Key_name: PRIMARY
Seq_in_index: 2
Column_name: from_date
Collation: A
Cardinality: 2706265
Sub_part: NULL
Packed: NULL
Null:
Index_type: BTREE
Comment:
Index_comment:
Visible: YES
Expression: NULL
2 rows in set (0.00 sec)
从该结果看该表只有一个PRIMARY的索引。也就是主键,一个表只能有一个主键,所以一般我们定义最重要最常查询的列为主键; 主键可以包括一个或者多个列, 有多个列的主键为复合主键; 主键所在的列的值必须是非空并且唯一的,这样才能保证每行都可以被唯一识别。 从该结果中我们还可以得出以下信息:
现在已经知道索引并不是越多越好,需要根据实际的查询需求来确定给哪些列建立索引,那么怎么知道某个查询是否用到索引呢, MySQL为我们提供了EXPLAIN语句。接下来是两个对于employees表的示例查询。
mysql> show index from employees \G
*************************** 1. row ***************************
Table: employees
Non_unique: 0
Key_name: PRIMARY
Seq_in_index: 1
Column_name: emp_no
Collation: A
Cardinality: 299290
Sub_part: NULL
Packed: NULL
Null:
Index_type: BTREE
Comment:
Index_comment:
Visible: YES
Expression: NULL
1 row in set (0.00 sec)
从以上结果中可以看出该表有一个PRIMARY主键, 所在的列是emp_no列且该索引可以用于查询。
mysql> explain select * from employees where emp_no = 11001 \G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: employees
partitions: NULL
type: const
possible_keys: PRIMARY
key: PRIMARY
key_len: 4
ref: const
rows: 1
filtered: 100.00
Extra: NULL
1 row in set, 1 warning (0.00 sec)
从结果中我们可以看出
mysql> explain select * from employees where first_name = "Baziley" and last_name = "Buchter" \G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: employees
partitions: NULL
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 299290
filtered: 1.00
Extra: Using where
1 row in set, 1 warning (0.00 sec)
接下来我们将比较一下在使用索引列和没有使用索引列的耗时的不同
mysql> SET profiling = 1;
Query OK, 0 rows affected, 1 warning (0.00 sec)
启用和查看查询的执行时间
mysql> select * from employees where emp_no = 11001 \G
*************************** 1. row ***************************
emp_no: 11001
birth_date: 1956-04-16
first_name: Baziley
last_name: Buchter
gender: F
hire_date: 1987-02-23
1 row in set (0.00 sec)
mysql> select * from employees where first_name = "Baziley" and last_name = "Buchter" \G
*************************** 1. row ***************************
emp_no: 11001
birth_date: 1956-04-16
first_name: Baziley
last_name: Buchter
gender: F
hire_date: 1987-02-23
1 row in set (0.16 sec)
执行查询,分别用emp_no和first_name,last_name的组合来查询数据
mysql> SHOW PROFILES;
+----------+------------+--------------------------------------------------------------------------------+
| Query_ID | Duration | Query |
+----------+------------+--------------------------------------------------------------------------------+
| 1 | 0.00034200 | select * from employees where emp_no = 11001 |
| 2 | 0.16409500 | select * from employees where first_name = "Baziley" and last_name = "Buchter" |
+----------+------------+--------------------------------------------------------------------------------+
查看查询语句的执行时间,duration以秒为单位,我们可以看到用到索引的查询所耗时为0.342毫秒, 而没用到索引的查询耗时为164.095毫秒,可谓差距十分巨大。
我们可以试着给employees表格增加以first_name, last_name的值的索引,
mysql> CREATE INDEX idx_name ON employees (first_name, last_name);
Query OK, 0 rows affected (1.16 sec)
Records: 0 Duplicates: 0 Warnings: 0
增加索引后我们再使用first_name和last_name来查询
mysql> SHOW PROFILES \G
*************************** 2. row ***************************
Query_ID: 2
Duration: 0.16409500
Query: select * from employees where first_name = "Baziley" and last_name = "Buchter"
*************************** 7. row ***************************
Query_ID: 7
Duration: 0.00042000
Query: select * from employees where first_name = "Baziley" and last_name = "Buchter"
对比来看增加了first_name和last_name的组合索引之后,同样的查询耗时变成0.42毫秒,比之前的164.095毫秒快了390倍。
由于我们提到对于有索引的列在INSERT, UPDATE, DELETE操作时都需要更新索引,所以过多的索引会减慢性能,在查询时应尽量使用已经有索引的列。 对于不那么需要的索引我们可以将之删除
mysql> DROP INDEX idx_name ON employees;
Query OK, 0 rows affected (0.01 sec)
Records: 0 Duplicates: 0 Warnings: 0
删除刚刚建立的关于first_name, last_name的索引
另外由于使用profiling会造成一定的性能影响,建议在非生产环境才使用profiling并在完成分析后关闭profiling。
mysql> SET profiling = 0;
Query OK, 0 rows affected, 1 warning (0.00 sec)
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。