MySQL并发控制(锁,事务)

并发控制

file
  1. 读写锁
    读锁也叫共享锁,共享,互不干扰。
    写锁也叫排它锁,写锁会阻塞其他写锁和读锁。
  2. 锁粒度
    锁的粒度越小,并发程度越高。比如行级锁不会影响其他行操作。
    行级锁,最大地支持并发。
    表级锁,开销最小。
  3. 悲观锁
    顾名思义,就是悲观的认为会发生并发冲突,因此,在数据处理时会给数据库中的数据加锁,避免其他线程操作数据,直到本线程事务提交并释放锁,其他线程才能操作数据。
  4. 乐观锁
    与悲观锁不同,乐观锁是乐观的认为不会发生并发冲突,所以并不会像悲观锁那样“占着数据不放,给数据上锁”,其他线程也可以操作数据,但是乐观锁机制会在最后更新数据的时候比较当前的数据和之前的数据是否一致,如果不一致,那么就代表这条数据被其他线程修改过,则放弃更新,如果一致,则完成更新。

事务

  • 提交 commit
  • 回滚 rollback

事务的锁只会在提交和回滚的时候释放。

ACID

  1. 原子性(Atomicity)
    事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
  2. 一致性(Consistency)
    事务前后数据的完整性必须保持一致。事务开始和结束时,外部数据一致。在整个事务过程中,操作是连续的。
  3. 隔离性(Isolation)
    多个用户并发访问数据库时,一个用户的事务不能被其它用户的事物所干扰,多个并发事务之间的数据要相互隔离。
  4. 持久性(Durability)
    一个事务一旦被提交,它对数据库中的数据改变就是永久性的。
    实现:预写日志、分布式日志等。

隔离级别

file
  1. READ UNCOMMITTED (未提交读/读未提交)
    在READ UNCOMMITTED级别,事务中的修改,即使没有提交,对其他事务也都是可见的。事务可以读取未提交的数据,这也被称为脏读(Dirty Read)
  2. READ COMMITTED (提交读/读已提交)
    一个事务开始时,只能“看见”已经提交的事务所做的修改。换句话说,一个事务从开始直到提交之前,所做的任何修改对其他事务都是不可见的。这个级别有时候也叫做不可重复读(nonrepeatableread),因为两次执行同样的查询,可能会得到不一样的结果。
  3. REPEATABLE READ (可重复读)
    REPEATABLE READ解决了脏读的问题。
    该级别保证了在同一个事务中多次读取同样记录的结果是一致的。无法解决幻读(Phantom Read)的问题。所谓幻读,指的是当某个事务在读取某个范围内的记录时,另外一个事务又在该范围内插入了新的记录,当之前的事务再次读取该范围的记录时,会产生幻行(Phantom Row), InnoDB和XtraDB存储引擎通过多版本并发控制(MVCC, Multiversion Concurrency Control)解决了幻读的问题。可重复读是MysQL的默认事务隔离级别。
  4. SERIALIZABLE (可串行化)
    SERIALIZABLE是最高的隔离级别。它通过强制事务串行执行,避免了前面说的幻读的问题。简单来说, SERIALIZABLE会在读取的每一行数据上都加锁,所以可能导致大量的超时和锁争用的问题。

几个概念:

  • 脏读:A写入未提交,B可读。 解决:记录旧值,提交后才显示新值。
  • 脏写:A写1,B写2,相互覆盖。 解决:行锁。
  • 读偏差:未刷新会看到旧值。
  • 读不阻塞写,写不阻塞读。
  • 幻读:即一个事务的写入改变了另一个事务的查询结果的正确性。
  • 快照隔离:它对长时间运行的只读查询(如备份和分析)非常有用。做法是,每个事务都从数据库的一致快照中读取。也就是说,事务始终可以看到事务开始时数据库中的所有数据。
  • 读已提交为每个查询保留单独的快照,而快照隔离为每个事务保留相同的快照。
  • 快照隔离可以避免只读查询中的幻读,两个事务同时写入,幻读问题仍然存在。

死锁

两个或多个事务在同一资源上的相互占用,并请求锁定对方占用的资源。
比如,A事务更新3,4两行,B事务更新4,3两行。A占用3等待4,B占用4等待3。则死锁。

对应的,mysql也有各种死锁检测和死锁超时的机制。InnoDB的处理方式是,将持有最少行级排它锁的事务进行回滚。

两阶段提交(2PC),两阶段锁定(2PL)

两阶段锁定要求:只要没有写入,就允许多个事务同时读取同一行。但只要有写入(修改或删除),就独占访问权限。换句话说,写入不仅会阻塞其他写入,也会阻塞读。这是和快照隔离之间的关键区别。

为什么叫“两阶段”锁定呢?
第一阶段(只加锁):事务开始前,进程尝试对所有此事务需要的行进行加锁,按顺序一次锁一行,查询就加共享锁,修改就加互斥锁。若第一阶段全部加锁成功,就开始第二阶段(只解锁):执行更新然后释放所有锁。若在第一阶段某个进程加锁时发生冲突,则该进程释放它所有加锁的行,然后重试第一阶段。

两阶段锁定真正实现了串行化性质,它可以防止之前讨论的所有并发问题,也是性能损耗最大的选择,尤其是它可能会更频繁地导致死锁出现。事务由于死锁而被中止后只能重试,这意味着巨大的开销。

事务日志

预写式日志,将内存中数据修改的部分记录到日志中,先持久化,然后在慢慢将数据修改,哪怕此时崩溃,也能根据日志恢复。

自动提交(AUTOCOMMIT)

MySQL默认采用自动提交模式,即只要不是显式开始一个事务,则每个查询都会被当做一个事务执行。

手动提交:可以自己控制逻辑,完成所有的才算。
自动提交:完成一个是一个。

多版本并发控制MVCC:

基于对并发性能的考虑,MySQL的大多数事务型存储引擎都实现了多版本并发控制,可以简单的认为MVCC是行级锁的一个变种,但是它在很多情况下避免了加锁操作,降低了开销。

实现:通过保存数据在某个时间点的快照来实现的。也就是同一时刻开始的事务看到的快照是一样的。

分为乐观型和悲观型。

乐观型:

觉得他人不会更新,添加版本号,每次自己提交的时候判断版本,如果有人修改了,自己就回滚。属于自己让步。

悲观型

觉得他人会更新,先表明自己要更新,添加互斥锁,让别人都不能写。属于要别人让步。

InnoDB的MVCC是通过在每行记录后添加两个隐藏列来实现的,一个列用于保存行的创建时间,一个列用于保存行的过期时间,这两个时间在实际存储的时候,存储的是系统版本号。每开始一个新事务,系统版本号都将递增。需要注意的是MVCC只能在Read Committed和Repeatable Read隔离级别下正常工作。

你可能感兴趣的:(MySQL并发控制(锁,事务))