MySQL 之并发控制(Concurrent Control in MySQL)

MySQL 之并发控制

1 MySQL 架构和性能优化

1.5 并发控制

所谓并发控制:是指多个进程同时读写一条数据的时候数据库对此情况的处理方式。

1.5.1 锁机制

锁类型

锁类型 

说明

读锁 

共享锁,也称为 S 锁,只读不可写(包括当前事务),多个读互不阻塞

写锁 

独占锁,排它锁,也称为 X 锁,写锁会阻塞其它事务(不包括当前事务)的读和写

S 锁和 S 锁是兼容的,X 锁和其它锁都不兼容,举个例子,事务 T1 获取了一个行 r1 的 S 锁,另外事务 T2 可以立即获得行 r1 的 S 锁,此时 T1 和 T2 共同获得行 r1 的 S 锁,此种情况称为锁兼容,但是另外一个事务 T2 此时如果想获得行 r1 的 X 锁,则必须等待 T1 对行 r1 锁的释放,此种情况也称为锁冲突。

锁是加在索引上的,而不是加在数据上的

锁粒度

存储引擎 

锁粒度

MyISAM 

表级锁

InnoDB 

行级锁

锁的实现

实现方式 

说明

存储引擎 

自行实现该引擎的锁策略和锁粒度

自行实现 

在程序中或命令行下用命令显式实现

锁分类

锁类型 

说明

隐式锁 

由存储引擎自动施加锁

显式锁 

用户手动请求

加锁策略:在锁粒度及数据安全性寻求的平衡机制

1.5.2 显式使用锁

官方文档

https://mariadb.com/kb/en/lock-tables/https://dev.mysql.com/doc/refman/8.0/en/lock-tables.htmlhttps://dev.mysql.com/doc/refman/5.7/en/lock-tables.html

查看帮助

mysql> help lockmysql> help unlock

施加锁

LOCK TABLES tbl_name [[AS] alias] lock_type [, tbl_name [[AS] alias] lock_type] ...
#lock_type: READ|WRITE​​​​​​
#加读锁-终端1mysql> lock tables student read;Query OK, 0 rows affected (0.00 sec)
#并不影响查询-终端1mysql> select * from student limit 1;+----+----------+------+--------+| id | name     | age  | gender |+----+----------+------+--------+| 11 | zhangsan | 12   | M      |+----+----------+------+--------+1 row in set (0.00 sec)
#并不影响查询-终端2mysql> select * from student limit 1;+----+----------+------+--------+| id | name     | age  | gender |+----+----------+------+--------+| 11 | zhangsan | 12   | M      |+----+----------+------+--------+1 row in set (0.00 sec)
#不可写-终端1mysql> update student set age=21 where id=11;ERROR 1099 (HY000): Table 'student' was locked with a READ lock and can't be updated
#不可写-终端2,直接是阻塞状态mysql> update student set age=21 where id=11;

释放锁

UNLOCK TABLES

​​​

#释放锁-终端1mysql> unlock tables;Query OK, 0 rows affected (0.00 sec)
#写成功-终端2,锁一释放后,终端2中的 update 就不再阻塞了mysql> update student set age=21 where id=11;Query OK, 1 row affected (10.95 sec)Rows matched: 1 Changed: 1 Warnings: 0
#加写锁-终端1mysql> lock table student write;Query OK, 0 rows affected (0.00 sec)
#加锁后可读可写-终端1mysql> select * from student limit 1;+----+----------+------+--------+| id | name     | age  | gender |+----+----------+------+--------+| 11 | zhangsan | 21   | M      |+----+----------+------+--------+1 row in set (0.00 sec)
mysql> update student set age=22 where id=11;Query OK, 1 row affected (0.00 sec)Rows matched: 1 Changed: 1 Warnings: 0
#其它终端或进程不可读也不可写,读写操作均被阻塞mysql> select * from student limit 1;mysql> update student set age=23 where id=11;

连接或进程只能释放其自身施加的锁

一个session只能为自己获取锁和释放锁,不能为其他session获取锁,也不能释放由其他session保持的锁

