MySQL优化

 

MySQL优化

1. 索引

索引是帮助MySQL高效获取数据的数据结构,在数据之外,数据库系统还维护着满足特定查找算法的数据结构,这些数据结构以某种方式引用指向数据,这样就可以在这些数据结构上实现高级查找算法,这种数据机构就是索引

1.1索引的优势和劣势

  • 优势

    • 类似于书籍的目录,提高数据检索的效率,降低数据库的IO成本

    • 通过索引列对数据进行排序,降低数据排序的成本,降低CPU的消耗

  • 劣势

    • 实际上索引也是一张表,该表中保存了逐渐和索字段,并指向实体类的记录,所以索引列也是要占用空间的

    • 虽然索引大大提高了查询效率,同时也降低了更新表的速度

1.2.索引结构

索引是在MySQL的存储引擎层中实现的,而不是在服务器实现的。目前MySQL提供了4中索引

  • BTree索引:最常见的索引类型,大部分索引都支持

  • Hash索引:只有Memory引擎支持,使用场景简单

  • R-Tree索引(空间索引):是MyISAM引擎的一个特殊索引类型,主要用于地理空间数据存储,通常使用较少

  • Full-Text(全文索引):是MyISAM引擎的一个特殊索引类型,主要用于全文索引,InnoDB从MySQL5.6以后开始支持全文索引

平常所说的索引,如果没有特别说明,都是指B+Tree

1.3. 创建索引

  • 创建索引: create index name on table(column);

create index idx_city_name on city(city_name);
  • 查看索引:show index from table;

    show index from person;

      

  • 删除索引:drop index index_name on table

    drop index idx_city_name on person;

     

1.4.索引设计原则

索引的设计可以遵循一些已有的原则,创建索引的时候尽量考虑符合这些原则,便于提升索引的使用效率,更高效的使用索引

  • 对于查询频次较高且数据量较大的表建立索引

  • 索引字段的选择,最佳候选列应当从where子句的条件中提取,如果where子句中的组合较多,那么应该挑选最常用,过滤效果最好的列的组合

  • 使用唯一索引,区分度越高,使用索引的效率越高

  • 索引可以有效的提升查询数据的效率,但是索引并不是越多越好,索引越多维护索引的成本也就越高。对于插入、删除、更新等DML操作比较频繁的表来说,索引越多,会引入相当高的维护代价

  • 利用最左前缀匹配原则

1.5.索引的使用

索引是数据库优化最常用也是最重要手段之一,通过索引通常可以帮助用户解决大多数的Mysql的性能优化问题。

1.5.1. 避免索引失效
  • 全值匹配,对组合索引中所有列都指定具体值

  • 最左前缀法则,如果索引了多列,要遵循最左前缀法则,指的是查询从组合索引的最左前列开始,并且不跳过索引中的列。

    创建组合索引

    CREATE INDEX idx_title_price_artist on album(title,artist,price);

    以下三个查询条件都包含组合索引中的最左列title,都会走索引查询,如果不包含最左列title,则不会走索引查询

    image-20210603185647051

    跟where查询条件中查询字段的顺序无关,只和查询条件中包不包含索引中的字段有关

    image-20210603185844699

    不包含最左列的查询,则不走索引查询

    image-20210603190609869

  • 查询范围右边的列,不能使用索引(为了说明这个问题重新创建了一个索引,调整了以下顺序)

    CREATE INDEX idx_title_price_artist on album(title,price,artist);

     

    image-20210603191758956

    现在在查询范围title>1的右边添加一个查询条件 artist = 'Blur' ,则该artist列不会走索引查询

    image-20210603192113413

  • 不能在索引列上进行运算操作,索引将失效

image-20210603192554601

  • 字符串不加单引号,造成索引失效(针对字符型类的数字)

  • 尽量使用覆盖索引,避免select *,避免回表查询。覆盖索引是指索引列完全包含查询列

  • 使用or分割开的条件,如果or前的条件中的列有索引,而后面的列中没有索引,那么涉及的索引都失效

  • 以%开头的like模糊查询,索引失效。如果仅仅是尾部模糊匹配,索引不会失效。如果头部模糊匹配,索引失效

    可以通过覆盖索引的方式解决模糊查询索引失效的问题

  • is Null 、 is Not Null, 有时索引失效。根据数据库中的数据量有关系,如果该字段的值为Null较多,则使用Is Null的话该字段的索引失效,使用Is not Null的话则索引不失效。反之亦然。

  • in走索引,not in 索引失效

