Oracle 知识篇+事务隔离控制

说明:本文为面向Oracle 事务隔离控制初学者的指导手册
标签:Oracle事务控制、事务隔离、Isolation Level、Transaction Isolation Levels
对比:本文将Oracle和MySQL进行了对比,方便理解
注意:文中删去了不需要的多余部分,让初学者一目了然一学就会
温馨提示:如果您发现本文哪里写的有问题或者有更好的写法请留言或私信我进行修改优化


★ 结论速览
※ Oracle数据库提供了“Read committed”(默认)和“Serializable”的2种国际隔离级别标准。此外Oracle还提供了非国标的“read-only”模式
※ MySQL数据库支持4种国际标准,不过默认隔离级别为“Repeatable read”。生产环境一般推荐改为“Read committed”并开启row格式的binlog

★ 相关文章
Oracle官方文档
为啥mysql的默认隔离级别是RR?

★ 知识点
※ ISO是指国际标准化组织(International Organization for Standards)
※ ANSI是指美国国家标准学会(American National Standards Institute)

★ ANSI/ISO事务隔离级别
ANSI和ISO / IEC已采用的SQL标准定义了事务隔离的四个级别。这些级别对事务处理吞吐量的影响程度不同。
这些隔离级别是根据在同时执行的事务之间必须防止的现象定义的。可预防的现象有:

※ 脏读(Dirty reads)
	事务读取另一个尚未提交的事务已写入的数据。
※ 不可重复读/模糊读(Nonrepeatable (fuzzy) reads)
	事务重新读取它先前已读取的数据,并发现另一个已提交的事务已修改或删除了该数据。例如,用户查询一行,然后再查询同一行,只是发现数据已更改。
※ 幻读(Phantom reads)
	事务重新运行查询,返回满足搜索条件的一组行,并发现另一个提交的事务已插入满足该条件的其他行。
	例如,某笔交易查询雇员人数。五分钟后,它执行相同的查询,但是现在这个数字增加了一个,因为另一个用户插入了新员工的记录。与以前相比,满足查询条件的数据更多,但是与模糊读取不同,以前读取的数据保持不变。

根据允许以特定隔离级别运行的事务遇到的现象,SQL标准定义了四个隔离级别。

按隔离级别划分的可预防的读取现象

隔离度 脏读 不可重复读 幻读
Read uncommitted

可能的

可能的

可能的

Read committed

不可能

可能的

可能的

Repeatable read

不可能

不可能

可能的

Serializable

不可能

不可能

不可能


★ 事务隔离控制(Oracle)
※ Oracle的3种事务隔离级别

  1. Read Committed Isolation Level
  2. Serializable Isolation Level
  3. Read-Only Isolation Level

※ 用法案例

1. Read Committed Isolation Level

在默认的已提交读隔离级别中,事务执行的每个查询仅看到在查询开始之前提交的数据(而不是事务)。这种隔离级别适用于很少有事务可能冲突的数据库环境。

下表显示了READ COMMITTED事务中的冲突写入和丢失更新

Session 1 Session 2 Explanation
SQL> SELECT last_name, salary
FROM employees WHERE last_name
IN ('Banda','Greene','Hintz');
 
LAST_NAME         SALARY
------------- ----------
Banda               6200
Greene              9500
 

Session 1 queries the salaries for Banda, Greene, and Hintz. No employee named Hintz is found.

SQL> UPDATE employees SET salary
= 7000 WHERE last_name = 'Banda';
 

Session 1 begins a transaction by updating the Banda salary. The default isolation level for transaction 1 is READ COMMITTED.

 
SQL> SET TRANSACTION ISOLATION
LEVEL READ COMMITTED;

Session 2 begins transaction 2 and sets the isolation level explicitly to READ COMMITTED.

 
SQL> SELECT last_name, salary
FROM employees WHERE last_name IN
('Banda','Greene','Hintz');
 
LAST_NAME         SALARY
------------- ----------
Banda               6200
Greene              9500

Transaction 2 queries the salaries for Banda, Greene, and Hintz. Oracle Database uses read consistency to show the salary for Banda before the uncommitted update made by transaction 1.

 
SQL> UPDATE employees SET salary =
9900 WHERE last_name = 'Greene';

Transaction 2 updates the salary for Greene successfully because transaction 1 locked only the Banda row

SQL> INSERT INTO employees
(employee_id, last_name, email,
hire_date, job_id) VALUES (210,
'Hintz', 'JHINTZ', SYSDATE,
'SH_CLERK');
 

Transaction 1 inserts a row for employee Hintz, but does not commit.

 
SQL> SELECT last_name, salary
FROM employees WHERE last_name IN 
('Banda','Greene','Hintz');
 
