1 MySQL 架构和性能优化
所谓并发控制:是指多个进程同时读写一条数据的时候数据库对此情况的处理方式。
锁类型
锁类型 |
说明 |
读锁 |
共享锁,也称为 S 锁,只读不可写(包括当前事务),多个读互不阻塞 |
写锁 |
独占锁,排它锁,也称为 X 锁,写锁会阻塞其它事务(不包括当前事务)的读和写 |
S 锁和 S 锁是兼容的,X 锁和其它锁都不兼容,举个例子,事务 T1 获取了一个行 r1 的 S 锁,另外事务 T2 可以立即获得行 r1 的 S 锁,此时 T1 和 T2 共同获得行 r1 的 S 锁,此种情况称为锁兼容,但是另外一个事务 T2 此时如果想获得行 r1 的 X 锁,则必须等待 T1 对行 r1 锁的释放,此种情况也称为锁冲突。
锁是加在索引上的,而不是加在数据上的
锁粒度
存储引擎 |
锁粒度 |
MyISAM |
表级锁 |
InnoDB |
行级锁 |
锁的实现
实现方式 |
说明 |
存储引擎 |
自行实现该引擎的锁策略和锁粒度 |
自行实现 |
在程序中或命令行下用命令显式实现 |
锁分类
锁类型 |
说明 |
隐式锁 |
由存储引擎自动施加锁 |
显式锁 |
用户手动请求 |
加锁策略:在锁粒度及数据安全性寻求的平衡机制
官方文档
https://mariadb.com/kb/en/lock-tables/
https://dev.mysql.com/doc/refman/8.0/en/lock-tables.html
https://dev.mysql.com/doc/refman/5.7/en/lock-tables.html
查看帮助
mysql> help lock
mysql> help unlock
施加锁
LOCK TABLES tbl_name [[AS] alias] lock_type [, tbl_name [[AS] alias] lock_type] ...
#lock_type: READ|WRITE
#加读锁-终端1
mysql> lock tables student read;
Query OK, 0 rows affected (0.00 sec)
#并不影响查询-终端1
mysql> select * from student limit 1;
+----+----------+------+--------+
| id | name | age | gender |
+----+----------+------+--------+
| 11 | zhangsan | 12 | M |
+----+----------+------+--------+
1 row in set (0.00 sec)
#并不影响查询-终端2
mysql> select * from student limit 1;
+----+----------+------+--------+
| id | name | age | gender |
+----+----------+------+--------+
| 11 | zhangsan | 12 | M |
+----+----------+------+--------+
1 row in set (0.00 sec)
#不可写-终端1
mysql> 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
#释放锁-终端1
mysql> 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
#加写锁-终端1
mysql> lock table student write;
Query OK, 0 rows affected (0.00 sec)
#加锁后可读可写-终端1
mysql> 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表-终端1
mysql> lock tables student write;
Query OK, 0 rows affected (0.00 sec)
#无法操作student表-终端2
mysql> select * from student limit 1;
^C^C -- query aborted
ERROR 1317 (70100): Query execution was interrupted
#执行释放-终端2
mysql> unlock tables ;
Query OK, 0 rows affected (0.00 sec)
#依然无法操作-终端2
mysql> 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锁
事务 ( Transactions )
事务是一组具有原子性的 SQL 语句,或者说一个独立单元。可以理解为一个事务对应的是一组完整的业务,这个业务由一条或多条 SQL 语句组成。所谓原子性是指,这一组业务中的 SQL 语句不可分割,所以,要么全部 SQL 语句都执行成功,事务也就执行成功;只要有一条 SQL 语句执行失败,则整个事务都要回滚到事务开始前。
事务日志
记录事务的日志,可以根据此日志实现事务的回滚(undo),重新提交(redo) 等功能。
此处说的事务仅限于 InnoDB 引擎下,在 MySQL 中,MyISAM 引擎是不支持事务的。
事务有 ACID 四个特性
原子性( Atomicity )
原子性又称不可分割性。一个事务中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。
一致性( Consistency )
事务的执行结果,必须是符合预期的,这表示在事务中进行的数据读写,完全符合所有的预设规则,这包含资料的精确度、串联性以及后续数据库可以自发性地完成预定的工作。
隔离性( Isolation )
隔离性又称独立性。MySQL 允许多个事务并发,同时进行写操作,隔离性用于实现事务的并发控制,防止多个事务并发执行时的交叉执行而导致的数据不一致。事务的隔离分为不同的级别,包括读未提交( Read uncommitted ),读提交( read committed ),可重复读( repeatable read ),串行化( Serializable )。
持久性( Durability )
事务执行成功后,其对于数据的修改会永久保存于数据库中。
显示启动事务
BEGIN
BEGIN WORK
START 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)
#update
mysql> update student set age=30 where id=11;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
#insert
mysql> 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 identifier
ROLLBACK [WORK] TO [SAVEPOINT] identifier
RELEASE 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)
#保存回滚点 p1
mysql> 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)
#保存回滚点 p2
mysql> 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)
#保存回滚点 p3
mysql> 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)
#回滚至保存点p2
mysql> 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: 218646
trx_state: RUNNING
trx_started: 2022-12-27 20:07:24
trx_requested_lock_id: NULL
trx_wait_started: NULL
trx_weight: 2
trx_mysql_thread_id: 10 #线程ID
trx_query: NULL
trx_operation_state: NULL
trx_tables_in_use: 0
trx_tables_locked: 1
trx_lock_structs: 2
trx_lock_memory_bytes: 1128
trx_rows_locked: 1
trx_rows_modified: 0
trx_concurrency_tickets: 0
trx_isolation_level: REPEATABLE READ
trx_unique_checks: 1
trx_foreign_key_checks: 1
trx_last_foreign_key_error: NULL
trx_adaptive_hash_latched: 0
trx_adaptive_hash_timeout: 0
trx_is_read_only: 0
trx_autocommit_non_locking: 0
trx_schedule_weight: NULL
#查看所有线程
mysql> show processlist\G
*************************** 1. row ***************************
Id: 5
User: event_scheduler
Host: localhost
db: NULL
Command: Daemon
Time: 33365
State: Waiting on empty queue
Info: NULL
*************************** 2. row ***************************
Id: 10
User: root
Host: localhost
db: testdb
Command: Sleep
Time: 387
State:
Info: NULL
*************************** 3. row ***************************
Id: 16
User: root
Host: localhost
db: performance_schema
Command: Query
Time: 0
State: init
Info: show processlist
3 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)
#查看事务锁的超时时长,默认50s
mysql> 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)
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以前及Mariadb
MariaDB [(none)]> select @@tx_isolation;
+-----------------+
| @@tx_isolation |
+-----------------+
| REPEATABLE-READ |
+-----------------+
1 row in set (0.001 sec)
指定事务隔离级别
#服务器变量指定,可在SESSION和GLOBAL级别进行设置
#MySQL8.0之前及MariaDB
SET 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)
#修改数据,但并没有执行commit
mysql> 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)