事务的四大特性,通常被称为ACID特性,是数据库管理系统(DBMS)确保事务处理的关键属性。这四大特性分别是:
数据库并发访问可能导致多种问题,主要与多个事务同时操作数据库时的交互有关。并发访问所产生的问题,在有些场景下可能是允许的,但是有些场景下可能是致命的。
这些问题在多用户、多事务并发访问数据库时可能出现,为了处理这些问题,数据库系统提供了不同的隔离级别,例如读未提交、读已提交、可重复读和串行化,以允许开发人员根据应用的需求选择适当的隔离级别。
事务的四个隔离级别是指在数据库管理系统中,用于控制并发事务之间相互影响的不同级别。这些隔离级别分别是:
脏读 | ** 不可重复读** | 幻读 | |
---|---|---|---|
Read Uncommited | √ | √ | √ |
Read Commited | × | √ | √ |
Repeatable Read | × | × | √ |
Serializable | × | × | × |
对应的是Up date操作 | 对应insert操作 |
图片来自: 事务的四种隔离级别详解_事务隔离级别-CSDN博客
脏读:一个事务读取另一个未提交的数据。
**不可重复读:**一个事务范围内两个相同的查询却返回了不同数据。
**幻读:**一个事务范围内两个相同的查询却返回了不同数据。对应的是插入操作。
上锁了都可以直接通过select …from…查询数据,因为普通查询没有任何锁机制。
直接给整个表添加锁:
select * from student where name = 'tom' for update
1
InnoDB在使用过程中只要不通过索引检索数据时,全部是表锁。
开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低
InnoDB中给指定的行添加锁:
select * from student where id > 10 for update
1
InnoDB行锁是通过给索引上的索引项加锁来实现的,InnoDB只有通过索引条件检索数据,InnoDB才使用行级锁
行锁的劣势:开销大;加锁慢;会出现死锁
行锁的优势:锁的粒度小,发生锁冲突的概率低;处理并发的能力强
页级锁是 MySQL 中比较独特的一种锁定级别,在其他数据库管理软件中并不常见。页级锁是对表中的页进行加锁,每个页的大小是固定的,一般为4KB
页级锁的颗粒度介于行级锁与表级锁之间,所以获取锁定所需要的资源开销,以及所能提供的并发处理能力同样也是介于上面二者之间。另外,页级锁和行级锁一样,会发生死锁。
页级锁主要应用于 BDB 存储引擎。
共享锁(S):又称读锁,允许一个事务去读取一行,阻止其他事务获得相同数据集的排它锁,若事务T对数据对象A加上S锁,则事务T可以读A,但不能修改A,其他事务只能对再对A加S锁,而不能加X锁,直到T释放A上的锁,这保证了其他事务可以读A,但在释放A上的S锁之前不能对A做任何修改。
我们有如下测试数据:
共享锁:
START TRANSACTION;
SELECT * FROM test WHERE id = 1 LOCK IN SHARE MODE;
别的线程是可以查询到数据的。
但加排他锁就查不到,因为排他锁与共享锁不能存在同一数据上。
排它锁(X):又称写锁,允许获取排它锁的事物更新数据,阻止其他事务取得相同的数据集共享读锁和排它写锁,若事务T对数据对象A加上X锁,事物T可以读A也可以修改A,其他事务不能再对A加任何锁,直到T 释放A上的锁
现在我们对id=1的数据行排他查询
排他锁:
START TRANSACTION;
SELECT * FROM test WHERE id = 1 FOR UPDATE;
可以看到开了排他锁查询和共享锁查询都会处于阻塞状态,因为id=1的数据已经被加上了排他锁,此处阻塞是等待排他锁释放。
事物B对一行数据使用行锁,当有另一个事物A对这个表使用了表锁,那么这个行锁就会升级为表锁,事务A在申请行锁(写锁)之前,数据库会自动先给事务A申请表的意向排他锁。当事务B去申请表的写锁时就会失败,因为表上有意向排他锁之后事务B申请表的写锁时会被阻塞。
需要强调一下,意向锁是一种不与行级锁冲突的表级锁
所谓死锁,是指多个进程在运行过程中因争夺资源而造成的一种僵局,当进程处于这种僵持状态时,若无外力作用,它们都将无法再向前推进。 因此我们举个例子来描述,如果此时有一个线程A,按照先锁a再获得锁b的的顺序获得锁,而在此同时又有另外一个线程B,按照先锁b再锁a的顺序获得锁。如下图所示:
如下表
CREATE TABLE `test` (
`id` int(20) NOT NULL,
`name` varchar(50) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
表中数据有:
两个事务对一个表进行如下操作:
navicat创建两个查询
每个查询都一步一步执行
查询1:
START TRANSACTION;
select * from test where id = 3 for update;
insert into test(id, name) values(3, "王五");
查询2:
START TRANSACTION;
select * from test where id = 4 for update;
insert into test(id, name) values(4, "赵六");
会出现死锁