sql

1 sql优化步骤

1.1 查看sql执行频率

show status like 'Com_______';  # 7个下划线,代替一个字符,查看当前连接的统计结果
show global status like 'Com_______';  # 7个下划线,代替一个字符,查看当前连接的统计结果
image.png
show [global] status like 'Innodb_rows_%'; #查看所有innodb引擎的状态
image.png

1.2 定位低效率执行SQL

  1. 慢查询日志(后面再详细说)
  2. show processlist:该命令可以查看当前mysql正在进行的线程,包括线程的状态、是否锁表等,可以实时的查看sql的执行情况,同时对一些锁表操作进行优化。
show processlist;
image.png

可以定位到当前慢查询的sql语句,Sengding data。

1.3 explain分析执行计划

查询到效率低的sql语句后,可以用过explain或者desc命令获取mysql如何执行select语句的信息,包括在select语句执行过程中表如何连接和连接的顺序。

explain select * from tb_item where id=1;
image.png
image.png
列名 说明
id 执行编号,标识select所属的行。如果在语句中没子查询或关联查询,只有唯一的select,每行都将显示1,从上到下顺序执行。否则,内层的select语句一般会顺序编号,对应于其在原始语句中的位置,序号越大优先级越高,先执行
select_type 显示本行是简单或复杂select。如果查询有任何复杂的子查询,则最外层标记为PRIMARY(DERIVED、UNION、UNION RESUlT)
table 访问引用哪个表(引用某个查询,如“derived3”)
type 数据访问/读取操作类型(ALL、index、range、ref、eq_ref、const/system、NULL)
possible_keys 揭示哪一些索引可能有利于高效的查找
key 显示mysql决定采用哪个索引来优化查询(若为null表示没有用索引)
key_len 显示mysql在索引里使用的字节数
ref 显示了之前的表在key列记录的索引中查找值所用的列或常量
rows 为了找到所需的行而需要读取的行数,估算值,不精确。通过把所有rows列值相乘,可粗略估算整个查询会检查的行数
Extra 额外信息,如using index、filesort等

1.4 show profile分析SQL

  1. 查看mysql是否支持
select @@have_profiling;
image.png
  1. 查看是否开启profile
select @@profiling;
image.png

为0表示当前为开启

  1. 为本次会话开启支持(仅当前登录有效)
set profiling=1
image.png
  1. 输入任意sql,profile会记录时间
  2. 查看sql执行的时间
show profiles;
image.png
  1. 查询具体sql执行过程中每个线程的状态和消耗的时间(all可以查看所有信息,包括cpu等,也可以把all换成cpu等只查看cpu等信息)
show profile [all] for query query_id; 
image.png

Sending data状态表示mysql线程开始访问数据行并把结果返回给客户端整个过程执行的时间。

1.5 trace分析优化器执行计划

通过trace文件能够进一步了解为什么优化器选择A计划而不是选择B计划。
打开trace,设置格式为JSON,并设置trace最大能够使用的内存大小,避免解析过程中因为默认内存国小而不能够完整展示。

set optimizer_trace="enabled=on",end_markers_in_json=on;
set optimizer_trace_max_mem_size=1000000;
image.png

执行sql语句,然后通过以下指令查看

select * from information_schema.optimizer_trace\G;

2 索引的使用

使用索引可以加快查询的速度,可以为经常查询的字段添加索引。

create index index_name on table_name(name,stauts...);

2.1 避免索引失效

  1. 全值匹配,对索引中所有列都指定具体值,该情况下,索引生效,执行效率高。
image.png
  1. 最左前缀法则
    如果索引了多列,要遵守最左前缀法则。指的是查询从索引的最左前列开始,并且不跳过索引中的列。

  2. 范围查询右边的列,不能使用索引。

  3. 不要在索引列上进行运算操作,索引将失效。

  4. 字符串不加单引号,造成索引失效(由于存在类型转换,相当于进行了运算操作)。

  5. 尽量使用覆盖索引,避免select *,查询列超出索引列,也会影响性能

