MySQL性能优化(索引优化)

文章目录

SQL的执行过程:

    1. 客户端发送一条查询给服务器;

    2. 服务器通过权限检查之后,先会检查查询缓存,如果命中了缓存,则立即返回存储在缓存中的结果。否则进入下一阶段;

    3. 服务器端进行SQL解析、预处理,再由优化器根据该SQL所涉及到的数据表的统计信息进行计算,生成对应的执行计划;

    4. MySQL根据优化器生成的执行计划,调用存储引擎的API来执行查询;

    5. 将结果返回给客户端。

在这里插入图片描述

schema(表结构)对性能的影响:

1. 冗余数据的处理:

    ① 适当的数据冗余可以提高系统的整体查询性能

    ②关系数据库的三范式:

扫描二维码关注公众号,回复: 9853183 查看本文章

     第一范式(1NF)是对关系模式的基本要求,不满足第一范式(1NF)的数据库就不是关系数据库,是指数据库表的每一列都是不可分割的基本数据项,同一列中不能有多个值;

     第二范式(2NF)要求数据库表中的每个实例或行必须可以被惟一地区分。

     第三范式(3NF)要求一个数据库表中不包含已在其它表中已包含的非主关键字信息。 (不允许有冗余数据) 。

2. 大表拆小表,有大数据的列单独拆成小表:

     ① 在一个数据库中,一般不会设计属性过多的表;

     ② 在一个数据库中,一般不会有超过500/1000万数据的表(拆表,按照逻辑拆分,按照业务拆分);

     ③有大数据的列单独拆成小表(富文本编辑器,CKeditor)。

3. 根据需求的展示设置更合理的表结构。

4. 把常用属性分离成小表:

      ①把经常用到的属性相关的表拆成多张表;

      ②减少查询常用属性需要查询的列;

      ③便于常用属性的集中缓存。

注意: SQL执行的最大瓶颈在于磁盘的IO,即数据的读取;不同SQL的写法,会造成不同的执行计划的执行,而不同的执行计划在IO的上面临完全不一样的数量级,从而造成性能的差距;所以说,优化SQL,其实就是让查询优化器根据程序猿的计划选择匹配的执行计划,来减少查询中产生的IO。

索引

原理:把无序的数据变成有序的查询

在这里插入图片描述

SELECT * from account_table where account_id = 6;

1. 如果在没有索引的情况下:

    ①遍历整张表的内容,比较account_id是否为6;

    ②如果为6,就把这条数据放在内存中的结果集中;

    ③这种情况下的问题是全表扫描,需要把表的所有数据查询一遍(IO多);

2. 在account_id这一列创建一个索引:

    ①把数据按account_id进行排序;

    ②把排序的结果变成一个倒排表;

3. 在查询的时候:

    ① 在索引中查询account_id为6的那条倒排表数据;

    ②顺序得到id为1,3,4,8,9,13,14对应的数据;

    ③把数据放到结果集中;

4. 在修改一条数据的时候:

    ① 如果是增加了一条数据(account_id = 8, id = 19);往account_id为8的倒排表中插入;

    ② 如果是删除了一条数据,查询到这条数据在account_id对应索引的位置,然后从倒排表中删除这条数据;

    ③如果是修改一条数据,若涉及到索引列值的改变,则重复(2,1)

在这里插入图片描述

索引的物理结构:

1,数据库文件存储的位置:my.ini配置文件中dataDir对应的数据目录中;

2,每一个数据库一个文件夹;

   ①MYISAM引擎:每一个表(t_name)-->

         table_name.MYI:存放的是数据表对应的索引信息和索引内容;

         table_name.FRM:存放的是数据表的结构信息;

         table_name.MYD:存放的是数据表的内容;

   ②InnoDB引擎:每一个表(t_name)-->

         table_name.frm:存放的是数据表的结构信息;

         数据文件和索引文件都是统一存放在ibdata文件中;

   ③索引文件都是额外存在的,对索引的查询和维护都是需要消耗IO的;

索引的结构:

1,默认情况下,一旦创建了一个表,这个表设置了主键,那么MYSQL会自动的为这个主键创建一个unique的索引;

2,索引类型:

        ①Normal:普通的索引;允许一个索引值后面关联多个行值;

        ②UNIQUE:唯一索引;允许一个索引值后面只能有一个行值;之前对列添加唯一约束其实就是为这列添加了一个unique索引;当我们为一个表添加一个主键的时候,其实就是为这个表主键列(设置了非空约束),并为主键列添加了一个唯一索引;

        ③Fulltext:全文检索,mysql的全文检索只能用myisam引擎,并且性能较低,不建议使用;

    3,索引的方法(规定索引的存储结构): 

         ①b-tree:是一颗树(二叉树,平衡二叉树,平衡树(B-TREE))

         使用平衡树实现索引,是mysql中使用最多的索引类型;在innodb中,存在两种索引类型,第一种是主键索引(primary key),在索引内容中直接保存数据的地址;第二种是其他索引,在索引内容中保存的是指向主键索引的引用;所以在使用innodb的时候,要尽量的使用主键索引,速度非常快;b-tree中保存的数据都是按照一定顺序保存的数据,是可以允许在范围之内进行查询;

         ②hash:把索引的值做hash运算,并存放到hash表中,使用较少,一般是memory引擎使用;优点:因为使用hash表存储,按照常理,hash的性能比B-TREE效率高很多。          

        hash索引的缺点:

            ⑴hash索引只能适用于精确的值比较,=,in,或者<>;无法使用范围查询;

            ⑵无法使用索引排序;

            ⑶组合hash索引无法使用部分索引;

            ⑷如果大量索引hash值相同,性能较低;

