美文网首页程序员
mysql事务概念简要(1)

mysql事务概念简要(1)

作者: 洛克黄瓜 | 来源:发表于2020-06-20 17:30 被阅读0次

    并发控制

    • 读写锁

      • 共享锁(读锁):读锁是共享的,相互不阻塞,但会阻塞写锁;多个客户在同一时刻可以读取同一个资源,互不干扰。
      • 排他锁(写锁):写锁是排他的,一个写锁会阻塞其他的写锁和读锁;这是出于安全策略,确保只有一个客户能执行写入,并防止其他用户读取正在写入的资源。
    • 锁粒度

    一种提高共享资源并发性的方式就是让锁定对象更有选择性。尽量只锁定需要修改的部分数据(最理想的方式),而不是所有的资源。锁定范围越小,并发性越高。大多数数据库都是在表上施加行级锁,并以各种复杂的方式来实现。

    • 表锁(table lock):MySQL中最基本的锁策略,并且是开销最小的策略(不涉及过多逻辑),会锁定整张表;eg:服务器会为诸如alter table之类的语句使用表锁,而忽略存储引擎的锁机制。【这么看来服务器层就实现了表锁】
      -行级锁(row lock):行级锁可以最大程度地支持并发处理(同时有带来了最大的锁开销)。行级锁只在储存引擎层实现,而MySQL服务器层没有实现;储存引擎Innodb就实现了行级锁。

    事务

    事务就是一组原子性的SQL执行,或者说一个独立的工作单元。事务内的语句,要么全部执行成功,要么全部执行失败。

    严格意义上来说,一个运行良好的事务应该具备4个标准特性:ACID

    • 原子性(atomicity):最小工作单元,要么都成功,要么都失败回滚。
    • 一致性(consistency):数据库总是从一个一致性状态转换到另外一个一致性的状态,失败事务中的改动不会保存到数据库中
    • 隔离性(isolation):\color{red}{通常来说},一个事务的修改在最终提交前,对其他事务是不可见的。这里是考虑到下文会提及的隔离级别的,所以说通常来说,并不绝对。
    • 持久性(durability):一旦事务成功提交,其修改就会永久保存到数据库中。

    隔离级别

    • read uncommitted(未提交读)
      • 事务中的修改尚未提交,对其他事务是可见的;一般不用这个隔离级别
      • 事务可以读取其他事务未提交的数据,这就叫\color{red}{脏读}
    • read committed(提交读/不可重复读)
      • 提交读就开始满足之前的隔离性定义了:一个事务从开始到提交之前,所做的修改对其他事务都是不可见的。
      • 也叫不可重复读,因为一个事务前后两次执行同样的SQL查询,可能会得到不一样的结果(两次查询间隙可能会有其他事务提交了改动,而第二次查询读取了其他事务的提交修改)
      • 大多数数据库系统的默认隔离级别
    • repeatable read(可重复读)
      • 在提交读的基础上,做到了可重复读:前后两次同样的SQL查询得到的结果一定相同。
      • 幻读(Phantom Read):指的是当某个事务在读取某个范围内的记录时,另外一个事务有在该范围内插入了新的记录,当之前的事务再次读取该范围的记录时,会产生幻行(Phantom Row)。
      • 理论上,可重复读无法解决幻读的问题;但是Innodb存储引擎通过MVCC解决了幻读的问题;
      • MySQL的默认事务隔离级别
    • serializable(可串行化)
      • 最高的隔离级别,通过强制事务串行执行,避免了前面的幻读问题。
      • 会在\color{red}{读取}的每一行数据上都加上锁,所以可能导致大量的超时和锁争用的问题。
      • 实际应用很少用这个级别,只有在非常需要确保数据一致性并接受没有并发的情况下,才考虑使用

    死锁

    死锁是指两个或多个事务在同一资源上互相占用,并请求对方占用的资源。
    Innodb目前处理死锁的方式是,将持有最少行级排他锁的事务进行回滚(这是相对简单的死锁回滚方法)。

    多版本并发控制-MVCC(Multi-Version Concurrency Control)

    • MySQL的大多数事务型存储引擎实现的都不是简单的行级锁,基于提升并发性能的考虑,他们一般都实现了各自的MVCC。包括Oracle,PostgreSQL等其他数据库也都实现了MVCC,只是机制不尽相同,MVCC没有一个统一的实现标准。
    • 可以认为MVCC是行级锁的一个变种,很多情况下它可以避免加锁操作,因此开销更低。
    • MVCC的实现,是通过保存数据在某个时间点的快照来实现的。

    \color{red}{PS}:下面仅仅是大意的说明,实际上行记录隐藏列是创建事务id、undo log指针,还有一个delete bit标记。

    不同存储引擎的MVCC的实现是不同的,典型的有乐观并发控制和悲观并发控制。从下文可以看出innodb的MVCC实现属于乐观并发控制:

    • InnoDB的MVCC,是通过在每行记录后面保存两个隐藏的列来实现的。一个列保存了行的创建时间,另一个列保存行的过期时间(删除时间)。当然存储的并不是实际的时间值,而是系统版本号。事务开始时刻的系统版本号会作为事务的版本号,每开始一个新的事务,系统版本号都会自动递增。(类似自增主键概念,系统版本号始终比最大的事务版本号大1)下面是repeatable read的隔离级别下,MVCC的具体操作:
      • select
        • 只查找创建版本早于当前事务版本的数据行(create_system_version_number <= now_tx_id),从而确保读取的行是事务开始前就存在的,或者是事务本身插入(或修改)的数据行
        • 查找的数据行的删除版本号要么未定义(数据没被删除),要么大于当前事务版本号(del_system_version_number > now_tx_id),从而确保事务读取的行在事务开始之前未被删除,也就是说事务之后其他事务删除的话该事务不可见。
        • 说白了就是读取数据的时候只认now_tx_id之前的数据来处理,其他的忽略。
      • insert:
        • 事务给自己新插入的行的创建版本号标记为now_system_id(now_system_id即当前系统版本号)
      • delete:
        • 事务给自己删除的行的删除版本号标记为now_system_id
      • update:
        • 事务的修改实际上是删除老的行,新增一行。
        • 删除、新增的隐藏列处理逻辑与上面保持一致

    保存这两个额外的系统版本号,使大多数的读操作都可以不用加锁。可以不加锁去做到安全的并发,这可不就是乐观并发控制嘛!不足之处是需要额外的存储空间,还有一些额外行检查工作、维护工作。
    MVCC只是在可重复读和提交读这两个隔离级别下工作。其他两个隔离级别不兼容,因为未提交读总是读最新数据而不管隐藏列的事务版本,可串行化则对所有读取的行都会加锁。

    PS:脏读 VS 幻读的理解

    其实这两个概念没有什么联系,只是名称都带个读而已。就我的理解而言:

    • 脏读强调的是其他事务未提交的改动就会被读取,这里的改动包括update、insert、delete
    • 幻读从定义来看仅仅是强调其他事务提交的改动会被读取,但是这里的改动仅仅包含了insert、delete,没有涉及update。只要没有update,就可以认为两次查询的结果一致,这里说的一致应该是指特定数据行的内容一致,但是数据行的范围发生了变化,多出来的行就是幻行(我认为少了的行也可以认为一种变相的幻行,这里书籍没有明确指明)。
    • 总而言之,就是脏读是读取了未提交的update,幻读是读取了已提交的insert/delete。讨论两者的比较没啥意义,本身就是特定模式下的概念,都没啥相似之处

    相关文章

      网友评论

        本文标题:mysql事务概念简要(1)

        本文链接:https://www.haomeiwen.com/subject/efzwxktx.html