TIP:
using index:使用覆盖索引的时候会出现
using where:在查找使用索引的情况下,需要回表去查询所需的数据
using index condition:查找使用了索引,但是需要回表查询数据
using index;using where:查找使用了索引,但是需要的数据都在索引列中能找到,所以不需要回表查询数据

  1. 用or分割开的条件,如果or前的条件中列有索引,二后面的列中没有索引,那么涉及的索引都不会被用到。及or前有索引,or后无索引,则最后查询不走索引(and会走索引)

  2. 以%开头的Like模糊查询,索引失效(可以通过覆盖索引来解决问题,使用索引)
    如果仅仅是尾部模糊匹配,索引不会失效。如果是头部模糊匹配,索引失效

  3. 如果mysql评估使用索引比全表更慢,则不使用索引

  4. is null,is not null 有时索引失效

  5. in走索引,not in索引失效。

  6. 单列索引和复合索引
    尽量使用复合索引,而少使用单列索引。(最左前缀原则)
    单列索引mysql只会选择最优的索引(辨识度最高的索引),而不会使用全部的索引。

2.2 查看索引的使用情况

show [global] status like 'Handler_read%';

3 SQL优化

3.1 大批量插入数据

使用 load 命令导入数据的时候,适当的设置可以提高导入的效率。

load data local infile 'file_path' into table `table_name` fields terminated by ',' lines terminated by '\n';

对于InnoDB类型的表,有以下几种方式可以提高导入的效率:

  1. 主键顺序导入
    因为InnoDB类型的表是按照主键的顺序保存的,所以当导入的数据按照主键的顺序排序,可以提高效率。
  2. 关闭唯一性校验
    在导入数据前执行 SET UNIQUE_CHECKS=0 关闭唯一性校验,再导入结束后执行 SET UNIQUE_CHECKS=1,恢复唯一性校验,可以提高效率。
  3. 手动提交事务
    在导入数据前执行 SET AUTOCOMMIT=0 关闭自动提交,再导入结束后执行 SET AUTOCOMMIT=1,恢复自动提交,可以提高效率。

3.2 优化insert语句

  • 将多条insert语句改成1条insert语句
  • 在事务中进行数据插入(相当于暂时关闭自动提交)
  • 数据有序插入
image.png

3.3 order by优化

尽量减少格外的排序,通过索引直接返回有序数据。
类似于覆盖索引,select返回的结果只查询有索引的字段,这样可以通过索引排序,提升效率,否则将通过文件排序。
按照多个字段进行排序时,最好全升序或者全降序。

3.4 group by优化

可以在最后加上order by null禁止排序来提升效率。
也可以通过创建索引提高效率。

3.5 子查询优化

使用多表联查替换子查询

3.6 or优化

对于包含or的人查询子句,如果要利用索引,则or之间的每个条件列都必须用到单列索引,而且不能使用到复合索引;如果没有索引,则应该考虑增加索引。建议使用union替换or。

3.7 优化分页查询

一般分页查询时,通过创建覆盖索引能够比较好的提高性能。一个常见的问题是 limit 2000000,10,此时mysql排序前2000010记录,仅仅返回2000000-2000010的记录,其他记录丢弃,查询排序的代价非常大。
优化:

  1. 在索引上完成排序分页操作,最后根据主键关联回原表查询所需要的其他列内容,如:
select * from item t, (select id from item order by id limit 2000000,10) a where t.id=a.id;
  1. 该方案适用于主键自增的表,可以把limit查询转换成某个位置的查询,但如果主键出现断层则不准确了,如:
select * from item where id>2000000 limit 10;

3.8 使用sql提示

即在sql语句中加入一些人为的提示来达到优化操作的目的。
使用use index或者ignore index强制指定索引。或者通过force index强制使用索引。

4 应用优化

4.1 使用数据库连接池

4.2 减少对mysql的访问

  1. 避免对数据进行重复检索
  2. 增加cache层

