postgre 事务隔离

https://github.com/angelfan/DayDayUp/blob/5aed5c7c43132a5bf2f2f1766dab045544c8f870/note/transaction_isolation.md#%E4%BA%8B%E5%8A%A1%E9%9A%94%E7%A6%BB

事务隔离

MVCC的实现方法有两种:

1.写新数据时,把旧数据移到一个单独的地方,如回滚段中,其他人读数据时,从回滚段中把旧的数据读出来;

2.写数据时,旧数据不删除,而是把新数据插入。

PostgreSQL数据库使用第二种方法,而Oracle数据库和MySQL中的innodb引擎使用的是第一种方法。

与racle数据库和MySQL中的innodb引擎相比较,PostgreSQL的MVCC实现方式的优缺点如下。

优点:

1.事务回滚可以立即完成,无论事务进行了多少操作;

2.数据可以进行很多更新,不必像Oracle和MySQL的Innodb引擎那样需要经常保证回滚段不会被用完,也不会像oracle数据库那样经常遇到“ORA-1555”错误的困扰;

缺点:

1.旧版本数据需要清理。PostgreSQL清理旧版本的命令成为Vacuum;

2.旧版本的数据会导致查询更慢一些,因为旧版本的数据存在于数据文件中,查询时需要扫描更多的数据块。

(本段转自《PostgreSQL修炼之道》)

脏读(dirty reads)

一个事务读取了另一个未提交的并行事务写的数据。

不可重复读(non-repeatable reads)

一个事务重新读取前面读取过的数据, 发现该数据已经被另一个已提交的事务修改过。

幻读(phantom read)

一个事务重新执行一个查询,返回一套符合查询条件的行, 发现这些行因为其他最近提交的事务而发生了改变。

BEGIN;
SELECT title FROM books WHERE id = 1;
-- ‘Old‘

gap

SELECT titoe FROm books WHERE id = 1
-- ‘???‘
UPDATE books SET title ‘New‘ WHERE id = 1

读未提交 Read Uncommitted

另一个事务中只要更新的记录, 当前事务就会读取到更新的数据 (脏读)

读已提交 Read Committed

另一个事务必须提交, 当前事务才会读取到最新数据 (不可脏读 但是可重复读)

可重复读 Repeatable Read

即使另一个事务几条, 当前事务也不会读取到新的数据 (不可重复读) 如果另一事务做插入操作, 当前事务是可以取得数量上的变化(select count(*) from table)(可以幻读)

可串行化 Serializable

另一事务做了插入操作, 当前事务不会取得数量上的变化(select count(*) from table)(不可以幻读)

隔离级别 脏读 不可重复读 幻读
读未提交 可能 可能 可能
读已提交 不可能 可能 可能
可重复读 不可能 不可能 可能
可串行化 不可能 不可能 不可能

读已提交隔离级别( BEGIN TRANSACTION ISOLATION LEVEl READ COMMITTED; )

当一个事务运行在这个隔离级别时: SELECT查询(没有FOR UPDATE/SHARE子句)只能看到查询开始之前已提交的数据而无法看到未提交的数据或者在查询执行期间其它事务已提交的数据 (仅读当时数据库快照)。 不过,SELECT看得见其自身所在事务中前面更新执行结果,即使它们尚未提交。 (注意:在同一个事务里两个相邻的SELECT命令可能看到不同的快照,因为其它事务会在第一个SELECT执行期间提交。)

分析

要是同时有两个事务修改同一行数据会怎么样? 这就是事务隔离级别(transaction isolation levels)登场的时候了。 Postgres支持两个基本的模型来让你控制应该怎么处理这样的情况。默认情况下使用读已提交(READ COMMITTED), 等待初始的事务完成后再读取行记录然后执行语句。如果在等待的过程中记录被修改了,它就从头再来一遍。 举一个例子,当你执行一条带有WHERE子句的UPDATE时,WHERE子句会在最初的事务被提交后返回命中的记录结果, 如果这时WHERE子句的条件仍然能得到满足的话,UPDATE才会被执行。 在下面这个例子中,两个事务同时修改同一行记录,最初的UPDATE 语句导致第二个事务的WHERE不会返回任何记录,因此第二个事务根本没有修改到任何记录