LAST_NAME         SALARY
------------- ----------
Banda               6200
Greene              9900

Transaction 2 queries the salaries for employees Banda, Greene, and Hintz.

Transaction 2 sees its own update to the salary for Greene. Transaction 2 does not see the uncommitted update to the salary for Banda or the insertion for Hintz made by transaction 1.

 
SQL> UPDATE employees SET salary =
6300 WHERE last_name = 'Banda';

-- prompt does not return 

Transaction 2 attempts to update the row for Banda, which is currently locked by transaction 1, creating a conflicting write. Transaction 2 waits until transaction 1 ends.

SQL> COMMIT;
 

Transaction 1 commits its work, ending the transaction.

 
1 row updated.
 
SQL>

The lock on the Banda row is now released, so transaction 2 proceeds with its update to the salary for Banda.

 
SQL> SELECT last_name, salary
FROM employees WHERE last_name IN
('Banda','Greene','Hintz');
 
LAST_NAME         SALARY
------------- ----------
Banda               6300
Greene              9900
Hintz

Transaction 2 queries the salaries for employees Banda, Greene, and Hintz. The Hintz insert committed by transaction 1 is now visible to transaction 2. Transaction 2 sees its own update to the Banda salary.

 
COMMIT;

Transaction 2 commits its work, ending the transaction.

SQL> SELECT last_name, salary
FROM employees WHERE last_name
IN ('Banda','Greene','Hintz');
 
LAST_NAME         SALARY
------------- ----------
Banda               6300
Greene              9900
Hintz
 

Session 1 queries the rows for Banda, Greene, and Hintz. The salary for Banda is 6300, which is the update made by transaction 2. The update of Banda's salary to 7000 made by transaction 1 is now "lost."

序列化读隔离级别中,事务仅看到事务开始时提交的更改(而不是查询)以及事务本身所做的更改。可序列化的事务在使它看起来好像没有其他用户在修改数据库中数据的环境中运行。

2. Serializable Isolation Level

下表显示了可序列化事务如何与其他事务交互。如果可序列化事务在开始可序列化事务之后没有尝试更改另一事务提交的行,那么可以避免序列化访问问题

Session 1 Session 2 Explanation
SQL> SELECT last_name, salary
FROM employees WHERE last_name 
IN ('Banda','Greene','Hintz');
 
LAST_NAME         SALARY
------------- ----------
Banda               6200
Greene              9500
 

Session 1 queries the salaries for Banda, Greene, and Hintz. No employee named Hintz is found.

SQL> UPDATE employees SET salary
= 7000 WHERE last_name = 'Banda';
 

Session 1 begins transaction 1 by updating the Banda salary. The default isolation level for is READ COMMITTED.

 
SQL> SET TRANSACTION ISOLATION
LEVEL SERIALIZABLE;

Session 2 begins transaction 2 and sets it to the SERIALIZABLE isolation level.

 
SQL> SELECT last_name, salary
FROM employees WHERE last_name 
IN ('Banda','Greene','Hintz');
 
LAST_NAME         SALARY
------------- ----------
Banda               6200
Greene              9500

Transaction 2 queries the salaries for Banda, Greene, and Hintz. Oracle Database uses read consistency to show the salary for Banda before the uncommitted update made by transaction 1.

 
SQL> UPDATE employees SET salary =
9900 WHERE last_name = 'Greene';

Transaction 2 updates the Greene salary successfully because only the Banda row is locked.

SQL> INSERT INTO employees
(employee_id, last_name, email,
hire_date, job_id) VALUES (210,
'Hintz', 'JHINTZ', SYSDATE,
'SH_CLERK');
 

Transaction 1 inserts a row for employee Hintz.

SQL> COMMIT;
 

Transaction 1 commits its work, ending the transaction.

SQL> SELECT last_name, salary
FROM employees WHERE last_name 
IN ('Banda','Greene','Hintz');
 
LAST_NAME         SALARY
------------- ----------
Banda               7000
Greene              9500
Hintz
SQL> SELECT last_name, salary
FROM employees WHERE last_name IN
('Banda','Greene','Hintz');
 
LAST_NAME         SALARY
------------- ----------
Banda               6200
Greene              9900

Session 1 queries the salaries for employees Banda, Greene, and Hintz and sees changes committed by transaction 1. Session 1 does not see the uncommitted Greene update made by transaction 2.

Transaction 2 queries the salaries for employees Banda, Greene, and Hintz. Oracle Database read consistency ensures that the Hintz insert and Banda update committed by transaction 1 are not visible to transaction 2. Transaction 2 sees its own update to the Banda salary.

 
COMMIT;

Transaction 2 commits its work, ending the transaction.

