【原创】mysql优化技巧_2018-12-23

说明

mysql数据库是一个存储和检索数据的地方,通过使用对数据库表中一列或多列的值进行排序的一种存储结构索引,减少硬盘IO使检索数据更高效。
MyISAM引擎非聚集索引数据文件和索引文件分离,叶子节点存物理地址,通过索引查找到后,就得到了数据的物理地址,然后根据地址定位数据文件中的记录,引擎表级锁,无事物,通常不用;
Innodb引擎数据文件本身是索引文件,叶子节点存数据就是聚集索引;Innodb主键索引中叶子节点存储的是数据内容,而普通索引的叶子节点中存储的是主键值!所以其主键很重要!
本文只考虑数据库本身优化,不涉及引入其他互联网组件 缓存,消息队列等

1,使用mysql自带工具检测sql

使用explain或describe修饰sql查看mysql如何使用索引来处理select语句以及连接表。可以帮助选择更好的索引和写出更优化的查询语句
使用 profile 查看 SQL 会执行多少时间, 并看出 CPU/Memory 使用量, 执行过程中 Systemlock, Table lock 花多少时间等
使用SHOW VARIABLES LIKE "%CACHE%"查看数据库缓存信息
1.1,select_type 表示查询中每个select子句的类型
1.2,type 表示MySQL在表中找到所需行的方式,又称“访问类型”
1.3,possible_keys 指出MySQL能使用哪个索引在表中找到行,查询涉及到的字段上若存在索引,则该索引将被列出,但不一定被查询使用
1.4,key 显示MySQL在查询中实际使用的索引,若没有使用索引,显示为NULL
1.5,key_len 表示索引中使用的字节数,可通过该列计算查询中使用的索引的长度
1.6,ref 表示上述表的连接匹配条件,即哪些列或常量被用于查找索引列上的值
1.7,Extra 包含不适合在其他列中显示但十分重要的额外信息
1.8,rows 这个量最重要,是本次查询扫描的数据行数,扫描的越小,越快速!!!

2,对查询进行优化,减少查询扫描的记录数,要尽量避免全表查询和子查询嵌套全表全量查询

2.1,join和limit分页加入条件提前缩减记录数,减少做笛卡尔积的表的记录数量
2.2,select里面的嵌套子查询dependent subquery子查询数量受外部查询记录数影响,使用连接查询(join)代替子查询可以提高查询效率。

3,对查询进行优化,考虑在 where 及 order by 涉及的列上建立索引,以空间换时间

3.1,索引可以提高相应的 select 的效率,但同时也降低了 insert 及 update 的效率(更新重新创建索引),且索引是的创建的数据文件更大,索引数量不宜过多,索引字段长度不宜过长
3.1,mysql会在主键上面自动建立主键索引,主键索引是加在主键上的索引,设置主键(primary key)的时候,mysql会自动创建主键索引
3.2,每张表可以为经常查询且高度唯一的字段创建一个聚集索引,聚集索引列的顺序就是表记录的物理存储顺序,要避免频繁调整
3.3,用于创建聚合索引(复合索引)最好的备选数据列是那些出现在WHERE子句、join子句(连接字段)、ORDER BY或GROUP BY子句中的列,对排序、分组、联合操作,查询条件建立联合索引为所搜的字段创建索引
3.4,对WHERE后的多个检索条件建立联合索引,在创建多列索引时,要根据业务需求,where子句中使用最频繁的一列放在最左边,建立三列复合索引相当于建立单列索引,复合索引,三列索引三个索引
3.5,当索引列有大量数据重复时,SQL查询可能不会去利用索引,比如male、female
3.6,对于复合索引,必须使用到该索引中的第一个字段作为条件时才能保证系统使用该索引,否则该索引将不会被使用
并且应尽可能的让查询条件顺序与索引字段顺序相一致
注意最左前缀匹配原则,并且将索引建立在区分度高的列。区分度的计算公式为count(distinct col)/count(*)
复合索引如果单独使用,只有复合索引里第一个字段有效
最左前缀匹配原则,mysql会一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配
3.7,索引是检索类字段,不是业务类字段,不要再索引上做统计聚合操作

