将查询同步到 MariaDB 数据库
Synchronization of queries to a MariaDB database
出于一些高可用性的考虑,我设计了一个系统,其中多个进程将 communicate/synchronize 通过数据库(最有可能是 MariaDB,但我愿意研究 PostgreSQL 和 MySQL 选项)。
已确定的要求之一是一个进程必须从数据库中获取一份工作,而不能让另一个进程同时获取相同的工作。
具体来说,这是我想到的竞争条件:
- 进程 A 启动 SQL 事务并运行
SELECT * FROM requests WHERE ReservedTS IS NULL ORDER BY CreatedTS LIMIT 100
。这里 ReservedTS
和 CreatedTS
是 DATETIME
列,存储该工作由工作提交进程创建并相应地由工作执行进程保留的时间。
- 进程 B 启动一个事务,运行相同的查询并获得相同的结果集。
- 进程 A 运行
UPDATE requests WHERE id IN (<list of IDs selected above>) AND ReservedTS IS NULL SET ReservedTS=NOW()
- 进程 B 运行相同的查询,但是,因为它的事务有它自己的数据快照,所以
ReservedTS
对进程 B 来说不为空,所以项目被保留了两次。
- 进程 A 提交事务。
- 进程 B 提交事务,覆盖进程 A 的值。
能否请您帮助解决上述数据竞争问题?
您可以使用排他锁轻松做到这一点:
为了简化测试table:
CREATE TABLE t1 (id int not null auto_increment primary key, reserved int);
INSERT INTO t1 VALUES (0,0), (1,0);
进程 A:
BEGIN
SELECT id, reserved from t1 where id=2 and reserved=0 FOR UPDATE;
UPDATE t1 SET reserved=1 WHERE id=2 and reserved=0;
COMMIT
如果进程 B 尝试在进程 A 完成事务之前更新同一条目,则它必须等到锁被释放(或发生超时):
update t1 set reserved=1 where id=2 and reserved=0;
Query OK, 0 rows affected (12.04 sec)
Rows matched: 0 Changed: 0 Warnings: 0
如您所见,进程 B 没有更新任何内容。
出于一些高可用性的考虑,我设计了一个系统,其中多个进程将 communicate/synchronize 通过数据库(最有可能是 MariaDB,但我愿意研究 PostgreSQL 和 MySQL 选项)。
已确定的要求之一是一个进程必须从数据库中获取一份工作,而不能让另一个进程同时获取相同的工作。
具体来说,这是我想到的竞争条件:
- 进程 A 启动 SQL 事务并运行
SELECT * FROM requests WHERE ReservedTS IS NULL ORDER BY CreatedTS LIMIT 100
。这里ReservedTS
和CreatedTS
是DATETIME
列,存储该工作由工作提交进程创建并相应地由工作执行进程保留的时间。 - 进程 B 启动一个事务,运行相同的查询并获得相同的结果集。
- 进程 A 运行
UPDATE requests WHERE id IN (<list of IDs selected above>) AND ReservedTS IS NULL SET ReservedTS=NOW()
- 进程 B 运行相同的查询,但是,因为它的事务有它自己的数据快照,所以
ReservedTS
对进程 B 来说不为空,所以项目被保留了两次。 - 进程 A 提交事务。
- 进程 B 提交事务,覆盖进程 A 的值。
能否请您帮助解决上述数据竞争问题?
您可以使用排他锁轻松做到这一点:
为了简化测试table:
CREATE TABLE t1 (id int not null auto_increment primary key, reserved int);
INSERT INTO t1 VALUES (0,0), (1,0);
进程 A:
BEGIN
SELECT id, reserved from t1 where id=2 and reserved=0 FOR UPDATE;
UPDATE t1 SET reserved=1 WHERE id=2 and reserved=0;
COMMIT
如果进程 B 尝试在进程 A 完成事务之前更新同一条目,则它必须等到锁被释放(或发生超时):
update t1 set reserved=1 where id=2 and reserved=0;
Query OK, 0 rows affected (12.04 sec)
Rows matched: 0 Changed: 0 Warnings: 0
如您所见,进程 B 没有更新任何内容。