1.5.2 单列索引和组合索引

尽量使用组合索引,而少使用单列索引。创建多个单列索引,查询多个字段时数据库会选择一个最优的索引(辨识度最高)来使用,并不会使用全部索引。

1.6 查看索引的使用情况

show status like 'Handler_read%';

 

2.视图

  • 创建视图

    create view view_name as select语句
  • 查看视图:

     show tables;#不仅可以查看表,而且可以查看创建的视图,不能使用show views;
  • 删除视图: 

    drop view view_name;

3.存储过程

  • 创建

    • Delimiter该关键字用来声明mysql语句的分隔符,默认情况下Mysql的分隔符是;

      create procedure procedure_name ()
      begin
      --SQL语句
      end
  • 调用

    使用call关键字

    call procedure_name();
  • 查看

    #查询db_name数据库中的所有的存储过程
    select name from mysql.proc where db='db_name'
    #查询存储过程的状态信息
    show procedure status;
    #查询某个存储过程的定义
    show create procedure db.procedure_name;
  • 删除

    drop procedure procdure_Name;

4.存储引擎

Mysql默认的存储引擎是InnoDB, InnoDB支持事务、行锁和外键,MyISAM支持表锁,不支持外键,其优势是访问速度快,对事务完整性没有要求可以使用该引擎

  • 存储引擎的选择

    在选择存储引擎时,应该根据应用系统的特点选择合适的存储引擎,对于复杂的应用系统,还可以根据实际情况选择多种存储引擎进行组合,以下时几种常见的存储引擎的使用环境

    • InnoDB:是Mysql的默认存储引擎,用于事务处理应用程序、支持外键,如果应用对事务的完整性有较高的要求,在并发条件下要求数据的一致性,数据库操作除了插入和查询以外,还包含很多的更新、删除操作,那么InnoDB存储引擎是比较合适的选择。对于类似于计费系统或者财务系统对数据准确性要求比较高的系统,InnoDB是最合适的选择。

    • MyISAM: 如果应用系统是以读操作和插入操作为主,只有很少的更新和删除操作,并且对事务的完整性、并发性要求不是很高,那么选择这个存储引擎是非常合适的。

  • show ENGINES指令

    image-20210603104125244

5. MySQL优化

5.1 优化步骤

5.1.1 查看SQL执行频率

MySQL客户端连接成功后,通过show [session | global] status命令可以提取服务器状态信息。可以根据需要加上参数“session”或者“global”来显示sessio或者global级的统计结果,如果不写默认使用参数是session;

该指令可以统计出数据库的操作是select还是insert还是update等哪种操作多一些

SHOW STATUS like 'Com_______';

image-20210603104250914

SHOW GLOBAL STATUS LIKE 'Innodb_rows_%'#InnoDB存储引擎的操作数统计结果

image-20210603104445721

5.1.2 定位低效率执行SQL

可以通过以下两种方式定位执行效率低下的SQL语句

      • 慢日志查询:通过慢查询日志定位那些执行效率较低的SQL语句,用--log.show-queries[=file_name]选项启动时,mysqld写一个包含所有执行时间超过long_query_time秒的SQL语句的日志文件。

        1. 检查是否开启慢查询日志

          SHOW VARIABLES like "%char%";
          SHOW VARIABLES like "%long%";#查询慢查询最大时间

           

          image-20210603115202823

          可以看到当前log_slow_queries状态为OFF,说明没有开启慢查询日志

        2. 在window环境下如何查找Mysql的安装目录,输入以下命令,其中character_sets_dir就是安装路径

            
        SHOW VARIABLES like "%char%";

         

        image-20210603114637981

        1. 在my.ini文件中添加如下配置,开启慢查询日志

          log-slow-queries = "C:\Program Files (x86)\MySQL\mysql-slow.log";
          long_query_time = 4
          log-queries-not-using-indexes

          重新启动Mysql服务并经常查看,发现已经开启Mysql的慢查询日志

          image-20210603120113409

         

  • show processlist:慢查询日志在查询结束后才记录,所以在应用反映执行效率出现问题时查询慢查询日志并不能定位问题,可以使用show processlist命令查看当前Mysql在进行的线程,包括线程的状态、是否锁表等,可以实时的查看SQL的执行情况,同时对一些锁表操作进行优化。

    show PROCESSLIST;#实时查看SQL的执行情况

    image-20210603112148754