4,避免使用使索引失效的sql语句,使用这些语句的时候请放到能有效使用索引的条件的右边

4.1,避免在 where 子句中对字段进行null值判断,提前在判空字段赋默认值0或999,避免 IS NULL 判断
4.2,避免在 where 子句中使用 != 或 <>(不等于)操作符,引擎将放弃使用索引而进行全表扫描
4.3,避免在 where 子句中使用 or 来连接条件,可以写单独的sql然后用 UNION 连接
4.4,避免在 where 子句中使用not exists 和 not in,对于连续的数值使用 between 1 and 3
4.5,避免在 where 子句中的“=”左边进行函数、算术运算或其他表达式运算,改 num/2=100 为 num=100*2
4.6,避免在 where 自居中使用 LIKE ‘%apple’ ,会引发索引失效(考虑MyISAM引擎的全文检索),LIKE ‘str%’可以使用索引
避免类型转换,这里所说的“类型转换”是指where子句中出现字段的类型和传入的参数类型不一致的时候发生的类型转换。

5,其他优化

5.1,数据行的长度不要超过8020字节,如果超过这个长度的话在物理页中这条数据会占用两行从而造成存储碎片,降低查询效率
5.2,尽量使用数字型字段代替字符型,引擎在处理查询和连接时会逐个比较字符串中每一个字符,而对于数字型而言只需要比较一次就够了
Int类型部分时候可以通过大于或小于代替"!="等条件,同时也方便满足一些需要按类型排序的需求
5.3,使用varchar/nvarchar 代替 char/nchar,前者存储空间更小,char查询快,但是耗存储空间
5.4,使用ENUM 代替 VARCHAR 用于性别、民族、国家的字段
5.5,当只要一行数据时使用 LIMIT 1,MySQL数据库引擎会在找到一条数据后停止搜索,而不是继续往后查少下一条符合记录的数据。
5.6,不要使用select * from t
5.7,使用 exists 代替 in ,select num from a where exists(select 1 from b where num=a.num)
5.8,COUNT()走的永远是最优索引,再不能确定那个字段计算数量最优的时候,使用count()
5.9,统计相关的查询。影响结果集往往巨大,且部分SQL语句本身已经难以优化。因此,应避免在业务高峰期执行统计相关的查询,或者仅在从库中执行统计查询。部分统计数据,可以通过冗余的数据结构保存
5.10,表间关联,尽可能的降低数据的冗余

6,大数据量优化策略

6.1,尽量避免大事务锁表操作,拆分大的事物为几个独立的事物,提高系统并发能力。
6.2,插入大量数据时候使用多线程、存储过程机制提高效率
6.3,删除大量数据时,先备份删除索引,删除完成后再重新创建索引,删除单条数据时,重建索引会消耗大量资源
6.4,大数据量分页观察id的分布规律计算出其id的范围通过范围查找来实现分页效果,例如自增主键,select * from goods where id > 8000000 limit 1000;
6.5,大数据量分页查询主键select id from table where .. order by .. limit 10000,10(搜索条件和排序请建立索引),再通过主键去获取数据。
6.6,数据量小时,联合查询比较简便,10万记录以上不建议;
6.7,配合索引垂直分割表的常改常查字段的列和非常改常查字段的列
6.8,做一个数据表对应的索引表,与数据表同时插入数据,专门存放索引字段数据,分页检索到时候,已索引表作为条件缩小范围

7,数据量巨大 分库分表,业务拆分,主从复制