#锁student表-终端1mysql> lock tables student write;Query OK, 0 rows affected (0.00 sec)
#无法操作student表-终端2mysql> select * from student limit 1;^C^C -- query abortedERROR 1317 (70100): Query execution was interrupted
#执行释放-终端2mysql> unlock tables ;Query OK, 0 rows affected (0.00 sec)
#依然无法操作-终端2mysql> select * from student limit 1;
#终端1解锁后其它终端才能操作mysql> unlock tables ;Query OK, 0 rows affected (0.00 sec)

全局锁

关闭正在打开的表(清除查询缓存),通常在备份前加全局读锁
​​​​​​​

FLUSH TABLES [tb_name[,...]] [WITH READ LOCK]
    FLUSH TABLES#关闭所有打开的表,强制关闭所有正在使用的表,并刷新查询缓存和预准备语句缓存,不会刷新脏块
    FLUSH TABLES WITH READ LOCK#关闭所有打开的表并使用全局读锁锁定所有数据库的所有表,不会刷新脏块,也不阻塞日志表写入,例如查询日志,慢日志等
    FLUSH TABLES tbl_name tb_name[,...] WITH READ LOCK#操作指定表
    FLUSH TABLES tbl_name [tb_name[,...]] FOR EXPORT#刷新脏块#脏块也称为脏页,当内存数据页和磁盘数据页上的内容不一致时,我们称这个内存页为脏页​​​​​​​
    
    #加锁mysql> flush tables with read lock;Query OK, 0 rows affected (0.00 sec)
    #可查mysql> select * from student where id=11;+----+----------+------+--------+| id | name     | age  | gender |+----+----------+------+--------+| 11 | zhangsan | 25   | M      |+----+----------+------+--------+1 row in set (0.00 sec)
    #不可更新,不可删除mysql> update student set age=28 where id=11;ERROR 1223 (HY000): Can't execute the query because you have a conflicting read lock
    mysql> delete from student where id=11;ERROR 1223 (HY000): Can't execute the query because you have a conflicting read lock
    #不能创建账号,创建账号本质也是写表mysql> create user test@'localhost' identified by '123456';ERROR 1223 (HY000): Can't execute the query because you have a conflicting read lock
    #不能创建数据库mysql> create database db1;ERROR 1223 (HY000): Can't execute the query because you have a conflicting read lock

    查询时加锁

    这种加锁方式只是在执行的过程中加锁,等查询结束之后,锁会被自动释放​​​​​​​

    SELECT clause [FOR UPDATE | LOCK IN SHARE MODE]LOCK IN SHARE MODE #对读取到的记录加S锁

    FOR UPDATE #对读取到的记录加X锁

    1.5.3 事务

    事务 ( Transactions )

    事务是一组具有原子性的 SQL 语句,或者说一个独立单元。可以理解为一个事务对应的是一组完整的业务,这个业务由一条或多条 SQL 语句组成。所谓原子性是指,这一组业务中的 SQL 语句不可分割,所以,要么全部 SQL 语句都执行成功,事务也就执行成功;只要有一条 SQL 语句执行失败,则整个事务都要回滚到事务开始前。

    事务日志

    记录事务的日志,可以根据此日志实现事务的回滚(undo),重新提交(redo) 等功能。

    此处说的事务仅限于 InnoDB 引擎下,在 MySQL 中,MyISAM 引擎是不支持事务的。

    1.5.3.1 事务特性

    事务有 ACID 四个特性

    原子性( Atomicity )

    原子性又称不可分割性。一个事务中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。

    一致性( Consistency )

    事务的执行结果,必须是符合预期的,这表示在事务中进行的数据读写,完全符合所有的预设规则,这包含资料的精确度、串联性以及后续数据库可以自发性地完成预定的工作。

    隔离性( Isolation )

    隔离性又称独立性。MySQL 允许多个事务并发,同时进行写操作,隔离性用于实现事务的并发控制,防止多个事务并发执行时的交叉执行而导致的数据不一致。事务的隔离分为不同的级别,包括读未提交( Read uncommitted ),读提交( read committed ),可重复读( repeatable read ),串行化( Serializable )。

    持久性( Durability )

    事务执行成功后,其对于数据的修改会永久保存于数据库中。

    1.5.3.2 管理事务

    显示启动事务

    BEGINBEGIN WORKSTART TRANSACTION

    结束事务

    #提交执行COMMIT
    #回滚ROLLBACK​​​​​​​
    
    mysql> select * from student where id=11;+----+----------+------+--------+| id | name     | age  | gender |+----+----------+------+--------+| 11 | zhangsan | 28   | M      |+----+----------+------+--------+1 row in set (0.00 sec)
    mysql> select * from t1;+----+------+------+--------+| id | name | age  | gender |+----+------+------+--------+| 10 | test | 123  | M      |+----+------+------+--------+1 row in set (0.00 sec)
    #开启事务mysql> begin;Query OK, 0 rows affected (0.00 sec)
    #updatemysql> update student set age=30 where id=11;Query OK, 1 row affected (0.00 sec)Rows matched: 1 Changed: 1 Warnings: 0
    #insertmysql> insert into t1 (name,age,gender)values('zhangsan',30,'F');Query OK, 1 row affected (0.00 sec)
    #再次查询mysql> select * from student where id=11;+----+----------+------+--------+| id | name     | age  | gender |+----+----------+------+--------+| 11 | zhangsan | 30   | M      |+----+----------+------+--------+1 row in set (0.00 sec)
    mysql> select * from t1;+----+----------+------+--------+| id | name     | age  | gender |+----+----------+------+--------+| 10 | test     | 123  | M      || 11 | zhangsan | 30   | F      |+----+----------+------+--------+2 rows in set (0.00 sec)​​​​​​​
    
    #在另一个终端中查看,并没有看到数据,因为事务没有提交mysql> select * from student where id=11;+----+----------+------+--------+| id | name     | age  | gender |+----+----------+------+--------+| 11 | zhangsan | 28   | M      |+----+----------+------+--------+1 row in set (0.00 sec)
    mysql> select * from t1;+----+------+------+--------+| id | name | age  | gender |+----+------+------+--------+| 10 | test | 123  | M      |+----+------+------+--------+1 row in set (0.00 sec)​​​​​​​
    
    #提交事务mysql> commit;Query OK, 0 rows affected (0.00 sec)#在另一个终端中再次查询mysql> select * from student where id=11;+----+----------+------+--------+| id | name     | age  | gender |+----+----------+------+--------+| 11 | zhangsan | 30   | M      |+----+----------+------+--------+1 row in set (0.01 sec)
    mysql> select * from t1;+----+----------+------+--------+| id | name     | age  | gender |+----+----------+------+--------+| 10 | test     | 123  | M      || 11 | zhangsan | 30   | F      |+----+----------+------+--------+2 rows in set (0.00 sec)

    回滚操作

    • ysql> select * from t1;+----+----------+------+--------+| id | name     | age  | gender |+----+----------+------+--------+| 10 | test     | 123  | M      || 11 | zhangsan | 30   | F      |+----+----------+------+--------+2 rows in set (0.00 sec) mysql> begin;Query OK, 0 rows affected (0.00 sec)mysql> delete from t1;Query OK, 2 rows affected (0.00 sec) #当前终端中查询己经没有数据了mysql> select * from t1;Empty set (0.00 sec) #在另一个终端中查询mysql> select * from t1;+----+----------+------+--------+| id | name     | age  | gender |+----+----------+------+--------+| 10 | test     | 123  | M      || 11 | zhangsan | 30   | F      |+----+----------+------+--------+2 rows in set (0.00 sec) #事务回滚mysql> rollback;Query OK, 0 rows affected (0.00 sec) mysql> select * from t1;+----+----------+------+--------+| id | name     | age  | gender |+----+----------+------+--------+| 10 | test     | 123  | M      || 11 | zhangsan | 30   | F      |+----+----------+------+--------+2 rows in set (0.00 sec)

    只有事务型存储引擎中的 DML 语句才能支事务操作​​​​​​​

    mysql> select * from t1;+----+----------+------+--------+| id | name     | age  | gender |+----+----------+------+--------+| 10 | test     | 123  | M      || 11 | zhangsan | 30   | F      |+----+----------+------+--------+2 rows in set (0.00 sec)
    #开启事务mysql> begin;Query OK, 0 rows affected (0.00 sec)
    #清空mysql> truncate table t1;Query OK, 0 rows affected (0.00 sec)mysql> select * from t1;Empty set (0.01 sec)
    #回滚mysql> rollback;Query OK, 0 rows affected (0.00 sec)
    #数据并没有回来,因为 truncate 属于 DDL 语句,不支持事务操作mysql> select * from t1;Empty set (0.00 sec)
    • mysql> create database db1;Query OK, 1 row affected (0.00 sec) mysql> show databases like 'db1%';+-----------------+| Database (db1%) |+-----------------+| db1             |+-----------------+1 row in set (0.00 sec) mysql> begin;Query OK, 0 rows affected (0.00 sec) #drop 也不是DML语句,所以也无法回滚mysql> drop database db1;Query OK, 0 rows affected (0.01 sec) mysql> show databases like 'db1%';Empty set (0.00 sec) mysql> rollback;Query OK, 0 rows affected (0.00 sec) mysql> show databases like 'db1%';Empty set (0.00 sec)

    自动提交
    ​​​​​​​

    set autocommit={1|0}
    #MySQL 默认开启了自动提交mysql> select @@autocommit;+--------------+| @@autocommit |+--------------+| 1            |+--------------+1 row in set (0.00 sec)
    #关闭自动提交mysql> set autocommit=0;Query OK, 0 rows affected (0.00 sec)
    mysql> select @@autocommit;+--------------+| @@autocommit |+--------------+| 0            |+--------------+1 row in set (0.00 sec)
    #查表,无记录mysql> select * from t1;Empty set (0.00 sec)
    #插入数据mysql> insert into t1 (name,age,gender) values ('lisi',22,'M');Query OK, 1 row affected (0.00 sec)
    #此处是未提交的中间状态mysql> select * from t1;+----+------+------+--------+| id | name | age  | gender |+----+------+------+--------+| 1  | lisi | 22   | M      |+----+------+------+--------+1 row in set (0.00 sec)
    #在另一个终端中查询,没有数据mysql> select * from t1;Empty set (0.00 sec)
    #手动提交mysql> commit;Query OK, 0 rows affected (0.00 sec)
    #在另一个终端中查询mysql> select * from t1;+----+------+------+--------+| id | name | age  | gender |+----+------+------+--------+| 1  | lisi | 22   | M      |+----+------+------+--------+1 row in set (0.00 sec)

    atuocommit 是会话级别的变量,同时也是一个服务器选项,需要此选项永久生效,可以写配置文件​​​​​​​

    [root@rocky86 ~]# vim /etc/my.cnf[mysqld]autocommit=0
    #重启服务[root@rocky86 ~]# systemctl restart mysqld.service
    #查看[root@rocky86 ~]# mysql -e 'select @@autocommit'+--------------+| @@autocommit |+--------------+| 0            |+--------------+

    MySQL中,每次写操作都是一个事务操作,批量写时,可以将多次提交合并成一次提交,以加快执行速度

    mysql> select count(*) from testlog;+----------+| count(*) |+----------+| 0        |+----------+1 row in set (0.00 sec)
    #执行存储过程,插入10W条记录用时53秒mysql> call sp_testlog;Query OK, 1 row affected (53.21 sec)
    mysql> select count(*) from testlog;+----------+| count(*) |+----------+| 100000   |+----------+1 row in set (0.02 sec)
    #清空表mysql> delete from testlog;select count(*) from testlog;Query OK, 100000 rows affected (0.31 sec)+----------+| count(*) |+----------+| 0        |+----------+1 row in set (0.00 sec)
    #事务批量提交mysql> begin;call sp_testlog;commit;Query OK, 1 row affected (3.01 sec)

    事务支持保存点

    事务保存点可以只提交部分事务​​​​​​​

    SAVEPOINT identifierROLLBACK [WORK] TO [SAVEPOINT] identifierRELEASE SAVEPOINT identifier​​​​​​​
    
    #查表mysql> select * from t1;Empty set (0.00 sec)
    #开启事务mysql> begin;Query OK, 0 rows affected (0.00 sec)
    #插入数据mysql> insert into t1 (name,age,gender)values('u1',11,'M');Query OK, 1 row affected (0.00 sec)
    #保存回滚点 p1mysql> savepoint p1;Query OK, 0 rows affected (0.00 sec)
    #插入数据mysql> insert into t1 (name,age,gender)values('u2',22,'M');Query OK, 1 row affected (0.00 sec)
    #保存回滚点 p2mysql> savepoint p2;Query OK, 0 rows affected (0.00 sec)
    #插入数据mysql> insert into t1 (name,age,gender)values('u3',33,'M');Query OK, 1 row affected (0.00 sec)
    #保存回滚点 p3mysql> savepoint p3;Query OK, 0 rows affected (0.00 sec)
    #未提交状态mysql> select * from t1;+----+------+------+--------+| id | name | age  | gender |+----+------+------+--------+| 5  | u1   | 11   | M      || 6  | u2   | 22   | M      || 7  | u3   | 33   | M      |+----+------+------+--------+3 rows in set (0.00 sec)
    #回滚至保存点p2mysql> rollback to p2;Query OK, 0 rows affected (0.00 sec)
    #提交mysql> commit;Query OK, 0 rows affected (0.00 sec)
    #只提交了部份数据mysql> select * from t1;+----+------+------+--------+| id | name | age  | gender |+----+------+------+--------+| 5  | u1   | 11   | M      || 6  | u2   | 22   | M      |+----+------+------+--------+2 rows in set (0.00 sec)

    查看事务​​​​​​​

    #查看当前正在进行的事务SELECT * FROM INFORMATION_SCHEMA.INNODB_TRX;
    #以下两张表在MySQL8.0中已取消#查看当前锁定的事务SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS;
    #查看当前等锁的事务SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCK_WAITS;

    死锁

    死锁是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去,此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等的进程称为死锁进程。

    在 MySQL中,两个或多个事务在同一资源相互占用,并请求锁定对方占用的资源的状态。

    死锁并不是 MySQL 中独有的,只要出现资源互相竞争的情况都有可能出现死锁。

    在 MySQL 中出现死锁时,MySQL 服务会自行处理,自行回滚一个事务,防止死锁的出现。​​​​​​​

    #查看innodb状态,可以查看锁信息show engine innodb status;
    #查看正在进行中的事务SELECT * FROM information_schema.INNODB_TRX;
    #查看锁SELECT * FROM information_schema.INNODB_LOCKS;
    #MySQL8.0.13及以后使用此语句查看锁SELECT * FROM performance_schema.data_locks;#查看锁等待SELECT * FROM information_schema.INNODB_LOCK_WAITS;
    #MySQL8.0.13及以后使用此语句查看锁等待SELECT * FROM performance_schema.data_lock_waits;​​​​​​​
    
    #查看正在进行的事务mysql> SELECT * FROM information_schema.INNODB_TRX\G*************************** 1. row ***************************trx_id: 218646trx_state: RUNNINGtrx_started: 2022-12-27 20:07:24trx_requested_lock_id: NULLtrx_wait_started: NULLtrx_weight: 2trx_mysql_thread_id: 10   #线程IDtrx_query: NULLtrx_operation_state: NULLtrx_tables_in_use: 0trx_tables_locked: 1trx_lock_structs: 2trx_lock_memory_bytes: 1128trx_rows_locked: 1trx_rows_modified: 0trx_concurrency_tickets: 0trx_isolation_level: REPEATABLE READtrx_unique_checks: 1trx_foreign_key_checks: 1trx_last_foreign_key_error: NULLtrx_adaptive_hash_latched: 0trx_adaptive_hash_timeout: 0trx_is_read_only: 0trx_autocommit_non_locking: 0trx_schedule_weight: NULL

    ​​​​​​​#查看所有线程mysql> show processlist\G*************************** 1. row ***************************Id: 5User: event_schedulerHost: localhostdb: NULLCommand: DaemonTime: 33365State: Waiting on empty queueInfo: NULL*************************** 2. row ***************************Id: 10User: rootHost: localhostdb: testdbCommand: SleepTime: 387State:Info: NULL*************************** 3. row ***************************Id: 16User: rootHost: localhostdb: performance_schemaCommand: QueryTime: 0State: initInfo: show processlist3 rows in set (0.00 sec)

    找到未完成导致阻塞的事务​​​​​​​

    #在第一个会话中执行mysql> begin;Query OK, 0 rows affected (0.00 sec)
    mysql> update student set age=1 where id=10;Query OK, 1 row affected (0.00 sec)Rows matched: 1 Changed: 1 Warnings: 0
    #在第二个会话中执行mysql> begin;Query OK, 0 rows affected (0.00 sec)
    mysql> update student set age=2 where id=10;Query OK, 1 row affected (0.00 sec)Rows matched: 1 Changed: 1 Warnings: 0
    #在第三个会话中执行mysql> show engine innodb status;
    mysql> SELECT * FROM information_schema.INNODB_TRX\G
    mysql> show processlist\G​​​​​​
    #杀掉未完成的事务mysql> kill 10;ERROR 1317 (70100): Query execution was interrupted
    MariaDB [testdb]> kill 11;Query OK, 0 rows affected (0.001 sec)​​​​​​​
    
    #查看事务锁的超时时长,默认50smysql> show global variables like 'innodb_lock_wait_timeout';+--------------------------+-------+| Variable_name            | Value |+--------------------------+-------+| innodb_lock_wait_timeout | 50    |+--------------------------+-------+1 row in set (0.00 sec)
    MariaDB [(none)]> show global variables like 'innodb_lock_wait_timeout';+--------------------------+-------+| Variable_name            | Value |+--------------------------+-------+| innodb_lock_wait_timeout | 50    |+--------------------------+-------+1 row in set (0.001 sec)

    1.5.3.3 事务隔离级别

    MySQL 的事务隔离级别一共有四个,分别是读未提交、读已提交、可重复读以及可串行化。

    MySQL 的隔离级别的作用就是让事务之间互相隔离,互不影响,这样可以保证事务的一致性。

    读未提交(READ UNCOMMITTED)

    在读未提交隔离级别下,事务A可以读取到事务B修改过但未提交的数据。可能发生脏读、不可重复读和幻读问题,一般很少使用此隔离级别。

    读已提交(READ COMMITTED)

    在读已提交隔离级别下,事务B只能在事务A修改过并且已提交后才能读取到事务B修改的数据。读已提交隔离级别解决了脏读的问题,但可能发生不可重复读和幻读问题,一般很少使用此隔离级别。

    可重复读(REPEATABLE READ)

    在可重复读隔离级别下,事务B只能在事务A修改过数据并提交后,自己也提交事务后,才能读取到事务B修改的数据。可重复读隔离级别解决了脏读和不可重复读的问题,但可能发生幻读问题。

    可串行化(SERIALIZABLE)

    可串行化,又称序列化。各种问题(脏读、不可重复读、幻读)都不会发生,通过加锁实现(读锁和写锁)。

    以上四种隔离级别,从上往下,隔离强度逐渐增强,性能逐渐变差,需要消耗的 MySQL 的资源越多,所以并不是隔离强度越高越好,采用哪种隔离级别要根据系统需求权衡决定,MySQL 中默认的隔离级别是可重复读。

    事务并发中可能出现的问题

    脏读(Dirty Read)

    脏读指的是读到了其他事务未提交的数据,未提交意味着这些数据可能会回滚,也就是可能最终不会存到数据库中,也就是不存在的数据。读到了并不一定最终存在的数据,这就是脏读。(脏读只在读未提交隔离级别才会出现)

    不可重复读(Non-Repeatable Read)

    不可重复读指的是在同一事务内,不同的时刻读到的同一批数据可能是不一样的,可能会受到其他事务的影响,比如其他事务改了这批数据并提交了。(不可重复读在读未提交和读已提交隔离级别都可能会出现)

    幻读(Phantom)

    一个事务先根据某些条件查询出一些记录,之后另一个事务又向表中插入了符合这些条件的记录,原先的事务再次按照该条件查询时,能把另一个事务插入的记录也读出来。(幻读在读未提交、读已提交、可重复读隔离级别都可能会出现)

    隔离级别 

    脏读 

    不可重复读 

    幻读 

    加读锁

    读未提交 

    Yes 

    Yes 

    Yes 

    No

    读已提交 

    No 

    Yes 

    Yes 

    No

    可重复读(默认) 

    No 

    No 

    Yes 

    No

    序列化 

    No 

    No 

    No 

    Yes

    MVCC和事务的隔离级别

    MVCC(多版本并发控制机制)只在 READ COMMITTED 和 REPEATABLE READ 两个隔离级别下工作。其他两个隔离级别都和 MVCC 不兼容,因为 READ UNCOMMITTED总是读取最新的数据行,而不是符合当前事务版本的数据行。而SERIALIZABLE则会对所有读取的行都加锁。

    查看隔离级别:默认都是可重复读​​​​​​​

    #MySQL8.0及以后mysql> select @@transaction_isolation;+-------------------------+| @@transaction_isolation |+-------------------------+| REPEATABLE-READ         |+-------------------------+1 row in set (0.00 sec)
    mysql> select @@tx_isolation;ERROR 1193 (HY000): Unknown system variable 'tx_isolation'
    #MySQL8.0以前及MariadbMariaDB [(none)]> select @@tx_isolation;+-----------------+| @@tx_isolation  |+-----------------+| REPEATABLE-READ |+-----------------+1 row in set (0.001 sec)

    指定事务隔离级别​​​​​​​

    #服务器变量指定,可在SESSION和GLOBAL级别进行设置
    #MySQL8.0之前及MariaDBSET tx_isolation='READ-UNCOMMITTED|READ-COMMITTED|REPEATABLEREAD|SERIALIZABLE'
    #MySQL8.0及以后SET transaction_isolation='READ-UNCOMMITTED|READ-COMMITTED|REPEATABLEREAD|SERIALIZABLE'​​​​​​​
    
    #服务器选项中指定vim /etc/my.cnf[mysqld]transaction-isolation=SERIALIZABLE

    修改事务隔离级别,查看未提交的事务中的数据​​​​​​​

    #隔离级修改为 读未提交[root@rocky86 ~]# vim /etc/my.cnf[mysqld]transaction-isolation=READ-UNCOMMITTED
    #重启服务[root@rocky86 ~]# systemctl restart mysqld.service
    #查看mysql> select @@transaction_isolation;+-------------------------+| @@transaction_isolation |+-------------------------+| READ-UNCOMMITTED        |+-------------------------+1 row in set (0.00 sec)​​​​​​​
    
    #另一个终端中查看mysql> select * from t1 where id=5;+----+------+------+--------+| id | name | age  | gender |+----+------+------+--------+| 5  | u1   | 44   | M      |+----+------+------+--------+1 row in set (0.00 sec)​​​​​​​
    
    #原终端中开启事务mysql> begin;Query OK, 0 rows affected (0.00 sec)
    #修改数据,但并没有执行commitmysql> update t1 set age=33 where id=5;Query OK, 1 row affected (0.00 sec)Rows matched: 1 Changed: 1 Warnings: 0​​​​​​​​​​​​​
    #另一个终端中查看,能看到未提交的事务mysql> select * from t1 where id=5;+----+------+------+--------+| id | name | age  | gender |+----+------+------+--------+| 5  | u1   | 33   | M      |+----+------+------+--------+1 row in set (0.00 sec)

    你可能感兴趣的:(用心耕耘,开启数据库之门,mysql,数据库,运维开发,云计算)