5.1.3 Explain分析执行计划

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

image-20210603140137806

字段含义
id select查询的序列号,是一组数字,表示的是查询中执行select子句或者操作表的顺序
select_type 表示select的类型,常用取值simple(简单表,即不使用连接或子查询)、primary(主查询,即外层的查询)、union(union中的第二个或者后面的查询语句)、subquery(子查询中的第一个select)等
table 输出结果的表
type 表示表的连接类型,性能由好到差的连接类型为system-->const-->eq_ref-->ref-->ref_or_null->index_merge-->index_subquery-->range-->index-->all
possible_keys 表示查询时可能使用的索引
key 表示实际使用的索引
key_len 索引字段的长度
ref  
rows 扫描行的数量
extra 执行情况和描述
  • id有三种情况

    • id相同表示从上往下顺序执行

      image-20210603142050500

    • id不同id值越大,优先级越高,越先被执行

      image-20210603143512658

    • id有相同,也有不同,同时存在,id相同的可以认为是一组,从上往下顺序执行;在所有的组中,id的值越大,优先级越高,越先被执行

  • type

    • const:表示通过索引一次就找到了,const用于primary key 或unique索引,只返回一行数据

      image-20210603150230260

      image-20210603150441206

    • ref: 非唯一性索引扫描,本质上也是一种索引访问,返回所有匹配某个单独值的所有行

    • range:值检索给定返回的行,使用一个索引来选择行,where之后出现between,<,>,in等操作

    • index:index与all的区别为index类型只是遍历了索引数,通常比all快,all遍历数据文件

    • all : 将遍历全表以找到匹配的行

    一般来说下、我们需要保证查询至少达到range级别,最后达到ref

  • explain 之key

    possible_keys:显示可能应用在这张表的索引,一个或多个

    key:实际使用的索引,如果为null,则没有使用索引

    key_len:表示索引中使用的字节数,该值为索引字段最大可能长度,并非实际使用长度,在不损失精确性的前提下,长度越短越好

  • explain之extra

    • using_filesort:说明musql会对数据使用一个外部的索引排序,而不是按照表内的索引进行读取,称为文件排序

    • using_temporary :使用了临时表保存中间结果,Mysql在对查询结果排序时使用临时表,常见于order by 或group by

    • using_index:表示相应的select操作使用了覆盖索引,避免访问表的数据行,效率不错

    如果出现using_filesort、using_temporary 就要考虑优化

5.1.4 show profile分析SQL

Mysql从5.0.37版本开始增加了show profiles和show profile语句的支持,show profiles能够在做SQL优化时帮助我们了解时间都耗费到哪里去了

默认profiling是关闭的,可以通过set语句在Session级别开启profiling

SELECT @@have_profiling;#查询当前版本mysql是否支持profile
SELECT @@profiling;#查看profile状态,是否开启profile
SET profiling=1;#设置开启profile

 

当开启profile后,在数据库所作的所有操作都会记录每个操作的耗费情况,可以使用show profiles命令查看

image-20210603172219049

#格式:show profile for query Query_ID
show profile for query 163;
show profile cpu for query 163;#查看CPU的耗费情况

image-20210603173849471

sending data状态表示Mysql线程开始访问数据行并把结果返回客户端。在获取到消耗时间的线程状态后,Mysql支持进一步选择all、cpu、block_io、context_switch等明细类型查看Mysql在使用什么资源上耗费了过高的时间。

5.1.5 trace分析优化器执行计划

Mysql5.6提供了对SQL的跟踪trace, 通过trace文件能够进一步了解为什么优化器选择A计划,而不是B计划。通过如下设置:

set optimizer_trace="enabled=on",end_marks_in_json=on;
set optimizer_trace_max_mem_size=1000000;

执行SQL语句:

select * from tb_item where id <=4;

最后检查information_schma.optimizer_trace就可以知道如何执行SQL的

select* from information_schma.optimizer_trace \G

 

5.2 SQL优化

5.2.1 大批量插入数据

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

#load命令格式
#LOAD DATA LOCAL INFILE 'file_path' INTO TABLE table_name FEILDS TERMINATED BY ',' LINES TERMINATED BY '\n'
#其中windows下file_path需要注意\,需要使用转移字符