7.1,对于mysql,其数据文件是以文件形式存储在磁盘上的。当一个数据文件过大的时候,操作系统对大文件的操作就会比较麻烦与耗时,而且有的操作系统就不支持大文件
数据文件过大的时候,B+树就会从层次和节点上比较多,当查询一个节点的时候可能会查询很多层次,而这必定会导致多次IO操作进行装载进内存,肯定会耗时的。
Innodb对于B+树的锁机制。对每个节点进行加锁,那么当更改表结构的时候,这时候就会树进行加锁,当表文件大的时候,这可以认为是不可实现的
随着数据量的增加,连表操作往往会导致影响结果集大增,从SQL优化的层面已经解决不了问题了
7.2,核心点就是必须先去除数据表之间的关联,即不用外键,不使用任何连表查询
7.3,为了确保不进行连表操作,在设计数据库表结构的时候,就需要设计适度冗余的字段来达到不连表的目的
7.4,单表查询相当于在应用中实现了哈希关联,而不是使用MySQL的嵌套循环关联,可以有效利用缓存,减少锁竞争,减少冗余记录查询,更利于后期分库分表。

8,优化MySQL的参数 my.cnf/my.ini-MySQL组

8.1,Key_buffer_size:表示索引缓冲区的大小。索引缓冲区所有的线程共享。增加索引缓冲区可以得到更好处理的索引。当然该值也不是越大越好,它的大小取决于内存的大小(如果该值太大,导致操作系统频繁换页,也会降低系统的性能)
8.2,Table_cache:表示同时打开的表的个数。这个值越大,能够同时打开的表就越多,但同时打开的表越多会影响操作系统的性能。
8.3,Query_cache_size:表示查询缓冲区的大小,该参数需要和query_cache_type配合使用。当query_cache_type的值是0时,所有的查询都不使用查询缓冲区。但是query_cache_type=0并不会导致MySQL释放query_cache_size所配置的缓冲区的内存。当query_cache_type=1时,所有的查询都将使用查询缓冲区。除非在查询语句中加select sql_no_cache * from tablename 。query_cache_type=2时,除非在查询中使用sql_cache 关键字,否则查询不会使用缓冲区。使用查询缓冲区可以提高查询的速度,这种方式只适用修改操作少并且经常执行相同的查询操作的情况。
8.4,Sort_buffer_size:表示排序缓冲区的大小,这个值越大进行排序的速度越快。
8.5,Read_buffer_size:表示每个线程连续扫描时为扫描的每个表分配的缓冲区的大小。当线程从表中连续读取数据时会用到该缓冲区。
8.6,Read_rnd_buffer_size:表示为每个线程保留的缓存区的大小,与Read_buffer_size相似。但主要用于存储按特定顺序读取出来的记录。
8.7,Innodb_buffer_pool_size:表示InnoDB类型的表和索引的最大缓存。这个值越大,查询的速度就会越快,但这个值太大会影响操作系统的性能。
8.8,Max_connections:表示数据库的最大连接数。这个数据不是越大越好,因为这些链接会浪费内存资源,过多的连接可能会导致MySQL服务器僵死。
8.9,Innodb_flush_log_at_trx_commit:表示何时将缓冲区的数据写入日志文件,并且将日志文件写入磁盘。该参数对于innoDB引擎非常重要。该参数有3个值,分别为0、1、2.值为0时表示每隔一秒将数据写入日志文件并将日志写入磁盘;值为1时表示每次提交事务时将数据写入日志文件并将日志文件写入磁盘;值为2时表示每次提交事务时将数据写入磁盘,每隔一秒将日志文件写入磁盘。该参数的默认值为1,值为1时安全性最高,但是每次事务提交或事务外的指令都需要把日志写入硬盘,是比较费时的;值为0时更快一些,但安全方面比较差;值为2时日志仍然会每秒都写入硬盘,所以即使出现故障,一般也不会丢失超过1~2秒的更新。
8.10,Back_log:表示在MySQL暂时停止回答新请求之前的短时间内,多少个请求可以被存在堆栈中。换句话说,该值表示到来的TCP/IP连接的侦听队列的大小。
8.11,Interactive_timeout:表示服务器在关闭连接前等待行动的秒数。
8.12,Sort_buffer_size:表示每个需要进行排序的线程分配的缓冲区的大小,增加这个参数可以提高order by或group by操作的速度。
8.13,Thread_cache_size:表示可以复用的线程的数量。
8.14,Wait_timeout:表示服务器在关闭一个连接时等待行动的秒数。

你可能感兴趣的:(【原创】mysql优化技巧_2018-12-23)