索引的利弊:

1,索引的好处:

     ①提高表数据的检索效率;

     ②如果排序的列是索引列(如果查询的列==排序的列[并且在这列上做了索引]),大大降低排序成本;

     ③在分组操作中如果分组条件是索引列,也会提高效率;

2,索引的问题:

       索引需要额外的维护成本;因为索引文件是单独存在的文件,对数据的增加,修改,删除,都会产生额外的对索引文件的操作,这些操作需要消耗额外的IO,会降低增/改/删的执行效率;

怎么创建索引:

1,较频繁的作为查询条件的字段应该创建索引;

2,唯一性太差的字段不适合单独创建索引,即使频繁作为查询条件;作为索引的列,如果不能有效的区分数据,那么这个列就不适合作为索引列;比如(性别,状态不多的状态列)  

       举例:SELECT sum(amount) FROM account_table WHERE accountType = 0;

       假如把accountType作为索引列,因为accountType只有14种,所以,如果根据accountType来创建索引,最多只能按照1/14的比例过滤掉数据;但是,如果可能出现,只按照该条件查询,那我们就要考虑到其他的提升性能的方式了;

      第一种方案:单独创建一个系统摘要表;在这个表里面有一个列叫做系统总充值金额;每次充值成功,增加这个列的值;以后要查询系统总充值金额,只需要从这个系统摘要表中查询;(缺陷:如果充值频率过快,会导致表的锁定问题;)

      第二种方案:流水一旦发生了,是不会随着时间改变的;针对这种信息,我们就可以使用增量查询(结算+增量查询);

      ①创建一张日充值表;记录每一天的充值总金额,每天使用定时器对当前的充值记录进行结算;日充值报表里面记录只能记录截止昨天的数据;

      ②创建一张月充值表;记录每一个月的充值总金额,每月最后一天使用定时器对当月的充值记录进行结算(数据源从日充值报表来);

      ③要查询系统总充值,从月报表中汇总(当前月之前的总充值金额),再从日充值报表中查询当天之前的日报表数据汇总;再从流水中查询当前截止查询时间的流水;使用另外一张当天流水表记录当天的流水;再把三个数据累加;         

3,更新非常频繁的字段不适合创建索引;原因,索引有维护成本;

4,不会出现在WHERE 子句中的字段不该创建索引;

5,索引不是越多越好;(只为必要的列创建索引)

        ①不管你有多少个索引,一次查询至多采用一个索引;(索引和索引之间是独立的)

        ②因为索引和索引之间是独立的,所以说每一个索引都应该是单独维护的;数据的增/改/删,会导致所有的索引都要单独维护;

索引的使用限制:

1,BLOB和TEXT的列只能创建前缀索引;

2,MySQL 目前不支持函数索引(在MYSQL中,索引只能是一个列的原始值,不能把列通过计算的值作为索引);

    实例:请查询1981年入职的员工:

    SELECT * FROM emp WHERE year(hire_date)='1981';

    问题:查询的列是在过滤之前经过了函数运算;所以,就算hire_date作为索引,year(hire_date)也不会使用索引;

    解决方案:

        ①SELECT * FROM emp WHERE hire_date BETWEEN '1981-01-01' AND '1981-12-31';

        ②在创建一列,这列的值是year(hire_date),然后把这列的值作为索引;     

3,使用不等于(!= 或者<>)的时候MySQL 无法使用索引;

4,过滤字段使用了函数运算后(如abs(column)),MySQL 无法使用索引;

5, Join 语句中Join 条件字段类型不一致的时候MySQL 无法使用索引;

6,使用LIKE 操作的时候如果条件以通配符开始( '%abc...')MySQL 无法使用索引;

        ①字符串是可以用来作为索引的;

        ②字符串创建的索引按照字母顺序排序;

        ③如果使用LIKE,实例:SELECT * FROM userinfo WHERE realName LIKE '黄%';这种情况是可以使用索引的;但是LIKE '_新' 或者LIKE '%新'都是不能使用索引的;

7,使用非等值查询的时候MySQL 无法使用Hash 索引

单列索引和复合索引:

1,因为一个查询一次至多只能使用一个索引,所以,如果都使用单值索引(一个列一个索引),在数据量较大的情况下,不能很好的区分数据;

2,所以,MYSQL引入了多值索引(复合索引); 复合索引就是由多列的值组成的索引;并且(注意),多列的索引是有顺序的;

3,复合索引的原理:就是类似orderby(orderby后面可以跟多个排序条件order by hire_date,username desc);就是在排序和分组(创建倒排表的时候),按照多个列进行排序和合并;

4,复合索引,在查询的时候,遵守向左原则;只要在查询的时候,是按照复合索引从左到右的顺序依次查询,不管查询条件是否完全满足所有的符合索引的列,都可以使用部分的符合索引;

5,在实际应用中,基本上都使用复合索引;

总结:

    原则一:选择需要优化的SQL

    原则二:从Explain和Profile入手

    原则三:永远用小结果集驱动大的结果集

    原则四:在索引中完成排序

    原则五:使用最小Columns

    原则六:使用最有效的过滤条件

    原则七:避免复杂的JOIN和子查询

发布了338 篇原创文章 · 获赞 244 · 访问量 10万+

猜你喜欢

转载自blog.csdn.net/BeiisBei/article/details/104888825