LOAD DATA LOCAL INFILE 'C:\\Users\\sean\\Desktop\\Mysql\\sql1.log' into TABLE tb_user FIELDS TERMINATED by ',' LINES TERMINATED by '\n'; 

 

image-20210604110702305

对于InnoDB类型的表,有以下几种方法提高效率

  • 主键顺序插入

    因为InnoDB类型的表是按照主键的顺序保存的,所以将导入数据按照主键的顺序排序,可以有效的提高导入数据的效率。如果InnoDB表没有主键,那么系统会自动创建一个内部列作为主键,如果可以给表创建一个主键,将可以利用这点来提高导入数据的效率。

  • 关闭唯一性校验

    在导入数据前通过 SET UNIQUE_CHECKS=0指令,关闭唯一性校验,导入数据后再执行SET UNIQUE_CHECKS=1,恢复唯一性校验,来提高导入数据的效率

  • 手动提交事务

    如果应用使用自动提交的方式,建议在导入前执行 SET AUTOCOMMIT=0,关闭自动提交,导入结束后再执行 SET AUTOCOMMIT=1,打开自动提交,也可以提高导入的效率。

5.2.2 优化insert语句

当进行数据的insert操作时,可以考虑以下几种优化方案

  • 如果需要同时对一张表插入很多行数据时,应该尽量使用多个值表的insert语句,这种方法将大大的缩减客户端于数据库之间的连接、关闭等消耗。

    原始方式为:

    insert into tb_table values(1,'Tomcat');
    insert into tb_table values(2,'Tomcat1');
    insert into tb_table values(3,'Tomcat2');

     

    优化后方案为:

    insert into tb_table values (1,'Tomcat'),(2,'Tomcat1')(3,'Tomcat2');

     

  • 手动提交事务,在事务中进行数据插入

    start transaction;
        insert into tb_table values(1,'Tomcat');
        insert into tb_table values(2,'Tomcat1');
        insert into tb_table values(3,'Tomcat2');
    commit;

     

  • 数据有序插入,调整表的主键按照升序的顺序插入数据。

5.2.3 优化 order by 语句

5.2.3.1 Mysql的两种排序方式
  • FileSort排序:通过对返回数据进行排序,所有不是通过索引直接返回排序结果的排序都叫FileSort排序

  • Using Index排序:通过有序索引顺序扫描直接返回有序数据,这种情况即为using index

了解Mysql的排序方式,优化目标就清晰了:尽量减少额外的排序,通过索引直接返回有序数据。

  • where 条件和order by使用相同的索引(覆盖索引)并且Order by的顺序和索引顺序相同,并且order by的字段都是升序或者都是降序,否则肯定需要额外的操作,这样就会出现FileSort

5.2.3.2 FileSort排序优化Mysqll

在实际开发工作中不可避免的会出现FileSort排序,通过创建合适的索引,能够减少FileSort的出现,但是在某些情况下,条件限制不能让FileSort消失,那就需要加快FileSort的排序操作。对于FileSort,可以通过如下方式优化FileSort排序

1)两次扫描算法:Mysql4.1之前使用该方法排序。首先根据条件取出排序字段和行指针信息,然后再排序区sort buffer中排序,如果sort buffer不够,则再临时表temporary table中存储排序结果。完成排序后,再根据行指针读取记录,该操作可能会导致大量随机I/O操作

2)一次扫描算法:一次性取出满足条件的所有字段,然后再排序区sort buffer中排序后直接输出结果。排序时内存开销较大,但是排序效率比两次扫描算法要高

Mysql通过比较系统变量max_length_sort_data的大小和Query语句取出的字段总大小,来判定适合哪种算法。如果max_length_sort_data更大,那么使用一次扫描算法,否则使用两次扫描算法

可以适当提高sort_buffer_size和max_length_sort_data系统变量,来增大排序区的大小,提高排序的效率

SHOW VARIABLES like '%sort_buffer_size%';#查看排序区大小
SHOW VARIABLES like '%max_length_sort_data%';
SET sort_buffer_size='xxxx';
SET max_length_sort_data='xxxx';

 

5.2.4 Group by 的优化

