1. 什么是事务隔离级别
数据不一致性是关系型数据库中常见的问题,事务隔离级别主要解决数据库并发访问时出现的问题,提供一种屏蔽并发访问带来的数据不一致性的机制。一般的,关系型数据库提供了4种事务隔离级别:读未提交(read uncommitted)、读已提交(read committed)、可重复读(repeatable read)和串行化(serializable)。
2. MSSQL的四种隔离级别
2.1 读未提交(read uncommitted)
读未提交是最低的事务隔离级别,指允许一个事务读取另一个事务没有提交的数据,也就是说这种情况下允许并发事务互相干扰,可以读取未提交的数据,因此也称之为“脏读”。
BEGIN TRANSACTION
SELECT * FROM users WITH (NOLOCK) WHERE gender = 'M'; -- 读操作事务
...
UPDATE users SET name = 'XXX' WHERE user_id = 10210; -- 写操作事务
COMMIT TRANSACTION
在上面的操作中,读操作事务中的SELECT语句可以读取到未提交的数据,可能因为写操作事务不提交或正在等待提交,在有些情况下,读操作事务可能会读到不一致的数据,即“脏数据”。
2.2 读已提交(read committed)
读提交是提交级别的默认值,读提交的意思是指读操作事务必须等写操作事务进行commit提交操作之后才能读取对应的数据,因此读已提交保证了读操作事务只能读取到已提交数据,避免了读未提交的情况。
BEGIN TRANSACTION A
UPDATE users SET name = 'xxx' WHERE user_id = 10210;
...
COMMIT TRANSACTION
BEGIN TRANSACTION B
SELECT * FROM users WHERE name LIKE '%xx%' AND gender = 'M';
...
COMMIT TRANSACTION
在上述示例中,在事务A执行UPDATE语句并提交后,事务B才能读取到对应的数据,因此事务B不会读到不一致的数据。
2.3 可重复读(repeatable read)
可重复读是比读已提交级别更高的一种事务隔离级别,在未提交数据时不允许数据被其他事务所读取,读取的数据与第一次查询时一致,因此也叫“幻读”。
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ
BEGIN TRANSACTION A
SELECT count(*) FROM table1 WHERE status = 1; -- 假设结果为5
-- 在另一个事务中进行更改操作
BEGIN TRANSACTION B
UPDATE table1 SET status = 0 WHERE id = 6;
COMMIT TRANSACTION
-- 再次查询
SELECT count(*) FROM table1 WHERE status = 1; -- 假设结果仍为5
COMMIT TRANSACTION
在上述示例中,隔离级别为可重复读的事务A首先查询了表table中状态为1的记录数,然后在另一个事务中将表中的某一条记录从状态1修改为状态0,然后在事务A中再次进行相同的查询,即会发现表中的记录数变化了,从而就产生了“幻读”问题。
2.4 串行化(serializable)
串行化隔离级别是最高的一种事务隔离级别,它通过强制事务串行执行,避免了并发事务产生的种种问题,保证所有并发执行的事务都是按照逐一顺序执行的,即串行执行。因此这种隔离级别会对日常操作性能造成很大的影响。
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
BEGIN TRANSACTION A
UPDATE users SET name = 'xxx' WHERE user_id = 10210;
...
COMMIT TRANSACTION
...
BEGIN TRANSACTION B
SELECT * FROM users WHERE gender = 'M';
...
COMMIT TRANSACTION
在上述示例中,事务B必须等待事务A提交之后才可以访问users表,因此这种隔离级别可以避免“幻读”等数据不一致性问题。
3. 总结
MSSQL提供了4种不同的事务隔离级别,不同的隔离级别在保证数据准确性和并发性之间进行了权衡,实际应用场景中需要根据具体情况选择合适的隔离级别,以便实现最优的性能和正确性。