mysql数据库索引优化与实践(一)

前言

mysql数据库是如今应用最普遍的数据库系统。与数据库打交道是每一个Java程序员平常工做之一,索引优化是必备的技能之一。mysql

为何要了解索引

真实案例

案例一:大学有段时间学习爬虫,爬取了知乎300w用户答题数据,存储到mysql数据中。那时不了解索引,一条简单的“根据用户名搜索所有回答的sql“须要执行半分钟左右,彻底知足不了正常的使用。程序员

案例二:最近线上应用的数据库频频出现多条慢sql风险提示,而工做以来,对数据库优化方面所知甚少。例如一个用户数据页面须要执行不少次数据库查询,性能很慢,经过增长超时时间勉强能够访问,可是性能上须要优化。算法

索引的优势

合适的索引,能够大大减少mysql服务器扫描的数据量,避免内存排序和临时表,提升应用程序的查询性能。sql

索引的类型

mysql数据中有多种索引类型,primary key,unique,normal,但底层存储的数据结构都是BTREE;有些存储引擎还提供hash索引,全文索引。数据库

BTREE是最多见的优化要面对的索引结构,都是基于BTREE的讨论。服务器

B-TREE

查询数据最简单暴力的方式是遍历全部记录;若是数据不重复,就能够经过组织成一颗排序二叉树,经过二分查找算法来查询,大大提升查询性能。而BTREE是一种更强大的排序树,支持多个分支,高度更低,数据的插入、删除、更新更快。数据结构

现代数据库的索引文件和文件系统的文件块都被组织成BTREE。性能

btree的每一个节点都包含有key,data和只想子节点指针。学习

btree有度的概念d>=1。假设btree的度为d,则每一个内部节点能够有n=[d+1,2d+1)个key,n+1个子节点指针。树的最大高度为h=Logb[(N+1)/2]。优化

索引和文件系统中,B-TREE的节点常设计成接近一个内存页大小(也是磁盘扇区大小),且树的度很是大。这样磁盘I/O的次数,就等于树的高度h。假设b=100,一百万个节点的树,h将只有3层。即,只有3次磁盘I/O就能够查找完毕,性能很是高。

索引查询

创建索引后,合适的查询语句才能最大发挥索引的优点。

另外,因为查询优化器能够解析客户端的sql语句,会调整sql的查询语句的条件顺序去匹配最合适的索引。

复制代码
-- 表建立语句
CREATE TABLE people (
    last_name VARCHAR(20) NOT NULL,
    first_name VARCHAR(20) NOT NULL,
    gender CHAR(1) NOT NULL,
   birth date NOT NULL, KEY last_first_name_gender_key(last_name, first_name, gener) );
复制代码

一,全值匹配

查询语句where条件和索引中的全部列进行匹配。

1 SELECT * FROM people WHERE last_name='zhang' AND first_name='yin' AND gender='m';

二,最左前缀匹配

查询条件能够匹配索引的最左若干列。注意关键词”最左前缀“。

-- 可使用部分索引"last_name"
SELECT * FROM people WHERE last_name='zhang' AND gender='m';

-- 没法使用索引
SELECT * FROM people WHERE first_name='zhang' AND gender='m';

三,列前缀匹配

查询中的like条件,在有些场景下也可使用索引。如 last_name like 'zh%'可使用索引,而last_name like '%ing'则没法使用索引。

-- 可使用索引,由于BTREE的节点比较key值时是从key值得最左侧开始匹配
SELECT * FROM people WHERE last_name like 'zhang%' AND gender='m' ;

四,范围查询

索引的列也支持范围查询。

SELECT * FROM people WHERE last_name > 'zhang' AND last_name <'wang'

五,排序

ORDER BY语句在特定状况下也支持用索引来排序来提升性能。

EXPLAIN SELECT * FROM people WHERE last_name = 'zhang' ORDER BY first_name ASC

六,限制

1,查询列不能参与表达式运算,不然没法使用索引。

复制代码
--表设计中没有age列,以示参考
--假设age是索引中一部分,这样的查询将没法使用到索引
SELECT * FROM people WHERE last_name='zhang' AND age+3>28;

--这样写就可使用索引
SELECT * FROM people WHERE last_name='zhang' AND age>25;
复制代码

2,若是不是从索引的最左列开始,则没法使用索引。如,根据first_name、gender或者查找的查询没法使用索引。

-- 不是从last_name开始匹配,因此没法使用索引
SELECT * FROM people WHERE first_name='zhang' AND gender='m'

3,不能跳过索引中的列。

-- 不能跳过first_name查询,不然只有last_name列用到了索引
SELECT * FROM people WHERE last_name='zhang' AND gender='m'

4,若是查询中某个列是范围查询(like,between,>,<等),则其右边全部的列都没法使用索引。

-- 因为first_name用了like查询,因此gender列没法使用索引了
SELECT * FROM people WHERE last_name='zhang' AND first_name LIKE '%in' AND gender='m';

高效索引策略

前面讲到了各类可使用索引的查询状况,下面讲如何创建高效的索引。

1,创建多列索引

 创建多列的索引,而不是每一列都创建单独的。由于在mysql服务器在查询分析后,最终只能根据查询匹配到一个索引(或者没有)并使用。因此,假设多列上分别都创建了单独索引,即便组合查询用到了多列,最终也只有一列用到了索引。

   因此,假设你最多见的查询是根据last_name、first_name和gender来查询,应该创建包含三列的索引。

ALTER TABLE people ADD INDEX idx_name_gender(last_name, first_name , gender);

2,索引列的顺序

在多列B-TREE索引中,意味着索引是按照最左列开始,从左往右进行排序的。一个设计经验法则,将”选择性高“的列放在索引最左列。这样有助于索引通过最少的比较找到目标元组。

索引列选择性:不重复的索引值与表的所有记录总数的比值,0<T<=1。惟一索引列的选择性是1。索引的选择性越高则查询效率越高,能够”更早地”过滤掉不匹配地记录。

假设要创建 last_name, first_name , gender 三列的索引。

T(last_name)= select count(distinct last_name) / count(*) ;

T(first_name)= select count(distinct first_name) / count(*) ;

T(gender)= select count(distinct gender) / count(*) ;

很显然,last_name和first_name应该放到索引的前面(以实际状况为主)

 

结尾

了解到了常见的索引策略和查询技巧,可是怎么在实际项目中应用并排查现存数据库中sql的性能缺陷?下一篇将介绍mysql数据库的explain关键字,总结和分析慢sql常见技巧。

相关文章
相关标签/搜索