由于group by实际上也同样会进行排序操作,而且order by相比,group by主要只是多了排序之后的分组操作。当然如果分组的时候还使用了其他的聚合函数,那么还需要一些聚合函数的计算。所以在group by的实现过程中,于order by一样可以利用到索引。

  • 如果查询包含group by 但是用户想要避免排序结果的消耗,则可以执行order by null禁止排序

    select age ,count(1) from t_user group by age order by null

     

  • 也可以通过索引来提高查询效率

    image-20210604220025426

5.2.5 子查询的优化

Mysql4.1版本之后,开始支持SQL的子查询,这个技术可以使用select语句来创建一个单列的查询结果,然后把这个结果作为过滤条件用在另一个查询中。使用子查询可以一次性的完成很多逻辑上需要多个步骤才能完成的SQL操作,同时也可以避免事务或者表锁死,并且写起来也很容易,但是有些情况下,子查询是可以被更高效的连接(JOIN)替代。

5.2.6 or查询的优化

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

  • 建议使用union替换or

    type显示的是访问类型,是较重要的一个指标,结果集从好到坏依次是

    system-->const-->eq_ref-->ref-->ref_or_null->index_merge-->index_subquery-->range-->index-->all

5.2.7 分页查询的优化

一般分页查询时,通过创建覆盖索引能够比较好的提高性能,一个常见又非常头疼的问题就是limit 200000,10,此时需要Mysql排序前200010条记录,仅仅返回200000~200010的记录,其他记录丢弃,查询排序的代价非常大。

  • 优化思路一

    在索引上完成排序分页操作,最后根据主键关联回原表查询所需要的其他列内容。

    select * from tb_item t,(select id from tb_item order by id limit 200000,1 0) a where t.id = a.id 
  • 优化思路二

    该方案使用于主键自增的表,可以把limit查询转换成某个位置的查询.前提条件是id主键自增且必须连续不能出现断层,满足这个条件 才能使用

    select * from tb_item where id >200000 limit 10; 

5.2.8 使用SQL提示

SQL提示是优化数据库的一个重要手段,简单来说,就是在SQL语句中加入一些人为的提示来达到优化操作的目的。

  • USE INDEX

    在查询语句中表名的后面,添加use index来提供希望mysql去参考的索引列表,就可以让MySQL不再考虑其他可用的索引

    select * from tb_item use index(idx_name) where name = "zhangsan"
  • IGNORE INDEX

    如果用户只是单纯的想让MySQL忽略一个或者多个索引,则可以使用ignore index

    select * from tb_item ignore index(idx_name) where name = "zhangsan"
  • FORCE INDEX

    为强制MySQL使用一个特定的索引,可在查询中使用force index

    select * from tb_item force index(idx_name) where name = "zhangsan"

6.应用程序的优化

6.1 使用连接池

对于访问数据库来说,建立连接的代价是比较昂贵的,因为频繁的创建、关闭连接,是比较耗费资源的,有必要建立数据库连接池以提高数据库访问的性能

6.2 减少对Mysql的访问

  • 避免对数据进行重复检索

    在编写应用代码时,需要能够理清对数据库的访问逻辑,能够一次连接就获取结果的,就不用两次连接,这样可以大大减少对数据无用的重复请求

  • 增加cache层

    在应用中,可以在应用中增加缓存层来达到减轻数据库负担的目的,缓存层有很多种,也有很多实现方式,只要能够达到降低数据库负担又能满足应用需求就可以。因此部分数据从数据库中抽取出来放到应用端以文本方式存储,或者使用框架(Mybatis\Hibernate)提供的一二级缓存,或者使用redis数据库来缓存数据。

6.3 负载均衡

负载均衡是应用中使用非常普通的一种优化方法,它的机制就是通过某种均衡算法,将固定的负载量分布到不同的服务器上,以此来降低单台服务器的负载,达到优化的效果

  • 利用mysql复制分流查询

    通过MySQL的主从复制,实现读写分离,使增删改操作走主节点,查询操作走从节点,从而实现单台服务器的读写能力。

  • 采用分布式数据库结构

    分布式数据库架构适合大数据量,负载高的情况,它有良好的拓展性和高可用性,通过在多台服务器之间分布数据,可以实现在多台服务器之间的负载均衡,提高访问效率

7. Mysql 查询缓存优化

开启MySQL的查询缓存,当执行相同的SQL语句时,服务器就会直接从缓存中读取结果,当数据被修改,之前的缓存会失效,修改比较频繁的表不适合做查询缓存。

7.1 查询缓存配置

使用如下命令可以查看查询缓存相关的信息

