我有几个关于标题主题的问题。首先,假设我们使用 JDBC,并且有 2 个事务 T1 和 T2。在 T1 中,我们对一个特定行执行 select 语句。然后我们对该行执行更新。在事务 T2 中,我们在同一行上执行 select 语句。
以下是问题:
1)事务T1什么时候获取上述行的锁?我认为它发生在 select 语句执行期间?
2)事务T1持有锁多久?它会保留它直到事务提交/回滚,还是在此之前释放锁?
3)隔离级别是否控制使用什么类型的锁?例如:
a) 如果我们在事务 T2 上使用读已提交隔离级别,这是否意味着 T2 将为 select 语句使用共享读锁,以便在 T1 已经更新该行的情况下,T2 将无法访问该行(避免脏读),如果 T1 没有更新行,但 T2 将具有对该行的读取访问权限?
b) 如果我们在事务 T2 上使用读未提交隔离级别,这是否意味着 T2 将对 select 语句使用无锁,因此即使 T1 正在修改数据,它也可以读取数据(允许脏读)。
所以,最让我烦恼的问题是谁有权决定应用什么类型的锁?是事务的隔离级别,还是有其他方式?
4) 如果问题 3 的答案是肯定的(隔离级别控制使用什么锁),那么如果我们在 mysql 数据库上使用 jdbc,并且在共享模式构造中使用 select for update 或 select lock,会发生什么情况?我记得第一个是独占锁,第二个是共享读锁。它将如何反映我们的事务隔离级别?
5)可重复读隔离级别会获取什么样的锁?假设我们的 T2(具有可重复读隔离级别)在同一行上有两个 select 语句,而 T1 与之前相同。首先在T2中执行一个select语句,然后执行T1并提交,然后执行T2第二个select。这种情况有可能吗?如果事务在提交/回滚之前一直持有锁,我认为在 T2 完成之前 T1 将无法获得更新的独占锁?
编辑:还有一个问题:
6)在多版本并发控制系统中,当我们设置可序列化 隔离级别时,尝试更新由另一个事务B更新的某些行的事务A(B在A启动后更新了该行)将被回滚。我想问的是,在乐观锁定场景中,这不是发生同样的事情吗?
提前致谢。
在我的测试中,我发现当使用MariaDB时,在 REPETEABLE_READ 隔离中执行相同的查询不会产生幻读,而它应该产生幻读。
例如:
我的bank_account 表中有两行:
ID | OWNER | MONEY
------------------------
1 | John | 1000
2 | Louis | 2000
Run Code Online (Sandbox Code Playgroud)
预期的流程应如下所示:
THREAD 1 (REPETEABLE_READ) THREAD 2 (READ_UNCOMMITED)
| |
findAll()->[1|John|1000,2|Louis|2000] |
| |
| updateAccount(1, +100)
| createAccount("Charles", 3000)
| flush()
| |
| commitTx()
| |_
|
findAll()->[1|John|1000,2|Louis|2000,
| 3|Charles|3000]
|
|
commitTx()
|_
Run Code Online (Sandbox Code Playgroud)
总而言之,在Thread2.createAccount("Charles", 3000);刷新之后,Thread1 将搜索所有行并得到
ID | OWNER | MONEY
------------------------
1 | John | 1000
2 | Louis …Run Code Online (Sandbox Code Playgroud) 我们使用 Amazon Aurora 作为我们的 Web 应用程序的数据库。我们通过创建新参数组并将其附加到主实例来将主实例的事务级别设置为 READ-COMMITTED 以避免锁定。默认情况下,亚马逊副本通过使用默认实例参数组将事务级别用作可重复读取。当副本在发生故障时升级为主实例时,我们的主实例的事务级别将更改为 (TRANSACTION ISOLATION=REPEATABLE-READ )。当副本(读取器)升级为主实例(写入器)时,我们希望将事务级别设置为 READ-COMMITTED 而不是 REPEATABLE-READ。如果有人提供一种方法来实现这一目标,那就太好了。
database transactions isolation-level amazon-web-services amazon-aurora
我正在阅读PostgreSQL 手册的第 13.2 节,但发现那里的文字描述不够清楚,并且缺少示例。
例如以下两段不清楚谁在学习 PostgreSQL:
带有 ON CONFLICT DO UPDATE 子句的 INSERT 行为类似。在 Read Committed 模式下,建议插入的每一行要么插入要么更新。除非存在不相关的错误,否则保证这两种结果之一。如果冲突源自另一个对 INSERT 尚不可见的事务,则 UPDATE 子句将影响该行,即使该行的常规版本可能对该命令不可见。”
和
可重复读取模式提供了严格的保证,每个事务都可以看到完全稳定的数据库视图。但是,此视图不一定总是与同一级别的并发事务的某些串行(一次一个)执行一致。例如,即使是此级别的只读事务也可能会看到更新的控制记录以显示批次已完成,但看不到逻辑上属于批次的详细记录之一,因为它读取了控制记录的较早版本.
有人可以举例说明这两段的内容吗?
有谁知道我在哪里可以找到 PostgreSQL 隔离级别行为的正式描述?我正在寻找这个,因为它是一个高级主题,我相信正式的描述将有助于阐明它的工作原理,从而有助于避免事务之间的并发错误。
更新:我的另一个疑问是,当一个可序列化的事务可以与其他隔离级别的其他事务同时运行时,它是如何根据数据库机器决定提交或中止它的方式来处理的?数据库是否决定可序列化事务的结果,就像其他事务也以可序列化隔离运行一样?
谢谢
更新 2:到目前为止,我发现的关于隔离级别的实现细节最好的是PostgreSQL Wiki Serializable Page。
我的 PostgreSQL 10.5 数据库中有以下语句,我在repeatable read事务中执行:
delete from task
where task.task_id = (
select task.task_id
from task
order by task.created_at asc
limit 1
for update skip locked
)
returning
task.task_id,
task.created_at
Run Code Online (Sandbox Code Playgroud)
不幸的是,当我运行它时,我有时会得到:
[67] ERROR: could not serialize access due to concurrent update
[67] STATEMENT: delete from task
where task.task_id = (
select task.task_id
from task
order by task.created_at asc
limit $1
for update skip locked
)
returning
task.task_id,
task.created_at
Run Code Online (Sandbox Code Playgroud)
这意味着事务回滚,因为在此期间其他事务修改了记录。(我认为?)
我不太明白这一点。不同的事务如何修改使用 选择for update skip locked并删除的记录?
也许你可以在这里为我阐明一些事情:
数据库 = MySQL 5.7
存储引擎:InnoDB
隔离级别:可重复读
下表:
---------------
| MyTable |
---------------
| PK | Concur |
---------------
| 3 | 2 |
---------------
Run Code Online (Sandbox Code Playgroud)
我此时没有进行任何交易,我选择此记录,例如
SELECT * FROM MyTable WHERE PK = 3
并将结果存储在我的程序中。
我现在开始一个数据库事务。在我的事务开始后,外部进程Concur将 = 3 的记录从 2 增加到 3。PK
我还没有再次从我的交易中的该表中读取内容。
我从交易内部发出以下查询:
UPDATE MyTable SET Concur = 3 WHERE PK = 3 AND Concur = 2
这将成功0 records affected。很明显,它会根据我的交易开始后更改的数据进行评估。仍在交易中我随后查询:
SELECT * FROM MyTable WHERE PK = 3
这将返回记录,其中PK = 3 and Concur …
我读到了有关数据库隔离级别和事务现象的内容。脏读是显而易见的,但我不明白脏写。
所有对脏写的描述都是这样的:
脏写是指一个进程保存的文件数据已被另一个进程在磁盘上更改。最后一个进程将覆盖第一个进程的数据。https://gerardnico.com/data/property/dirty_write
其他一些描述使用示例来演示脏写,但没有说明如何解决该问题。https://esb-dev.github.io/mat/IsoLevel.pdf
这是示例中的脏写:
- 萨尔多从 100 开始
- T2 开始:更新 Acct 设置 Saldo = 200,其中 Acct = 1
- T1 开始:更新 Acct 设置 Saldo = 250,其中 Acct = 1
- T1 提交 => Saldo=250
- T2 提交 => Saldo=200
不知道当隔离级别不允许脏写时会发生什么。
我不知道我应该从事务管理中得到什么。修改后的例子:
我阅读了下面的《初学者\xe2\x80\x99s读写倾斜现象指南》和《初学者\xe2\x80\x99s不可重复读异常指南》来了解什么是读倾斜和不可重复读。
\n读取倾斜:
\n\n不可重复读取:
\n\n但是,我无法区分读取倾斜和不可重复读取,基本上,似乎两者都可以通过REPEATABLE READ隔离SERIALIZABLE级别来防止来防止。
我的问题:
\n读取倾斜和不可重复读取有什么区别\n读取?
\n可以通过或来防止读取倾斜和不可重复读取REPEATABLE READSERIALIZABLE吗?
我在一个利用NHibernate的项目中使用sqlite进行测试用例.一切都很好,除非我尝试创建一个ReadUncommitted事务:
例如Session.BeginTransaction(System.Data.IsolationLevel.ReadUncommitted)
错误消息是:
"的IsolationLevel"
(而已)
调用堆栈如下所示:
at System.Data.SQLite.SQLiteConnection.BeginDbTransaction(IsolationLevel isolationLevel)
at System.Data.Common.DbConnection.System.Data.IDbConnection.BeginTransaction(IsolationLevel isolationLevel)
at NHibernate.Transaction.AdoTransaction.Begin(IsolationLevel isolationLevel)
Run Code Online (Sandbox Code Playgroud)
如果我切换到不同的隔离级别(如序列化或readcommitted),一切都会正确执行.
想法?
NHibernate 2.1.2
SQLite (.NET version) 1.0.65.0
Fluent NHibernate 1.0
Run Code Online (Sandbox Code Playgroud) 我正在开发一个连接到Microsoft SQL Server数据库的Flex/BlazeDS/Spring/JPA/Hibernate Web应用程序.它似乎是过于激进地锁定表格.从我的研究来看,看起来使用快照隔离策略是最好的选择.
我已经这样设定了:
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" lazy-init="true">
<property name="persistenceUnitName" value="OrderManagerPersistenceUnit" />
<property name="dataSource" ref="dataSource"/>
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
</property>
<property name="jpaProperties">
<props>
<prop key="hibernate.jdbc.batch_size">${db.main.hibernate.jdbc.batch_size}</prop>
<prop key="hibernate.hbm2ddl.auto">${db.main.hbm2ddl.auto}</prop>
<prop key="hibernate.search.default.indexBase">${db.main.search.default.indexBase}</prop>
<prop key="hibernate.search.autoregister_listeners">${db.main.search.autoregister_listeners}</prop>
<prop key="hibernate.show_sql">${db.main.show_sql}</prop>
<prop key="hibernate.dialect">${db.main.dialect}</prop>
<prop key="hibernate.connection.isolation">${db.main.isolation}</prop>
<prop key="hibernate.ejb.naming_strategy">com.herffjones.zebra.db.ZebraNamingStrategy</prop>
</props>
</property>
</bean>
Run Code Online (Sandbox Code Playgroud)
但是,我不相信它实际上是在使用hibernate.connection.isolation.看起来我必须在JDBC数据源上设置一些属性.
我想验证它当前是否使用4096作为查询的事务隔离级别.
我可以将哪些包和日志级别添加到我的logback.xml文件中,以清楚地查看特定查询正在使用的隔离级别?
谢谢!
isolation-level ×10
transactions ×8
database ×3
jdbc ×2
mysql ×2
postgresql ×2
dirty-write ×1
hibernate ×1
innodb ×1
jpa ×1
locking ×1
locks ×1
mariadb ×1
nhibernate ×1
rdbms ×1
spring ×1
sql ×1
sqlite ×1