4.3 负载均衡

  1. 利用mysql复制分流查询(mysql主从复制)
  2. 采用分布式数据库架构

5 mysql内存管理及优化

5.1 内存优化原则

  1. 将尽量多的内存分配给mysql做缓存,但是要给操作系统和其他程序预留足够的内存。
  2. myisam存储引擎的数据文件读取依赖于操作系统自身的IO缓存,因此,如果有myisam表,就要预留更多的内存给操作系统做IO缓存。
  3. 排序区、连接区等缓存是分配给每个数据库会话(session)专用的,其默认值的设置要根据最大连接数合理分配,如果设置太大,不但浪费资源,而且在并发连接较高时会导致物理内存耗尽。

5.2 MyISAM内存优化

MyISAM存储引擎使用key_buffer缓存索引块,加速MyISAM索引的读写速度。对于MyISAM表的数据块,mysql没有特别的缓存机制,完全依赖于操作系统的IO缓存。

key_buffer_size
该参数决定MyISAM索引块缓存区的大小,直接影响到MyISAM表的存取效率。可以在MySQL参数文件中设置,一般建议至少将1/4可用内存分配给该参数。
在/usr/my.cnf中进行配置,如:

key_buffer_size=512M

read_buffer_size
如果需要经常顺序扫描myisam表,可以通过增大read_buffer_size的值来改善性能。但需要注意的是read_buffer_size是每个session独占的,如果设置太大,会造成内存浪费。

read_rnd_buffer_size
如果需要做排序的myisam表的查询,如带有order by子句的sql,适当增加read_rnd_buffer_size的值,可以改善此类的sql性能。但需要注意的是read_rnd_buffer_size是每个session独占的,如果设置太大,会造成内存浪费。

5.2 InnoDB内存优化

Innodb用一块内存区做IO缓存池,该缓存池不仅用来缓存innodb的索引块,而且也用来缓存innodb的数据块。

innodb_buffer_pool_size
该参数决定了innodb存储引擎表数据和索引数据的最大缓存区大小。在保证操作系统和其他程序有足够内存可用的情况下,该值越大,缓存命中率越高,访问innodb表需要的磁盘i/o就越少,性能也就越高。

innodb_buffer_pool_size=512M

innodb_log_buffer_size
决定了innodb重做日志缓存的大小,对于可能产生大量更新记录的大事物,增加该参数大小,可以避免innodb在事务提交前就执行不必要的日志写入磁盘操作

innodb_log_buffer_size=10M

6 Mysql并发参数调整

在mysql中,控制并发连接和线程的主要参数包括max_connections、back_log、thread_cache_size、table_open_cache。

6.1 max_connections

该参数控制允许连接到mysql数据库的最大数量,默认值是151。如果状态变量connect_errors_max_connections不为零,并且一直增长,则说明不断有连接请求因数据库连接数已达到允许最大值而失败,这时可以考虑增加max_connections的值。

mysql最大可支持的连接数,取决于很多因素,包括内存大小,每个连接负荷,cpu处理速度等。所以不是设置的越大越好。

6.2 back_log

该参数控制mysql监听tcp端口时设置的积压请求栈大小。如果mysql的连接数达到max_connections,新来的请求将会存在堆栈中,该堆栈数量即为back_log,如果等待连接的数量超过back_log,将不被授予连接资源,将会报错。如果需要短时间内处理大量请求,可以适当增大一点。

6.3 table_open_cache

该参数控制所有sql语句执行线程可打开表缓存的数量,而在执行sql语句时,每一个sql执行线程至少要打开1个表缓存。应根据最大连接数max_connections以及每个连接执行的关联查询中涉及的表的最大数量来设定。

6.4 thread_cache_size

为了加快连接数据库速度,mysql会缓存一定数量的客户服务线程以备重用,通过该参数可控制缓存客户服务线程的数量

6.5 innodb_lock_wait_timeout

