Mysql逻辑架构事务并发控制

Posted zqq_hello_world

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Mysql逻辑架构事务并发控制相关的知识,希望对你有一定的参考价值。

文章目录

mysql

Mysql逻辑架构

  • 第一层:负责连接处理、授权认证、安全等等。
  • 第二层:大多数Mysql的核心服务功能都在这一层,包括查询解析分析优化缓存以及所有的内置函数,所有跨存储引擎的功能都在这一层实现:存储过程触发器视图等。
  • 第三层:存储引擎存储引擎负责Mysql中数据的存储和提取,每个存储引擎都有它的优势和劣势,服务器通过API与存储引擎进行通信。这些接口屏蔽了不同存储引擎之间的差异,使得这些差异对上层的查询过程透明。

连接管理

每个客户端连接都会在服务器进程中拥有一个线程,这个连接的查询只会在这个单独的线程中执行。服务器会负责缓存线程(线程池),不需要为每个新建的连接创建或者销毁线程。

客户端连接到MySql服务器时,服务器需要对其进认证。认证基于用户名、原始主机信息和密码。一但客户端连接成功,服务器会继续验证该客户端是否具有执行某个特定查询的权限。

优化与执行

Mysql会解析查询,并创建内部数据结构(解析树),然后对其进行各种优化,包括重写查询决定表的读取顺序,以及选择合适的索引等。

对于SELECT语句,在解析查询之前,服务器会先检查查询缓存,如果能够在其中找到对应的查询,服务器就不必再执行查询解析、优化和执行的整个过程,而是直接返回查询缓存中的结果集。

并发控制

读写锁

  • 读锁(read lock)

    读锁也叫共享锁(shared lock)。读锁是共享的,或者说是相互不阻塞的。多个客户端在同一时刻可以同时读取同一个资源,互不干扰

  • 写锁(write lock)

    写锁也叫排他锁(exclusive lock)。写锁是排他的,也就是说一个写锁会阻塞其他的写锁和读锁,保证在给定的时间里,只有一个用户能执行写入,并防止其他用户读取正在写入的同一资源。

在实际的数据库系统中,每时每刻都在发生锁定,当某个用户在修改某一部分数据时,Mysql会通过锁定防止其他用户读取同一数据。

锁粒度

一种提高共享资源并发性的方式就是让锁对象更有选择性。尽量只锁定需要修改的部分数据,而不是所有的资源。更理想的方式是,只对会修改的数据片进行精确的锁定。任何时候,在给定的资源上,锁定的数据量越少,则系统的并发程度越高

加锁也需要消耗资源。锁的各种操作,包括获得锁、检查锁是否解除、释放锁等,都会增加系统的开销。锁策略就是在锁的开销和数据的安全性之间寻求平衡,这种平衡也会影响到性能。两种重要的锁策略表锁(table lock)行级锁(row lock)

  • 表锁(table lock)

    表锁是Mysql中最基本的锁策略,并且是锁开销最小的策略。表锁会锁定整张表。一个用户在对表进行写操作前(插入、删除、更新等),需要先获得写锁,这会阻塞其他用户对该表的所有读写操作。只有没有写锁时,其他用户才能获得读锁,读锁之间是不相互阻塞的。

    写锁比读锁有更高的优先级,因此一个写锁请求可能会被插入到读锁队列前面。反之读锁不能插入到写锁前面。

    尽管存储引擎可以管理自己的锁,Mysql本身还是会使用各种有效的表锁来实现不同的目的。列如服务器会为诸如ALTER TABLE之类的语句使用表锁,而忽略存储引擎的锁机制

  • 行级锁(row lock)

    行级锁可以最大程度地支持并发处理(同事也带来了更大的锁开销)。行级锁只在存储引擎层实现,而Mysql服务器层没有实现,服务器层完全不了解存储引擎中的锁实现。列如InnoDB存储引擎实现了行级锁。

事务

事务是一组原子性的SQL操作,或者说一个独立的工作单元。如果数据库引擎能够成功地对数据库应用该组查询的全部语句,那么就执行该组查询。如果其中任何一条语句因为崩溃或其他原因无法执行,那么所有语句都不会执行。也就是说,事务内语句,要么全部执行成功,要么全部执行失败

一般来说事务必须满足4个条件(事务的四大特性ACID):原子性(atomicity)一致性(consistency)隔离性(isolation)持久性(durability)

  • 原子性(atomicity)

    一个事务必须被视为一个不可分割的最小单元,整个事务中的所有操作要么全部提交成功,要么全部失败回滚,对于一个事务来说,不可能只执行其中一部分操作,这就是事务的原子性。

  • 一致性(consistency)

    数据库总是从一个一致性状态转换到另外一个一致性状态。在事务开始之前和事务结束以后,数据库的完整性没有被破坏。比如一个事务多个语句修改数据库,后面的语句失败了,前面的语句也不会修改成功,数据的一致性状态是完整的。

  • 隔离性(isolation)

    通常来说(受隔离级别影响),一个事务所做的修改在最终提交以前,对其他事务是不可见的。

  • 持久性(durability)

    一旦事务提交,则其所做的修改就会永久保存到数据库中。此时即使系统崩溃,修改的数据也不会丢失。

隔离级别

在SQL标准中定义了四种隔离级别,每一种级别都规定了一个事务中所做的修改,哪些在事务内和事务间是可见的,哪些是不可见的。较低级别的隔离通常可以执行更高的并发,系统的开销也更低