Order.transaction do
  Order.where(ship_code: ‘123‘).update_all(ship_code: ‘123456‘)
  sleep 20
end

# 在第一个事务执行完update_all后提交之前执行
Order.transaction do
  Order.where(ship_code: ‘123‘).update_all(ship_code: ‘1234567‘)
end
# ship_code => 123456
# Order.where(ship_code: ‘123‘).update_all(ship_code: ‘1234567‘) 的时候已经找不到 where(ship_code: ‘123‘)

Order.transaction do
  Order.where(ship_code: ‘123‘).first.update(ship_code: ‘1234567‘)
end
# ship_code => 1234567
# 这种情况下会修改记录是因为 它先执行的是Order.where(ship_code: ‘123‘).first
# 因为默认的隔离级别是读已提交, 所以这个时候
# Order.where(ship_code: ‘123‘) 是可以拿到数据的
# 执行的sql语句是
## 先拿到order#id
## UPDATE "orders" SET "ship_code" = 1234567 WHERE "orders"."id" = order#id

# repeatable
Order.transaction do
   old_ship_code =  Order.where(id: 2).pluck(:ship_code)
   sleep 10
   new_ship_code =  Order.where(id: 2).pluck(:ship_code)
   p [old_ship_code, new_ship_code] # 数据是不一样的
end

Order.where(id: 2).update_all(ship_code: 123456123)

可重复读隔离级别

ActiveRecord::Base.isolation_level(:repeatable_read) do
   Order.transaction do
       old_ship_code =  Order.where(id: 2).pluck(:ship_code)
       sleep 10
       new_ship_code =  Order.where(id: 2).pluck(:ship_code)
       p [old_ship_code, new_ship_code] # 数据是一样的
    end
end

可串行化隔离级别

可串行化级别提供最严格的事务隔离。这个级别为所有已提交事务模拟串行的事务执行, 就好像事务将被一个接着一个那样串行(而不是并行)的执行。不过,正如可重复读隔离级别一样, 使用这个级别的应用必须准备在串行化失败的时候重新启动事务。 事实上,该隔离级别和可重复读希望的完全一样,它只是监视这些条件,以所有事务的可能的序列不一致的(一次一个)的方式执行并行的可序列化事务执行的行为。 这种监测不引入任何阻止可重复读出现的行为,但有一些开销的监测,检测条件这可能会导致序列化异常 将触发序列化失败。

分析:如果你需要更好的“两个事务同时修改同一行数据”控制这种行为,你可以把事务隔离级别设置为 可串行化(SERIALIZABLE) 。 在这个策略下,上面的场景会直接失败,因为它遵循这样的规则:“如果我正在修改的行被其他事务修改过的话,就不再尝试”, 同时 Postgres会返回这样的错误信息:由于并发修改导致无法进行串行访问 。 捕获这个错误然后重试就是你的应用需要去做的事情了,或者不重试直接放弃也行,如果那样合理的话。

ActiveRecord::Base.isolation_level(:serializable) do
   Order.transaction do
       old_ship_code =  Order.where(id: 2).update_all(ship_code: 123)
       sleep 10
    end
end

Order.where(id: 2).update_all(ship_code: 123)
# PG::TRSerializationFailure: ERROR:  could not serialize access due to concurrent update
# ActiveRecord::TransactionIsolationConflict
时间: 2024-11-06 01:05:28

postgre 事务隔离的相关文章

MySQL的事务与事务隔离

MySQL中自从引入InnoDB引擎后,在MySQL中就支持事务,事务就是一组原子性的查询语句,也即将多个查询当作一个独立的工作单元,平时通过提交工作单元来完成在事务中的相应的查询或修改,在能支持事务的数据库中必须要满足ACID测试,即事务的四个特性: A:Atomicity,原子性(都执行或者都不执行) C:Consistency,一致性(从一个一致性状态转到另外一个一致性状态) I:Isolaction,隔离性(一个事务的所有修改操作在提交前对其他事务时不可见的) D: Durability

数据库事务的四大特性和事务隔离级别

Reference: [1] http://www.cnblogs.com/fjdingsd/p/5273008.html [2] http://blog.csdn.net/fg2006/article/details/6937413 数据库事务四大特性 如果一个数据库声称支持事务的操作,那么该数据库必须要具备以下四个特性: ⑴ 原子性(Atomicity) 原子性是指事务包含的所有操作要么全部成功,要么全部失败回滚,这和前面两篇博客介绍事务的功能是一样的概念,因此事务的操作如果成功就必须要完全

