数据库锁

数据库四个问题

  1. 数据丢失

    • 不加锁

  2. 脏读

    • 脏读/脏数据,读数据的时候不用加锁,但是读到数据后,另一个修改这个数据的进程回滚了,于是现在读到的数据就和真实数据不符合。

    • 使用共享锁解决

  3. 重复读

    • 在一个事务中要多次读取某个数据,但是如果期间释放了共享锁,这个数据被其他人修改了,第二次重复读的时候就会和第一次不一样。解决办法就是事务执行期间一直不释放共享锁,直到事务提交。

  4. 幻读

    • 一个人修改了一批数据,比如把所有年龄18岁的都修改成19岁,修改完一看,还有一个18岁的,好像没有修改。实际是修改的同时,另一个人插入了一个18岁的记录。导致好像是这个18岁的没有修改。

    • 串行化解决,就是我修改完,你才能插入。

锁的分类

排它锁

  • 就普通的锁

  1. 记录锁,又叫行锁,对某行数据操作的时候上锁。

  2. 间隙锁,事务加锁后锁住的是表记录的某一个区间,当表的相邻ID之间出现空隙则会形成一个区间,遵循左开右闭原则。

    • 间隙锁作用?防止幻读问题,事务并发的时候,如果没有间隙锁,就会发生如下图的问题,在同一个事务里,A事务的两次查询出的结果会不一样。

    - 间隙锁出现的条件:范围查询并且查询未命中记录,查询条件必须命中索引、间隙锁只会出现在REPEATABLE_READ(重复读)的事务级别中。

自旋锁

  • 旋锁是采用让当前线程不停地的在循环体内执行实现的,当循环的条件被其他线程改变时 才能进入临界区,线程尝试获取锁的过程不会阻塞;但不适应于单核场景。好处就是不用上下文切换,坏处是如果持有锁时间太长,其他线程一直自旋浪费CPU。适用于执行速度快,频繁切换的场景。

共享锁/读写锁

  • 读共享,写独占

  • 当读数据的时候加一个共享锁,如果有读数据就+1,有共享锁就不能有排它锁,这样解决了脏读的问题。

Innodb引擎特有锁

  1. 临键锁

    • 临键锁是INNODB的行锁默认算法,它是记录锁和间隙锁的组合,临键锁会把查询出来的记录锁住,同时也会把该范围查询内的所有间隙空间也会锁住,再之它会把相邻的下一个区间也会锁住。

    • 临键锁避免了在范围查询时出现脏读、重复读、幻读问题。加了临键锁之后,在范围区间内数据不允许被修改和插入。

    • 间隙锁和临键锁的区别:

      • 间隙锁在根据索引范围查找失败的时候才锁,而且只锁一个区间。

      • 临键锁是查找成功也锁,把找到的数据和他们的间隙都锁上。

  2. 行锁:当SQL命中索引的时候才加行锁。

  3. 表锁:其他时候都是加表锁。

  4. 意向锁:

    • 意向共享锁:当一个事务试图对整个表进行加共享锁之前,首先需要获得这个表的意向共享锁。

    • 意向排它锁:当一个事务试图对整个表进行加排它锁之前,首先需要获得这个表的意向排它锁。

  5. 为什么我们需要意向锁?

    • innodb加锁的方式是基于索引,并且加锁粒度是行锁。

    • 在事务A的操作过程中,后面的每个需要对表加持表锁的事务都需要遍历整个索引树才能知道自己是否能够进行加锁,太浪费时间和损耗数据库性能了

    • 所以INNODB就加了意向锁的概念:如果当事务A加锁成功之后就设置一个状态告诉后面的人,已经有人对表里的行加了一个排他锁了,你们不能对整个表加共享锁或排它锁了,那么后面需要对整个表加锁的人只需要获取这个状态就知道自己是不是可以对表加锁,避免了对整个索引树的每个节点扫描是否加锁.

数据库多版本并发控制(MVCC)

  1. 使用锁进行串行化处理虽然解决了一系列问题,但是效率太低,而且无法避免死锁。

  2. 多版本并发控制(MVCC)是一种用来解决读-写冲突的无锁并发控制,也就是为事务分配单向增长的时间戳,为每个修改保存一个版本,版本与事务时间戳关联,读操作只读该事务开始前的数据库的快照。 这样在读操作不用阻塞写操作,写操作不用阻塞读操作的同时,避免了脏读和不可重复读

  3. MVCC给每个事务一个事务id,和回滚指针。有一个Read View数据结构,记录当前所有活跃事务和活跃事务中的最大事务id和最小事务id。

  4. 在读数据的时候,这个事务有个单独的id,重复读的时候再找这个事务id对应的 数据就行了。

  5. 写数据的时候是一个新的事务id。

  6. 这样在读写的时候就不用互相加锁了,提高读写效率。

乐观并发控制(OCC)

  1. 乐观并发控制(OCC)是一种用来解决写-写冲突的无锁并发控制,认为事务间争用没有那么多,所以先进行修改,在提交事务前,检查一下事务开始后,有没有新提交改变,如果没有就提交,如果有就放弃并重试。乐观并发控制类似自选锁。乐观并发控制适用于低数据争用,写冲突比较少的环境。

Undo日志:

  1. 记录事务执行前的数据,如果事务执行一半需要回滚,就直接回到最初的数据状态。

  2. Undo日志的写入规则:有个缓冲区,隔一段时间写入硬盘,回滚的时候就看undo日志,如果事务提交了,就不用回滚,否则说明事务没执行完就中断了,直接回滚。

  3. Undo日志的写入次序,一定要在相关的数据项写入之前。

最后更新于

这有帮助吗?