Mysql进阶学习Ⅲ,数据库锁机制初级

Mysql进阶学习Ⅲ

  在数据库中,数据是一种共享资源,为了保证数据并发访问的一致性、有效性,使用锁机制就很有必要。

回顾下数据库的事务

  事务的概念:逻辑上的一组操作,组成这组操作的各个单元,要么全部成功,要么全部不成功。

  • 事务的四大特性
    1、原子性:事务的操作要么全部成功,要么全部不成功。
    2、一致性:前后状态一致,例如:转账前后总金额不变。
    3、隔离性:多用户并发访问数据库时,每个用户的事务不被其他事务影响。
    4、持久性:事务一旦提交,数据库数据的改变是永久性的。
  • 事务不考虑隔离性引发的问题
    1、脏读:一个事务读取了另一个事务未提交的数据。例如:用户A将库存从1修改为0,但是未提交,此时用户B查询了库存为0,然后用户A执行失败回滚了数据,库存变为1,用户B读到的数据(库存0)就叫脏数据。(针对update)
    2、不可重复读:事务读取某一行数据时,多次读取结果不同,及读取了另一个事务已经提交的数据。例如:用户A先查询库存为1,此时用户B修改库存为0,这时用户A再次查询库存为0,两次查询结果不一致。(针对update)
    3、虚读:事务读取到了别的事务插入的数据,导致前后读取不一致。 事务在操作过程中进行两次查询,第二次查询的结果包含了第一次查询中未出现的数据或者缺少了第一次查询中出现的数据。(针对insert)
  • 事务的隔离级别
    1、Serializable(串行化):全部避免
    2、Repeatable read(可重复读):避免脏读,不可重复读(Mysql默认)
    3、Read committed(读已提交):可避免脏读(Oracle默认)
    4、Read Uncommitted(读未提交):最低级别,都无法保证

一、悲观锁和乐观锁

1、悲观锁: 悲观锁是由数据库自己实现了的,要用的时候,我们直接调用数据库的相关语句就可以了。

2、乐观锁: 乐观锁不是数据库自带的,需要我们自己去实现,在操作数据时,并不进行任何其他的特殊处理(也就是不加锁),而在进行更新后,再去判断是否有冲突了。
 
实现:
给数据表加一个版本(version)字段,每次操作成功都将version+1,如果要对那条记录进行操作(更新),则先判断此刻version的值是否与刚刚查询出来时的version的值相等,如果相等,则说明这段期间,没有其他程序对其进行操作则可以执行更新,反之则不执行。

select kc,version from sku where sku_id = ?
update sku set kc = kc -1,version = version + 1 where sku_id = ? and version = version
二、表锁(MyISAM锁)

  锁定一张表,一个用户操作完成之前其他用户都不能对该表操作。

1、读锁(Table Read Lock): 不会阻塞其他用户对同一表的读操作,但会阻塞对同一表的更新(Update)和插入(Insert)操作。
加锁语法:LOCK TABLE 表名 READ
解锁语法:UNLOCk TABLES

2、写锁(Table Write Lock): 同时阻塞其他用户的所有操作。
加锁语法:LOCK TABLE 表名 WRITE
解锁语法:UNLOCk TABLES

3、查询表锁争用情况: 可以通过检查table_locks_waited和table_locks_immediate状态变量来分析系统上的表锁定争夺情况。
语法:show status like ‘table%’;
在这里插入图片描述
如果Table_locks_waited的值比较高,则说明存在着较严重的表级锁争用情况。

三、行锁(InnoDB锁)

  锁定一行数据,一个用户操作完成之前其他用户都不能对该数据操作。事务使用的就是行锁。

1、 共享锁(读锁): 多个事务只能读数据不能改数据
语法:sql语句 + LOCK IN SHARE MODE

2、排他锁(写锁): 一个事务在一行数据加上排他锁后,其他事务不能再在其上加其他的锁。所以加过排他锁的数据行在其他事务种是不能修改数据的,也不能通过for update和lock in share mode锁的方式查询数据,但可以直接通过select …from…查询数据,因为普通查询没有任何锁机制。
语法:sql语句 + FOR UPDATE

3、 查询行锁争用情况: 可以通过检查InnoDB_row_lock状态变量来分析系统上的行锁的争夺情况
语法:show status like ‘innodb_row_lock%’;
在这里插入图片描述
如果InnoDB_row_lock_waits和InnoDB_row_lock_time_avg的值比较高,则说明存在着较严重的表级锁争用情况。

  • 默认的修改数据语句:update,delete,insert都会自动给涉及到的数据加上排他锁,select语句默认不会加任何锁类型,如果加排他锁可以使用select …for update语句

4、行锁实现方式(重点)
  行锁是通过给索引上的索引项加锁来实现的,只有通过索引条件检索数据,才使用行级锁,否则使用表锁。

  1. 在不通过索引条件查询的时候,InnoDB使用的是表锁,而不是行锁。
    在这里插入图片描述
  2. 访问不同行的记录,如果使用相同的索引键,会出现锁冲突。应用设计的时候要注意这一点。
    在这里插入图片描述
  3. 当表有多个索引的时候,不同的事务可以使用不同的索引锁定不同的行,另外,不论是使用主键索引、唯一索引或普通索引,InnoDB都会使用行锁来对数据加锁。
    在这里插入图片描述
四、间隙锁

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

  • 举例来说,假如emp表中只有101条记录,其empid的值分别是 1,2,…,100,101,下面的SQL:
    Select * from emp where empid > 100 for update;
    是一个范围条件的检索,InnoDB不仅会对符合条件的empid值为101的记录加锁,也会对empid大于101(这些记录并不存在)的“间隙”加锁。
  • 特别说明:InnoDB除了通过范围条件加锁时使用间隙锁外,如果使用相等条件请求给一个不存在的记录加锁,InnoDB也会使用间隙锁!下面这个例子假设emp表中只有101条记录,其empid的值分别是1,2,……,100,101。
    在这里插入图片描述
发布了33 篇原创文章 · 获赞 1 · 访问量 1426

猜你喜欢

转载自blog.csdn.net/smileKutper/article/details/103929094