InnoDB 事务交错和原子性

问题描述 投票:0回答:1

考虑一个假设的日志系统,它定期将传入的日志分组到

LogIndex
中。一次,只有一个
Active
索引可以接收新日志。 如果在过去 1 小时内没有新日志到达,则有一个周期性的工作从
LogIndex
转换
Active
->
Closed
状态。

DB schema如下:

1. LogIndex (Id, State [Active|Closed])

2. Log (Id, LogIndexId, LogLine, LogTimestamp)

可能会有 2 个数据库事务并行运行的场景:

Transaction 1 : To insert a new log line

Query1. Select Id from LogIndex where State='Active' LOCK IN SHARE MODE;
Query2. Insert into Log values(Id, LongIndex.Id, LogLine, LogTimestamp);

事务 2:定期检查器以确定是否需要关闭索引

Query1. Select Id from LogIndex where State='Active' LOCK IN SHARE MODE;
Query2. Select LogTimestamp from Log where LogIndexId='<someLogId>' order by LogTimestamp desc Limit 1;

<If the LogTimestamp is more than 1 hour old, then>

Query3. Update LogIndex set State='Closed' where Id='<someLogId>'

我有几个问题从非常基本的开始:

Q1:

Select
LOCK IN SHARE MODE
运行时,事务中的查询是否仍然以交错方式执行?

Q2: 假设交错执行发生: 如果我们想避免使用像

Select...LOCK FOR UPDATE
这样的独占锁,那么我们如何序列化这些交易以避免由于以下顺序导致的不一致状态:

t1:

Transaction1 - Query1
-> 选择
Active
状态下的 LogIndex。一次只有 1 个 LogIndex 处于
Active
状态。

t2:

Transaction2 - Query1
-> 选择
Active
状态下的 LogIndex

t3:

Transaction2 - Query2
-> 列出 LogIndex 中的所有日志

t4:

Transaction2 - Query3
-> 将 LogIndex 更新为
Closed

t5:

Transaction1 - Query2
-> Inserts Log record for LogIndex.Id just set as
Closed

或者另一种可能的情况:

t1:

Transaction1 - Query1
-> 选择
Active
状态下的 LogIndex。一次只有 1 个 LogIndex 处于
Active
状态。

t2:

Transaction2 - Query1
-> 选择
Active
状态下的 LogIndex

t3:

Transaction2 - Query2
-> 列出最后一个日志超过 1 小时的 LogIndex 中的所有日志

t4:

Transaction1 - Query1
-> 为 LogIndex.Id 插入日志记录,它处于
Active
状态

t5:

Transaction2 - Query3
-> 将 LogIndex 更新为
Closed
,即使在 t4 时插入了新日志。

有问题的数据库是 MySQL InnoDB,默认隔离级别设置为可重复读取。 谢谢!

mysql transactions locking innodb
1个回答
0
投票
  1. 写一份完成工作的陈述清单。忽略多个连接等
  2. 添加
    BEGIN
    FOR UPDATEs
    COMMIT

这两个步骤就足够了。

更多讨论。

代码可以改成一次做多行吗?如果是这样,那将提供更大的可扩展性,并且发生冲突的可能性要小得多。

一次“交易”需要多长时间?希望不到一秒钟。

通常的模式是

BEGIN;
SELECT ... FOR UPDATE;   -- any rows that you don't way changed
...
UPDATE / INSERT / DELETE -- whatever is needed, in whatever tables
COMMIT;

Of the

SELECT(s)
fetch more than one row (say 10), have an
ORDER BY
so they always in the same order. (这避免了某种类型的死锁。)

我不明白“选择处于活动状态的 LogIndex”——那是一行吗?所有“活动”行?一个限制?

注意:如果“关闭”任务足够快,您可能只需要其中一个。

另见“乒乓”技术,其中数据被插入一个表而另一个表被清空。然后他们交换:http://mysql.rjweb.org/doc.php/staging_table

暗示使用默认的隔离级别。

“列出最后一个日志超过 1 小时的 LogIndex 中的所有日志”——可以有多少个?一打 = 好的。一百万 = 在交易中可能太慢了。

Update LogIndex set State='Closed' WHERE id = ...
您可以使用
SELECT
而不是
JOIN
来折叠前面的
WHERE

© www.soinside.com 2019 - 2024. All rights reserved.