该参数用来设置innodb事务等待行锁的时间,默认50ms。对于需要快速反馈的业务系统来说,可以将行锁的等待时间调小,以免事务长时间挂起;对于后台运行的批量处理程序来说,可以调大,以避免发生大的回滚操作。

7 mysql锁

7.1 锁分类

从对数据操作的粒度分:

  1. 表锁:操作时,会锁定整张表
  2. 行锁:操作时,会锁定当前操作行
    从对数据操作的类型分:
  3. 读锁(共享锁):针对同一份数据,多个读操作可以同时进行而不会互相影响
  4. 写锁(排它锁):当前操作没有完成之前,它会阻断其他写锁和读锁

7.2 mysql锁

存储引擎 表级锁 行级锁 页面锁
MyISAM 支持 不支持 不支持
InnoDB 支持 支持 不支持
MEMORY 支持 不支持 不支持
BDB 支持 不支持 支持

表级锁: 偏向MyISAM存储引擎,开销小,加锁快;不会出现死锁;锁定粒度大,发生所冲突的概率最高,并发度最低。
行级锁:偏向InnoDB存储引擎,开销大,加锁慢;会出现死锁;锁定力度小,发生锁冲突概率最低,并发度最高。
页面所:开销和加锁时间介于表锁和行锁之间;会出现死锁;锁定力度介于表锁和行锁之间,并发度一般。

7.3 MyISAM表锁

7.3.1 如何加锁

只支持表锁
select前,自动给涉及的表加读锁,执行(update、delete、insert等)前,自动给涉及的表加写锁,该过程不需要用户干预,因此一般不需要直接用lock table命令显示加锁。
显示加表锁语法:

加读锁:lock table table_name read;
加写锁:lock table table_name write;
释放锁:unlock tables;

读锁会阻塞写,但不会阻塞读。写锁既会阻塞读,又会阻塞写。

7.3.2 查看锁的争用情况

show open tables; 查看每张表的情况
show status like 'Table_locks%'; 查看所有表汇总的情况

7.4 InnoDB行锁

  1. 事务四大特性ACID
    原子性(Atomicity):事务是一个原子操作但愿,其对数据的修改,要么全部成功,要么全部失败。
    一致性(Consistent):在事务开始和完成时,数据都必须保持一致状态。
    隔离性(Isolation):数据库系统提供移动的隔离机制,保证事务在不受外部并发操作影响的“独立”环境下运*行。
    持久性(Durable):事务完成之后,对于数据的修改是永久的。

  2. 并发事务处理带来的问题
    丢失更新:当两个或多个事务选择同一行,最初的事务修改的值,会被后面的事务修改的值覆盖。
    脏读:当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问了这个数据,然后使用了这个数据。
    不可重复读:一个事务在读取某些数据后的某个时间,再次读取以前读过的数据,却发现和以前读出的数据不一致。
    幻读:一个事务按照相同的查询条件重新读取以前查询过的数据,却发现其他事务插入了满足其查询条件的新数据。

  3. 事务的隔离级别

隔离级别 丢失更新 脏读 不可重复读 幻读
read uncommitted ×
read committed × ×
repeatable read (default) × × ×
serializable × × × ×

查看当前的隔离级别:

mysql5: show variables like 'tx_isolation';
mysql8: show variables like 'transaction_isolation';
  1. 无索引行锁升级为表锁
    索引失效或者无索引会使行锁升级成表锁

  2. 间隙锁
    当用范围条件,而不是使用相等条件检索数据,并请求共享或排它锁时,InnoDB会给符合条件的已有数据进行加锁;对于键值在条件范围内但并不存在的记录,叫做”间隙(GAP)“,InnoDB也会对这个”间隙“加锁,这种锁机制就是所谓的间隙锁(Next-Key锁)。(如:查询id<4,其中id=2记录不存在,则会给id=2加上间隙锁)。

  3. 行锁征用情况

show status like 'innodb_row_lock%';

8 常用的sql技巧

  1. sql执行顺序

编写顺序:

image.png

执行顺序:

image.png

你可能感兴趣的:(sql)