SHOW VARIABLES like "%QUERY_CACHE%"

 

image-20210607135848840

检查查询缓存状态相关信息:

SHOW STATUS LIKE "Qcache%";

image-20210607140721695

7.2 开启查询缓存

在MySQL的配置文件my.ini中添加命令query_cache_type=1

7.3 查询缓存select选项

可以在select语句中指定两个于查询相关的选项:

  • SQL_CACHE:如果查询结果可缓存,并且query_cache_type系统变量的值为ON或DEMAND,则缓存查询结果

  • SQL_NO_CACHE:服务器不适用查询缓存,它既不检查查询缓存,也不检查结果是否已缓存,也不缓存结果

select SQL_NO_CACHE id , name from tb_user;
select SQL_CACHE id , name from tb_user;

 

8. Mysql并发参数调整

从实现上来说,MySQL Server 是多线程结构,包括后台线程和客户服务线程。多线程可以有效利用服务器资源,提高数据库的并发性能。在Mysql中,控制并发连接和线程的主要参数包括 max_connections、back_log、thread_cache_size、table_open_cahce。

8.1 max_connections

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

Mysql 最大可支持的连接数,取决于很多因素,包括给定操作系统平台的线程库的质量、内存大小、每个连接的负荷、CPU的处理速度,期望的响应时间等。在Linux 平台下,性能好的服务器,支持 500-1000 个连接不是难事,需要根据服务器性能进行评估设定。

8.2 back_log

back_log 参数控制MySQL监听TCP端口时设置的积压请求栈大小。如果MySql的连接数达到max_connections时,新来的请求将会被存在堆栈中,以等待某一连接释放资源,该堆栈的数量即back_log,如果等待连接的数量超过back_log,将不被授予连接资源,将会报错。5.6.6 版本之前默认值为 50 , 之后的版本默认为 50 + (max_connections / 5), 但最大不超过900。

如果需要数据库在较短的时间内处理大量连接请求, 可以考虑适当增大back_log 的值。

8.3 table_open_cache

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

max_connections x N ;

8.4 thread_cache_size

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

8.5 innodb_lock_wait_timeout

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

9. Mysql锁问题

读锁会堵塞写,但是不会阻塞读,而写锁,既会阻塞读也会阻塞写。

9.1 事务及其ACID属性

事务是由一组SQL语句组成的逻辑处理单元。

事务具有以下4个特性,简称为事务ACID属性。

        

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

并发事务处理带来的问题

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

9.2 无索引行锁升级为表锁

如果不通过索引条件检索数据,那么InnoDB将对表中的所有记录加锁,实际效果跟表锁一样。

查看当前表的索引 : show index from test_innodb_lock ;

1554385956215

由于 执行更新时 , name字段本来为varchar类型, 我们是作为数组类型使用,存在类型转换,索引失效,最终行锁变为表锁 ;

9.3 间隙锁危害

当我们用范围条件,而不是使用相等条件检索数据,并请求共享或排他锁时,InnoDB会给符合条件的已有数据进行加锁; 对于键值在条件范围内但并不存在的记录,叫做 "间隙(GAP)" , InnoDB也会对这个 "间隙" 加锁,这种锁机制就是所谓的 间隙锁(Next-Key锁) 。

9.4 InnoDB 行锁争用情况

show  status like 'innodb_row_lock%'

1556455943670

Innodb_row_lock_current_waits: 当前正在等待锁定的数量

Innodb_row_lock_time: 从系统启动到现在锁定总时间长度

Innodb_row_lock_time_avg:每次等待所花平均时长

Innodb_row_lock_time_max:从系统启动到现在等待最长的一次所花的时间

Innodb_row_lock_waits: 系统启动后到现在总共等待的次数


当等待的次数很高,而且每次等待的时长也不小的时候,我们就需要分析系统中为什么会有如此多的等待,然后根据分析结果着手制定优化计划。

 

优化建议:

  • 尽可能让所有数据检索都能通过索引来完成,避免无索引行锁升级为表锁。

  • 合理设计索引,尽量缩小锁的范围

  • 尽可能减少索引条件,及索引范围,避免间隙锁

  • 尽量控制事务大小,减少锁定资源量和时间长度

  • 尽可使用低级别事务隔离(但是需要业务层面满足需求)

posted @ 2021-06-08 15:21  肖恩雷  阅读(136)  评论(0编辑  收藏  举报