索引详解:
- 索引的原理:把无序的数据变成有序的查询;
select * from test where name = 'wyh';
在没有索引的情况下,通过观察我们可以得知:
- 便利整张表的内容,比较名称是否为wyh
- 如果为wyh,那么把数据放入结果集当中去
- 这种情况下的问题是全表扫描,需要把表所有的数据查询一次(IO比较多)
在test表中创建一个索引(使用列:name)
- mysql会把数据按照name进行排序
- 把排序的结果变成一个倒排表
- 在索引中查询name为wyh的那条倒排表数据
- 把数据放到结果集当中去
索引在一定意义上大大的调高了插叙的性能,但有利就会有弊:
- 如果增加了一条数据,那么到排表中也会实时更新,并重新倒排
- 删除数据同理可得,修改数据涉及到了索引列值得改变
- 索引的物理结构:
数据库文件存储的位置:my.ini配置文件中dataDir对应的数据目录中;
每一个数据库一个文件夹:
MYISAM引擎:每一个表(table_name)
Table_name:MYI:存放的是数据表对应的索引信息和索引内容
Table_name:FRM:存放的是数据表的结构信息
Table_name:MYD:存放的是数据表的内容
InnoDB引擎:
Table_name:fim:存放的是数据表的结构信息
数据文件和索引都是统一存放在ibdata文件中
索引文件都是额外存在的,对索引的查询查询和维护都是需要IO的;
- 索引的结构
- 默认情况下,一旦创建了一个表,这个表设置了主键,那么mysql会自动的为这个主键创建一个unique的索引。
- 索引的类型
- normal:普通索引,允许一个索引值后面关联多个行值
- UNIQUE:唯一索引;允许一个索引值后面只能有一个行值;之前对列添加唯一约束其实就是为这列添加了一个unique索引;当我们为一个表添加一个主键的时候,其实就是为这个表主键列(设置了非空约束),并为主键列添加了一个唯一索引;
- Fulltext:全文检索,mysql的全文检索只能用myisam引擎,并且性能较低,不建议使用;
3、索引的方法:
1、b-tree:是一颗树(二叉树,平衡二叉树,平衡树(B-TREE))
使用平衡树实现索引,是mysql中使用最多的索引类型;在innodb中,存在两种索引类型,第一种是主键索引(primary key),在索引内容中直接保存数据的地址;第二种是其他索引,在索引内容中保存的是指向主键索引的引用;所以在使用innodb的时候,要尽量的使用主键索引,速度非常快;
b-tree中保存的数据都是按照一定顺序保存的数据,是可以允许在范围之内进行查询;
2、Hash:把索引的值做hash运算,并存放到hash表中,使用较少,一般是memory引擎使用;优点:因为使用hash表存储,按照常理,hash的性能比B-TREE效率高很多。
Hash索引的缺点:
1,hash索引只能适用于精确的值比较,=,in,或者<>;无法使用范围查询;
2,无法使用索引排序;
3,组合hash索引无法使用部分索引;
4,如果大量索引hash值相同,性能较低;
下面结合两幅图来看看b-tree和hash
- 索引的利弊:
索引的好处:
- 提高表数据的检索效率
- 如果排序的列是索引列(如果查询的列等于排序的列[并且在这列上面做了索引]),大大降低了排序的成本
- 在分组操作中如果分组条件是索引列,也会提高效率
索引的问题:
索引需要额外的维护成本;因为索引文件是单独存在的文件,对数据的增加,修改,删除,都会产生额外的对索引文件的操作,这些操作需要消耗额外的IO,会降低增/改/删的执行效率;
创建索引所需的注意事项:
1、较频繁的作为查询条件的字段应该创建索引;
2、唯一性太差的字段不适合单独创建索引,即使频繁作为查询条件;
3、更新非常频繁的字段不适合创建索引;原因,索引有维护成本;
4、不会出现在WHERE 子句中的字段不该创建索引;
5、索引不是越多越好;(只为必要的列创建索引)
1,不管你有多少个索引,一次查询至多采用一个索引;(索引和索引之间是独立的)
2,因为索引和索引之间是独立的,所以说每一个索引都应该是单独维护的;数据的增/改/删,会导致所有的索引都要单独维护;
索引的使用限制:
1、BLOB 和TEXT 类型的列只能创建前缀索引
2、MySQL 目前不支持函数索引(在MYSQL中,索引只能是一个列的原始值,不能把列通过计算的值作为索引);
实例:请查询xxxx年入职的员工:
SELECT * FROM emp WHERE year(hire_date)=xxxx;
问题:查询的列是在过滤之前经过了函数运算;所以,就算hire_date作为索引,year(hire_date)也不会使用索引;
解决方案:
1,BETWEEN AND;
2,在创建一列,值是year(hire_date),然后把这列的值作为索引;
3、使用不等于(!= 或者<>)的时候MySQL 无法使用索引
4、过滤字段使用了函数运算后(如abs(column)),无法使用索引
5、Join 语句中Join 条件字段类型不一致的时候,无法使用索引
6、使用LIKE 操作的时候如果条件以通配符开始( '%abc...')MySQL 无法使用索引
1字符串是可以用来作为索引的;
2字符串创建的索引按照字母顺序排序;
- 使用非等值查询的时候MySQL 无法使用Hash 索引
- 查看MYSQL的执行计划和执行明细状态(explain+profiling)
1、Explain:可以让我们查看MYSQL执行一条SQL所选择的执行计划;
2、Profiling:可以用来准确定位一条SQL的性能瓶颈;
EXPLAIN:
1、使用方式:
explain SQL;
2、返回结果:
1、ID:执行查询的序列号;
2、elect_type:使用的查询类型
DEPENDENT SUBQUERY:子查询中内层的第一个SELECT,依赖于外部查询的结果集;
DEPENDENT UNION:子查询中的UNION,且为UNION 中从第二个SELECT 开始的后面所有SELECT,同样依赖于外部查询的结果集;
PRIMARY:子查询中的最外层查询,注意并不是主键查询;
SIMPLE:除子查询或者UNION 之外的其他查询;
SUBQUERY:子查询内层查询的第一个SELECT,结果不依赖于外部查询结果集;
UNCACHEABLE SUBQUERY:结果集无法缓存的子查询;
UNION:UNION 语句中第二个SELECT 开始的后面所有SELECT,第一个SELECT 为PRIMARY
UNION RESULT:UNION 中的合并结果;
3、table:这次查询访问的数据表;
4、type:对表所使用的访问方式:
all:全表扫描
const:读常量,且最多只会有一条记录匹配,由于是常量,所以实际上只需要读一次;
eq_ref:最多只会有一条匹配结果,一般是通过主键或者唯一键索引来访问;
fulltext:全文检索,针对full text索引列;
index:全索引扫描;
index_merge:查询中同时使用两个(或更多)索引,然后对索引结果进行merge 之后再读取表数据;
index_subquery:子查询中的返回结果字段组合是一个索引(或索引组合),但不是一个主键或者唯一索引;
rang:索引范围扫描;
ref:Join 语句中被驱动表索引引用查询;
ref_or_null:与ref 的唯一区别就是在使用索引引用查询之外再增加一个空值的查询;
system:系统表,表中只有一行数据;
unique_subquery:子查询中的返回结果字段组合是主键或者唯一约束;
5、possible_keys:可选的索引;如果没有使用索引,为null;
6、key:最终选择的索引;
7、key_len:被选择的索引长度;
8、ref:过滤的方式,比如const(常量),column(join),func(某个函数);
9、rows:查询优化器通过收集到的统计信息估算出的查询条数;
10、Extra:查询中每一步实现的额外细节信息
1,Distinct:查找distinct 值,所以当mysql 找到了第一条匹配的结果后,将停止该值的查询而转为后面其他值的查询;
2,Full scan on NULL key:子查询中的一种优化方式,主要在遇到无法通过索引访问null值的使用使用;
3,Impossible WHERE noticed after reading const tables:MySQL Query Optimizer 通过收集到的统计信息判断出不可能存在结果;
4,No tables:Query 语句中使用FROM DUAL 或者不包含任何FROM 子句;
5,Not exists:在某些左连接中MySQL Query Optimizer 所通过改变原有Query 的组成而使用的优化方法,可以部分减少数据访问次数;
6,Select tables optimized away:当我们使用某些聚合函数来访问存在索引的某个字段的时候,MySQL Query Optimizer 会通过索引而直接一次定位到所需的数据行完成整个查询。当然,前提是在Query 中不能有GROUP BY 操作。如使用MIN()或者MAX()的时候;
7,Using filesort:当我们的Query 中包含ORDER BY 操作,而且无法利用索引完成排序操作的时候,MySQL Query Optimizer 不得不选择相应的排序算法来实现。
8,Using index:所需要的数据只需要在Index 即可全部获得而不需要再到表中取数据;
9,Using index for group-by:数据访问和Using index 一样,所需数据只需要读取索引即可,而当Query 中使用了GROUP BY 或者DISTINCT 子句的时候,如果分组字段也在索引中,Extra 中的信息就会是Using index for group-by;
10,Using temporary:当MySQL 在某些操作中必须使用临时表的时候,在Extra 信息中就会出现Using temporary 。主要常见于GROUP BY 和ORDER BY 等操作中。
11,Using where:如果我们不是读取表的所有数据,或者不是仅仅通过索引就可以获取所有需要的数据,则会出现Using where 信息;
12,Using where with pushed condition:这是一个仅仅在NDBCluster 存储引擎中才会出现的信息,而且还需要通过打开Condition Pushdown 优化功能才可能会被使用。控制参数为engine_condition_pushdown 。
- profiling:
Query Profiler是MYSQL5.1之后提供的一个很方便的用于诊断Query执行的工具,能够准确的获取一条查询执行过程中的CPU,IO等情况;
1,开启profiling:set profiling=1;
2,执行QUERY,在profiling过程中所有的query都可以记录下来;
3,查看记录的query:show profiles;
4,选择要查看的profile:show profile cpu, block io for query 6;
status是执行SQL的详细过程;
Duration:执行的具体时间;
CPU_user:用户CPU时间;
CPU_system:系统CPU时间;
Block_ops_in:IO输入次数;
Block_ops_out:IO输出次数;
profiling只对本次会话有效;
- JOIN的原理及其使用的注意事项:
1、JOIN的原理:
在mysql中使用Nested Loop Join来实现join;
A JOIN B:通过A表的结果集作为循环基础,一条一条的通过结果集中的数据作为过滤条件到下一个表中查询数据,然后合并结果;
2,JOIN的优化原则:
1、尽可能减少Join 语句中的Nested Loop 的循环总次数,用小结果集驱动大结果集;
2、优先优化Nested Loop 的内层循环;
3、保证Join 语句中被驱动表上Join 条件字段已经被索引;
4、扩大join buffer的大小;
这里笔者写了一个伪代码帮助理解(执行流程):
For(id in a.id){
Select * from b where b.a_id = a.id;
得到查询的结果集,并把结果合并。
}
如果存在where条件,那么再从结果集中进行过滤
- Sql的优化原则
[原则一:选择需要优化的SQL]
1,选择需要优化的SQL:不是所有的SQL都需要优化,在优化的过程中,首选更需要优化的SQL;优先选择优化高并发低消耗的SQL;
1小时请求1W次,1次10个IO;
1小时请求10次,1次1W个IO;
从单位时间产生的IO总数来说,相同的;
针对一个SQL,如果能把10个IO变成7个IO,一小时减少3W个IO;针对第二个SQL,如果能把1W个IO变成7K个IO,一小时减少3W个IO;从优化难度上讲,1W->7K难的多;从整体性能上来说,第一个SQL的优化能够极大的提升系统整体的性能;第二个SQL慢一点,无非也就是10个连接查询慢一点;
2,定位性能瓶颈;
1,SQL运行较慢有两个影响原因,IO和CPU,明确性能瓶颈所在;
2,明确优化目标;
[原则二:从Explain和Profile入手]
1,任何SQL的优化,都从Explain语句开始;Explain语句能够得到数据库执行该SQL选择的执行计划;
2,首先明确需要的执行计划,再使用Explain检查;
3,使用profile明确SQL的问题和优化的结果;
[原则三:永远用小结果集驱动大的结果集]
[原则四:在索引中完成排序]
[原则五:使用最小Columns]
1,减少网络传输数据量;
2,特别是需要使用column排序的时候.为什么?MYSQL排序原理,是把所有的column数据全部取出,在排序缓存区排序,再返回结果;如果column数据量大,排序区容量不够的时候,就会使用先column排序,再取数据,再返回的多次请求方式;
[原则六:使用最有效的过滤条件]
1,过多的WHERE条件不一定能够提高访问性能;
2,一定要让where条件使用自己预期的执行计划;
[原则七:避免复杂的JOIN和子查询]
1,复杂的JOIN和子查询,需要锁定过多的资源,MYSQL在大量并发情况下处理锁定性能下降较快;
2,不要过多依赖SQL的功能,把复杂的SQL拆分为简单的SQL;
3,MySQL子查询性能较低,应尽量避免使用;
- innobuffer和事务
1,Innodb_buffer_pool_size:innodb的缓存,可以用于缓存索引,同时还会缓存实际的数据;
innodb_buffer_pool_size 参数用来设置Innodb 最主要的Buffer(Innodb_Buffer_Pool)的大小,对Innodb 整体性能影响也最大,可以按需要设置大一些;
2,innodb中的事务处理:
1,理解Innodb事务机制:
1,事务在buffer中对数据进行修改;
2,事务的变化记录在事务日志中;
3,在合适的时机同步事务日志中的数据到数据库中;
2,所以什么时候提交事务日志文件,对系统性能影响较大,可以通过设置innodb_flush_log_at_trx_commit来修改事务日志同步时机:
1,innodb_flush_log_at_trx_commit = 0,每1秒钟同步一次事务日志文件;
2,innodb_flush_log_at_trx_commit = 1,默认设置,每一个事务完成之后,同步一次事务日志文件;
3,innodb_flush_log_at_trx_commit = 2,事务完成之后,写到事务日志文件中,等到日志覆盖再同步数据;
注意,1性能最差,2不能完全保证数据是写到数据文件中,如果宕机,可能会有数据丢失现象,但性能最高;1,性能和安全性居中;