数据库事务隔离级别

数据库事务的隔离级别有4个,由低到高依次为Read uncommitted.Read committed.Repeatable read.Serializable,这四个级别可以逐个解决脏读.不可重复读.幻读这几类问题. √: 可能出现    ×: 不会出现   脏读 不可重复读 幻读 Read uncommitted √ √ √ Read committed × √ √ Repeatable read × × √ Serializable × × × 注意:我们讨论隔离级别的场景,主要是在多个

<事务隔离性>

Overview 事务的隔离性是指在并发环境中,并发的事务是隔离的.一个事务的执行不能被其他事务干扰. 也即,不同的事务并发操作相同数据时,每个事务都有各自完整的数据空间. 隔离性 在标准SQL规范中,定义了4个事务隔离级别,不同的隔离级别对事务的处理不同,包括: 未授权读取,也称为读未提交(Read Uncommitted).该级别允许脏读取,其隔离级别最低.[关于脏读,幻读,blabla...请往下拉].如果一个事务正在处理某一数据,并对其进行了更新,但同时尚未完成事务,因此还未进行事务提交

MySQL事务隔离级别详解

SQL标准定义了4类隔离级别,包括了一些具体规则,用来限定事务内外的哪些改变是可见的,哪些是不可见的.低级别的隔离级一般支持更高的并发处理,并拥有更低的系统开销.Read Uncommitted(读取未提交内容) 在该隔离级别,所有事务都可以看到其他未提交事务的执行结果.本隔离级别很少用于实际应用,因为它的性能也不比其他级别好多少.读取未提交的数据,也被称之为脏读(Dirty Read).Read Committed(读取提交内容) 这是大多数数据库系统的默认隔离级别(但不是MySQL默认的).

Spring五个事务隔离级别和七个事务传播行为

1. 脏读 :脏读就是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据. 2. 不可重复读 :是指在一个事务内,多次读同一数据.在这个事务还没有结束时,另外一个事务也访问该同一数据.那么,在第一个事务中的两 次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的的数据可能是不一样的.这样就发生了在一个事务内两次读到的数据是不一样的,因此称为是不 可重复读.例如,一个编辑人员两次读取同一文档,但在两次读取之间

Innodb中的事务隔离级别和锁的关系

前言: 我们都知道事务的几种性质,数据库为了维护这些性质,尤其是一致性和隔离性,一般使用加锁这种方式.同时数据库又是个高并发的应用,同一时间会有大量的并发访问,如果加锁过度,会极大的降低并发处理能力.所以对于加锁的处理,可以说就是数据库对于事务处理的精髓所在.这里通过分析MySQL中InnoDB引擎的加锁机制,来抛砖引玉,让读者更好的理解,在事务处理中数据库到底做了什么. #一次封锁or两段锁? 因为有大量的并发访问,为了预防死锁,一般应用中推荐使用一次封锁法,就是在方法的开始阶段,已经预先知道

事务隔离级别与阻塞

本篇文章参考<Microsoft SQL Server企业级平台管理实践>中第9章和第10章 阻塞与死锁未提交读(read uncommitted)指定语句可以读取已由其他事务修改但尚未提交的行.也就是说,允许脏读.未提交读的意思也就是,读的时候不申请共享锁.所以它不会被其他人的排他锁阻塞,它也不会阻塞别人申请排他锁.SELECT * FROM TABLE WITH(NOLOCK)已提交读(read committed)--防脏读指定语句不能读取已由其他事务修改但尚未提交的数据.这样可以避免脏

事务隔离级别,操作

然后说说修改事务隔离级别的方法: 1.全局修改,修改mysql.ini配置文件,在最后加上 1 #可选参数有:READ-UNCOMMITTED, READ-COMMITTED, REPEATABLE-READ, SERIALIZABLE. 2 [mysqld] 3 transaction-isolation = REPEATABLE-READ 这里全局默认是REPEATABLE-READ,其实MySQL本来默认也是这个级别 2.对当前session修改,在登录mysql客户端后,执行命令: 要记