SQL> SELECT last_name, salary
FROM employees WHERE last_name
IN ('Banda','Greene','Hintz');
 
LAST_NAME         SALARY
------------- ----------
Banda               7000
Greene              9900
Hintz
SQL> SELECT last_name, salary 
FROM employees WHERE last_name 
IN ('Banda','Greene','Hintz');
 
LAST_NAME         SALARY
------------- ----------
Banda               7000
Greene              9900
Hintz

Both sessions query the salaries for Banda, Greene, and Hintz. Each session sees all committed changes made by transaction 1 and transaction 2.

SQL> UPDATE employees SET salary
= 7100 WHERE last_name = 'Hintz';
 

Session 1 begins transaction 3 by updating the Hintz salary. The default isolation level for transaction 3 is READ COMMITTED.

 
SQL> SET TRANSACTION ISOLATION
LEVEL SERIALIZABLE;

Session 2 begins transaction 4 and sets it to the SERIALIZABLE isolation level.

 
SQL> UPDATE employees SET salary =
7200 WHERE last_name = 'Hintz';

-- prompt does not return

Transaction 4 attempts to update the salary for Hintz, but is blocked because transaction 3 locked the Hintz row. Transaction 4 queues behind transaction 3.

SQL> COMMIT;
 

Transaction 3 commits its update of the Hintz salary, ending the transaction.

 
UPDATE employees SET salary = 7200
WHERE last_name = 'Hintz'
*
ERROR at line 1:
ORA-08177: can't serialize access
for this transaction

The commit that ends transaction 3 causes the Hintz update in transaction 4 to fail with the ORA-08177 error. The problem error occurs because transaction 3 committed the Hintz update after transaction 4 began.

 
SQL> ROLLBACK;

Session 2 rolls back transaction 4, which ends the transaction.

 
SQL> SET TRANSACTION ISOLATION
LEVEL SERIALIZABLE;

Session 2 begins transaction 5 and sets it to the SERIALIZABLE isolation level.

 
SQL> SELECT last_name, salary 
FROM employees WHERE last_name 
IN ('Banda','Greene','Hintz');
 
LAST_NAME         SALARY
------------- ----------
Banda               7100
Greene              9500
Hintz               7100

Transaction 5 queries the salaries for Banda, Greene, and Hintz. The Hintz salary update committed by transaction 3 is visible.

 
SQL> UPDATE employees SET salary =
7200 WHERE last_name = 'Hintz';

1 row updated.

Transaction 5 updates the Hintz salary to a different value. Because the Hintz update made by transaction 3 committed before the start of transaction 5, the serialized access problem is avoided.

Note: If a different transaction updated and committed the Hintz row after transaction transaction 5 began, then the serialized access problem would occur again.

 
SQL> COMMIT;

Session 2 commits the update without any problems, ending the transaction.

只读隔离级别类似于序列化隔离级别,但只读交易不会允许数据在交易中,除非用户进行修改SYS。因此,只读事务不易受该ORA-08177错误的影响。只读事务对于生成报告(其中内容必须与事务开始时间保持一致)很有用。3. Read-Only Isolation Level


★ 事务隔离控制(MySQL)(详情参考引用原文)

在Oracle,SqlServer中都是选择读已提交(Read Commited)作为默认的隔离级别,为什么Mysql不选择读已提交(Read Commited)作为默认隔离级别,而选择可重复读(Repeatable Read)作为默认的隔离级别呢?

这个是有历史原因的,当然要从我们的主从复制开始讲起了!
主从复制,是基于什么复制的?
是基于binlog复制的!这里不想去搬binlog的概念了,就简单理解为binlog是一个记录数据库更改的文件吧~
binlog有几种格式?
OK,三种,分别是

  • statement:记录的是修改SQL语句
  • row:记录的是每行实际数据的变更
  • mixed:statement和row模式的混合

那Mysql在5.0这个版本以前,binlog只支持STATEMENT这种格式!而这种格式在读已提交(Read Commited)这个隔离级别下主从复制是有bug的,因此Mysql将可重复读(Repeatable Read)作为默认的隔离级别!

两个疑问

在RC级别下,不可重复读问题需要解决么?
不用解决,这个问题是可以接受的!毕竟你数据都已经提交了,读出来本身就没有太大问题!Oracle的默认隔离级别就是RC,你们改过Oracle的默认隔离级别么?

在RC级别下,主从复制用什么binlog格式?
OK,在该隔离级别下,用的binlog为row格式,是基于行的复制!Innodb的创始人也是建议binlog使用该格式!


※ 如果您觉得文章写的还不错, 别忘了在文末给作者点个赞哦 ~

over

猜你喜欢

转载自blog.csdn.net/zzt_2009/article/details/114232995