MySQL性能优化(九)-- 锁机制之行锁

1、行锁概念及特色

1.概念:给单独的一行记录加锁,主要应用于innodb表存储引擎mysql

2.特色:在innodb存储引擎中应用比较多,支持事务、开销大、加锁慢;会出现死锁;锁的粒度小,并发状况下,产生锁等待的几率比较低,因此支持的并发数比较高。sql

2、数据库事务

1.概念:事务是一系列操做组成的工做单元,该工做单元内的操做是不可分割的,也就是说要么所有都执行,要么所有不执行。数据库

2.特性:ACIDbash

  • 原子性:事务是最小的工做单元,不可分割,要么都作,要么都不作
  • 一致性:事务执行前和执行后的数据要保证正确性,数据完整性没有被破坏。
  • 隔离性:在并发事务执行的时候,一个事务对其余事务不会产生影响。
  • 持久性:一个事务一旦提交,它对数据库中的数据的改变就应该是永久性的

3、多个事务并发执行 问题及解决方案

1.问题session

  • 丢失更新:在没有事务隔离的状况下,两个事务同时更新一条数据,后一个事务 会 覆盖前面事务的更新,致使前面的事务丢失更新。
  • 脏读:事务A先更新数据,可是没有提交,事务B读到了事务A没有提交的数据。
  • 不可重复读:事务A中,先读到一条数据,事务A尚未结束,此时,事务B对该条数据进行了修改操做,事务A又读到了这条数据,事务A两次读到的数据不一样。
  • 幻读:事务A先读到一批数据,假设读到10条,事务B插入了一条数据,此时,事务A又读这一批数据,发现多了一条,好像幻觉同样。

注:不可重复读的重点是修改,一样的条件,你读取过的数据,再次读取出来发现值不同。并发

幻读的重点在于新增或者删除,一样的条件,第 1 次和第 2 次读出来的记录数不同。spa

2.解决方案--数据库隔离机制code

  1. 未提交读(read uncommitted):这是数据库最低的隔离级别,容许一个事务读另外一个事务未提交的数据。

解决了丢失更新,可是会出现脏读、不可重复读、幻读。cdn

  1. 提交读(read committed):一个事务更新的数据 在提交以后 才能够被另外一个事务读取,即一个事务不能够读取到另外一个事务未提交的数据。

解决了丢失更新和脏读,可是会出现不可重复读和幻读。blog

  1. 可重复读(repeatale read):这是数据库默认的事务隔离级别,保证一个事务在相同条件下先后两次读取的数据是一致的。

解决了丢失更新、脏读和不可重复读,可是会出现幻读。

  1. 序列化(serializable):这是数据库最高的隔离级别。事务串行执行,不会交叉执行。

解决了全部的问题。

注:乐观所能够解决幻读。

4、行锁的特性

查看mysql事务隔离级别:show variables like 'tx_iso%';

前提:set autocommit=0; // 设置自动提交事务为手动提交 

/* 行锁案例*/
create table lock_two(
    id int,
    col int
)engine=innodb;

insert into lock_two(id,col) values (1,1);
insert into lock_two(id,col) values (2,2);
insert into lock_two(id,col) values (3,3);
复制代码

1.在session1中执行update : update lock_two set col=11 where id=1;

(1)分别在session1和session2中查询lock_two,看id为1的记录的col是否修改了。

img

发现session1 的记录修改了,session2中的记录没有被修改。

(2)在session1中执行commite后,而后再在session2中查询:

img

发现session2中的表数据改变了。

2.在session1中执行update:update lock_two set col=11 where id=1,不执行commit;

在session2中执行uodate :update lock_two set col=11 where id=1,不执行commit;

img

发现session2中的update发生阻塞,而且超过一段时间报错。

3.在session1中执行update:update lock_two set col=22 where id = 2; 不执行commit

在session2中执行另外一条update:update lock_two set col=33 where id = 3;

img
 此时,session2中的update发生阻塞,在没发生错误的状况下,session1执行commit,session2中的update会立刻执行。

4.在lock_two中建立索引,

create index idx_id on lock_two(id);
create index idx_col on lock_two(col);
复制代码

而后重复第3步,

img

发现session2能够更新,不会产生阻塞。由于用上了索引,至关于行锁。

结论:若是没有用上索引,行锁变成表锁

5、手动锁一行记录格式

begin;

select * from lock_two where id=2 for update;
复制代码
  1. 在session1中执行上面语句,在ssesion2中能够查看,可是不能够修改 sesion1中的for update 的记录。
  2. 当session1中执行commit后,seesion2中的update马上执行。

6、间隙锁

1.定义

在范围查找的状况下, innodb会给范围条件中的数据加上锁,不管数据是不是否真实存在。

2.例子

在session1中update:update lock_two set col=666 where id>2 and id<8;

1) 在session2中执行insert:insert into lock_two values(9,99);

插入执行成功!

2) 在session2中执行insert:insert into lock_two values(7,77);

插入阻塞,一段时间后报错!

执行select:select * from lock_two where id=4;

查询成功!

建议:在innodb中,由于有间隙锁的存在,最好在where中少使用这种范围查找。

7、查看行锁的信息

show status like 'innodb_row_lock%';

img

说明:

  • Innodb_row_lock_current_waits :当前正在等待的数量
  • Innodb_row_lock_time: 从启动到如今锁定的总时长,单位是ms
  • Innodb_row_lock_time_avg :锁等待的平均时长
  • Innodb_row_lock_time_max:等待锁时间最长的一个时间
  • Innodb_row_lock_waits:总共的等待次数

欢迎关注个人公众号,第一时间接收最新文章~ 搜索公众号: 码咖 或者 扫描下方二维码:

img
相关文章
相关标签/搜索