事务四种隔离级别

  • READ UNCOMMITTED(未提交读)

    在READ UNCOMMITTED级别,事务中的修改,即使没有提交,对其他事务也都是可见的。事务可以读取未提交的数据,也被称为脏读(Dirty Read)。这个级别会导致很多问题,从性能上来说,READ UNCOMMITTED不会比其他级别好太多,但却缺乏其他级别的好处,除非真的有非常必要的理由,在实际应用中一般很少使用。

  • READ COMMITTED(提交读)

    一个事务从开始直到提交之前,所做的任何修改对其他事务都是不可见的。这个级别有时候也叫做不可重复读,因为两次执行同样的查询,可能会得到不一样的结果。

  • REPEATABLE READ(可重复读)

    可重复读解决了脏读的问题。该级别保证了在同一事务中多次读取同样的记录结果是一致的。但是理论上,可重复读隔离级别还是无法解决另外一个幻读的问题。所谓的幻读,指的是当某个事务再次读取该范围的记录时,另外一个事务又在该范围内插入新的记录,当之前的事务再次读取该范围的记录时,会产生幻行。InnoDB和XtraDB存储引擎通过多版本并发控制(MVCC,Multiversion Concurrency Control)解决了幻读的问题。

    可重复读是Mysql默认事务隔离级别

  • SERIALIZABLE(可串行化)

    SERIALIZABLE是最高的隔离级别。它通过强制事务串行执行,避免了幻读的问题。简单的来说SERIALIZABLE会在读取的每一行上都加锁,所以可能导致大量的超时和锁争用的问题。实际应用中很少用到这个隔离级别,只有在非常需要确保数据的一致性而且可以接受没有并发的情况下,才考虑用该级别。

死锁

死锁是指两个或多个事务在同一资源上相互占用,并请求锁定对方占用的资源,从而导致恶性循环的现象。当多个事务试图已不同的顺序锁定资源时,就可能会产生死锁。

为了解决死锁问题,数据库系统实现了各种死锁检测和死锁超时机制。越复杂的系统,比如InnoDB存储引擎,越能检测到死锁的循环依赖,并立即返回一个错误。这种解决方式很有效,否则死锁会导致出现非常慢的查询。还有一种方式,就是当查询的时间达到锁等待超时的设定后放弃锁请求,这种方式通常来说不太好。

死锁的产生有双重原因:有些因为真正的数据冲突,这种情况通常很难避免,但有些则完全是由存储引擎实现方式导致的。

死锁发生以后,只有部分或者完全回滚其中一个事务,才能打破死锁。对于事务型的系统,这是无法避免的,所以应用程序在设计时必须考虑如何处理死锁。大多数情况下只需要重新执行因死锁回滚的事务即可

Mysql中的事务

Mysql默认采用自动提交(AUTOCOMMIT)模式。如果不是显示的开始一个事务,则每个查询(增删改)都被当作一个事务执行提交操作。

当前连接中,可以设置AUTOCOMMIT变量来启用或禁用自动提交模式

#查看自动提交是否开启,1或者ON表示启用,0或者OFF标识禁用。
SHOW VARIABLES like 'AUTOCOMMIT';
#关闭自动提交事务,设置成1 则表示开启自动提交。当前连接有效。
SET AUTOCOMMIT = 0;

当自动提交关闭时(0或者OFF),所有的SQL语句都在一个事务中,直到显示的执行COMMIT(提交事务)或ROLLBACK(回滚事务),该事务结束。对于非事务型的表,修改AUTOCOMMIT不会有任何影响。

显示的开启事务(InnoDB存储引擎)

  1. Mysql通过BEGIN 或 START TRANSACTION 显式地开启一个事务

  2. COMMIT会提交事务,ROLLBACK会回滚事务

    -- 通过BEGIN开启事务,同一会话中可分批执行命令测试
    BEGIN;
    update member set name = '17' where id = '17';
    COMMIT;
    
    -- 通过START TRANSACTION开启事务
    START TRANSACTION;
    update member set name = '18' where id = '17';
    COMMIT;
    
    -- 通过ROLLBACK回滚事务,修改未生效
    BEGIN;
    update member set name = '19' where id = '17';
    ROLLBACK;
    

事务隔离级别

Mysql能够识别4个ANSI隔离级别,InnoDB引擎也支持所有的隔离级别。

Mysql可以设置事务隔离级别,可以通过SET TRANSACTION ISOLATION LEVEL设置或在配置文件中设置整个数据库的隔离级别,默认REPEATABLE-READ(可重复读)。

设置当前会话的事务隔离级别

-- 事务隔离级别设置成 读提交
set session transaction isolation level read committed;

-- 查看当前会话隔离级别(有版本限制)
select @@transaction_isolation;

在事务中混合使用存储引擎

Mysql服务器层不管理事务,事务是由下层存储引擎实现的,所以在一个事务中使用多种存储引擎是不可靠的。如果在事务中使用了非事务存储引擎的表,在正常提交的情况下不会有什么问题,但如果该事务回滚,非事务型的表上的变更就无法撤销,这会导致数据库处于不一致状态,很难恢复,事务最终结果无法确定

在非事务型的表上执行事务相关操作的时候,Mysql通常不会发出提醒,也不会报错。有时候只有回滚的时候才会发出一股警告。但大多数情况下对非事务型的操作都不会有提示。

以上是关于Mysql逻辑架构事务并发控制的主要内容,如果未能解决你的问题,请参考以下文章

MySQL 进阶 InnoDB引擎 -- 逻辑存储结构架构(内存结构磁盘结构后台线程)事务原理(事务基础redo logundo logMVCC多版本并发控制:版本链 ReadView)

mysql事务与多版本并发控制

mysql事务与多版本并发控制

mysql在django中开启事务,实现悲观锁和乐观锁

事务隔离实现并发控制:MySQL系列之十

NHibernate之并发控制简记