chew-hjc
1,
SERIALIZABLE
隔离级别介绍
SERIALIZABLE
事务隔离级别,中文叫串行化,他是
postgresql
事务级别中最高一级,
postgresql
默认
事务隔离级别是
read committed,
不过这个可以
通过配置
中的
default_tra
nsaction_isolation
参数来设置默认事务隔离级别,采用
SERIALIZABLE
事务隔离
级别可以防止
脏读(
dirty read
)
,非重
复读(
nonrepeatable
read
)
,和幻像(
phantom
read
)
,
一个事务如果进入了
p>
set traansaction isolation level serializ
able;
就会独占这个事务需要的所有
资源,其他任何修改同
样资源的请求都会被推出,不过讲述
postgresql
事务
隔离级别
SERIALIZABLE
的特性时是必需要结合
p>
postgresql
的另一个实现机制
M
VCC(
多版本控制
Multiversion Concurrency Control,
MVCC)
一起来说明。
SQL
标准定义了四种隔离级别。最严格的是可序列化,在标准中用了一
整段来定义它,其中
说到一组可序列化事务的任意并发执行被保证效果和以某种顺序一个
一个执行这些事务一
样。
其他三种级别使用并发事务之间交互产
生的现象来定义,
每一个级别中都要求必须不出
现一种现象。<
/p>
注意由于可序列化的定义,
在该级别上这些现象都不可能发生
p>
(这并不令人惊
讶
--
如果事务的效果与每个时刻只运行一个的相同,你怎么可能看见由于交互产生的现
象
?)。
在各个级别上被禁止出现的现象是:
脏读(
dirty read
)
:当一个事务读取另一个事务尚未提交的修改时数据就叫脏读
非重复读(
nonrepeatable
read
)
:同一查询在同一事务中多次进行,由于其他提
交事务所做
的修改或删除,如果每次返回不同的结果集,就叫非重复读。
幻像
(
phantom read
p>
)
:
同一查询在同一事务中多次进行,
p>
由于其他提交事务所做的插入操作,
每次返回不同的结果集,就叫幻
像读。
表
13-1.
标准
SQL
事务隔离级别
隔离级别
脏读
不可重复读
幻读
读未提交
可能
可能
读已提交
不可能
可能
可重复读
不可能
不可能
可序列化
不可能
不可能
可能
可能
可能
不可能
PostgreSQL
官方文档介绍:
/docs/9.5/#XACT-SERIALIZABLE
13.2.1.
读已提交隔离级别
读已提交
是
PostgreSQL
中的默认隔离级别。<
/p>
当一个事务运行使用这个隔离级别时,
一个
查询(没有
FOR
UPDATE/SHARE
子句)只能看到查询开始之前已经被提交的数据,
而无法看
到未提交的数据或在查询执
行期间其它事务提交的数据。实际上,
SELECT
查询看到的
是一
个在查询开始运行的瞬间该数据库的一个快照
。不过
SELECT
可以看见在它自身事务中之前
执
行的更新的效果,
即使它们还没有被提交。
还要注意的是,
p>
即使在同一个事务里两个相邻
的
SELEC
T
命令可能看到不同的数据,
因为其
它事务可能会在第一个
SELECT
开始和第二个
SELECT
开始之间提交
。
13.2.2.
可重复读隔离级别
可重复读
隔离级别只看到在事务开始之前被提交的数据;
它从来看不到未提交的数
据或者并
行事务在本事务执行期间提交的修改(不过,查询能够看见在它的事务中之前执
行的更新,
即使它们还没有被提交)
。
这是比
SQL
标准对此隔离级别所要求的更强的保证,
并且阻止
表
13-1
中描述的所有现象。如上面所提到的,这是标准特别允许的,标准只描述了每种隔离
p>
级别必须提供的
最小
保护。
这个级别与读已提交不同之处在于,
一个可重复读
事务中的查询可以看见在
事务
开始时的一
个快照,而不是事务中当前查询开始时的快照。因此,在一个
单一
事务中的后续
SELECT
命
令看
到的是相同的数据,即它们看不到其他事务在本事务启动后提交的修改。
使用这个级别的应用必须准备好由于序列化失败而重试事务。
UPDATE
、
DELETE
、
SELECT FOR
UPDATE
和
SELECT FOR SHARE
命令在搜索目标行时的行为和
SELECT
一样:
它们将只找到在事务开始时已经被提交的行。
不过,在被找到时,这样的
目标行可能已经被其它并发事务更新(或删除或锁住
)。在这种情况下,
可重复读事务将
等待第一个更新事务提交或者回滚(如果它还在进行中)。
如
果第一个更新事务回滚,那
么它的作用将被忽略并且可重复读事务可以继续更新最初发现
的行。
但是如果第一个更新
事务提交
(并且实际更新或删除该行,而不是只锁住它)
,则可重复读事务将回滚并带有如
下消息
ERROR: could
not serialize access due to concurrent update
因为一个可重复读事务无法修改或者锁住被其他在可重复读事务开始之后的事务改变的行。
当一个应用接收到这个错误消息,
它应该
中断当前事务并且从开头重试整个事务。
在第二次
执行中,
p>
该事务将见到作为其初始数据库视图一部分的之前提交的改变,
这样
在使用行的新
版本作为新事务更新的起点时就不会有逻辑冲突。
注意只有更新事务可能需要被重试;只读事务将永远不会有序列化冲突。
可重复读模式提供了一种严格的保证,
在其中每一
个事务看到数据库的一个完全稳定的视图。
不过,
这个视图并不
需要总是和同一级别上并发事务的某些序列化
(一次一个)
执行
保持一
致。
例如,
即使这个级别上的一
个只读事务可能看到一个控制记录被更新,
这显示一个批处
理已
经被完成但是
不能
看见作为该批处理的逻辑组成部分的一个细节
记录,
因为它读取空值
记录的一个较早的版本。
如果不小心地使用显式锁来阻塞冲突事务,
尝试用运行在这个隔离
级别的事务来强制业务规则不太可能正确地工作。
2,
实例演示:
在数据库默认的读已提交隔离级别下:
会发生死锁问题:
如下图,两个用户
session<
/p>
同时更新同一条记录时,
第二个
sess
ion
需要等前一个更新提交或
回退之后,才能继续完成,如果
前一个用户
session
一直没有提交或回退,则会阻塞所有
其
他的
session
,直到发生超时
(若有设定
lock_timeout
的话)
< br>。通常情况下生产系统的异常
session
会阻塞部分
业务,需要手工
kill
进程的方式处理。
会发生更新丢失问题:
两个用户
session
同时更新同一条记录,第一个用户的更新,会被第二个用户覆盖,因为第
二
个用户无法得知当前记录已发生变化。如下图:
注:
更新丢失会影响的业务数据准确性,
如果更新的是银行卡账户余额,
p>
则必须避免更新丢
失问题。
实例演示:
在数据库的可重复读隔离级别下:
可解决上例中的并行更新时更新丢失问题:
用户
A
和<
/p>
B
的
session
同为可重复读级别,
A
、
B
用户先后(或同时)对同一条记录更新,
后更新的
session
会等待前一个
SQL
提交或回退,当回退时,第二个
session
会继续执行,
当提交时,第二个
session
会报
错“
could not serialize access due to
concurrent update
”
,这种
保护机制会避免更新丢失的情况。
注:此时没有锁等
待时间,第二个
session
在即时检测到记录被更新时立刻
返回报错信息,
即使
A
、
B
更新的是不同字段,只要是同一记录也会出错。
3,
XXX
生产系统问题重现:
以查明的
案例为例,其他可能发生此错误的场景与此类似,具有代表性:
Mobile
App
接口调用时,同时调用
user_info &
cart_detail
接口:
由于
在
us
er_info
中
,
(
由于方法
current_user._get_default_image
()
的调用
)
会执行
< br>SQL
:
UPDATE
(738)
在
cart_detai
l
中,会执行:
[
'r'
].write(cr,
SUPERUSER_ID, ,
{
'last_website_so_id'
:
sale_order_id})
两者在并行处理时,会针对同一条
res_partner
记录
做更新:
返回异常结果:
Odoo
Server
日志错误信息:
2016-11-10 17:06:18,447 73932 INFO
demo_20161020 _db:
bad query: UPDATE
'UTC') WHERE id IN
(738)
2016-11-10
17:06:18,447 73932 ERROR demo_20161020
_rt:
could not serialize
access due to concurrent update
2016-11-10 17:06:18,447 73932 ERROR
demo_20161020
_rt:
40001
4,
解决方案一:接口重复调用方式代码实现:
< br>根据
“
使用这个级别的应用必须准备好由于序列化失败而
重试事务
”
的官方提示,
新增重试
p>
事务功能。
装
饰器
repeat_execute
:
参数:
count =
执行次数
说明:
当被装饰的方法出现串行化更新失败时,
对其执行指定次数的重复调用,
当其中某一
次调用成功执行时,终止重复执行并返回结果。
Class 40
—
Transaction
Rollback
40000
transaction_rollback
40002
transaction_integrity_constraint_violation
40001 serialization_failure
40003 statement_completion_unknown
40P01 deadlock_detected
def
repeat_execu
te(count=
1
):
def
decorator(func):
@
(func)
def
wrapper(*args, **kwargs):
v_first_call=func(*args,**kwargs)
chew-hjc
chew-hjc
chew-hjc
chew-hjc
chew-hjc
chew-hjc
chew-hjc
chew-hjc
-
上一篇:广州开发区外资企业目录
下一篇:java英文参考文献