【问题标题】:ACID transactions across multiple technologies跨多种技术的 ACID 事务
【发布时间】:2025-12-27 11:50:06
【问题描述】:

我有一个使用本地数据库和远程数据库进行同步的应用程序。本地数据库使用 SQLite,远程数据库我使用 postgres。我需要将数据从一个数据库移动到另一个数据库,并避免重复信息。

大概是我现在做的:

BEGIN;                                       //remote database  (start transaction)
SELECT * FROM local.queued TOP 1;            //local database   (select first queued element)
INSERT INTO remote.queued VALUES ( element ) //remote database  (insert first queued element on remote database)
BEGIN;                                       //local database   (start transaction)
DELETE * FROM local.queued LIMIT 1;          //local database   (delete first queued element on local database)
END;                                         //local database   (finalize transaction local database)
END;                                         //remote database  (finalize transaction remote database)

这在大多数情况下都比较有效,但顺便说一下,在对程序进行硬重置后,我注意到数据记录被复制了。我相信这与交易完成有关。因为我使用了两种不同的技术,所以不可能使用 WAL 归档创建单个原子提交。

关于如何改进这个概念以避免重复条目的任何想法。

【问题讨论】:

  • 你遇到过saga模式吗?
  • PostgreSQL 有 XA 驱动程序,但据我所知 SQLite 没有。两阶段提交在这里可能不是一个选项。

标签: sql database postgresql sqlite


【解决方案1】:

我认为让您的 DML 操作幂等 是有意义的 - 也就是说,如果您多次调用它们,它们具有相同的整体效果。例如,如果数据存在,我们可以使 INSERT 成为空操作:

INSERT INTO x(id, name)
SELECT nu.id, nu.name
FROM
  (SELECT 1 as id, 'a' as name) as nu
  LEFT JOIN x ON nu.id = x.id
WHERE
  x.id IS NULL

你可以多次运行它,它只会插入一条记录

https://www.db-fiddle.com/f/nbHmy3PVDQ3RrGMqLni1su/0

您需要决定如果记录处于更改状态时该怎么做 - 例如,您是想不理会它,还是将其重置为传入的值 - 另一个问题

【讨论】:

    【解决方案2】:

    做到这一点的规范方法是使用两阶段提交协议分布式事务

    很遗憾,SQLite 似乎不支持它,但由于 PostgreSQL 支持,如果只涉及两个数据库,您仍然可以使用它:

    BEGIN;  -- on PostgreSQL
    
                  BEGIN;  -- on SQLite
    
       /*
        * Do work on both databases.
        * On error, ROLLBACK both transactions.
        */
    
    PREPARE TRANSACTION 'somename';  -- PostgreSQL
    
                  COMMIT;  -- SQLite
    
    COMMIT PREPARED 'somename';  -- PostgreSQL
    

    现在,如果在 SQLite COMMIT 期间发生错误,您可以在 PostgreSQL 上运行 ROLLBACK PREPARED 'sonename'。这个想法是,在提交期间可能失败的所有事情都在PREPARE TRANSACTION 期间完成,并且事务的状态保持不变,以便它保持打开状态,但仍然会在服务器重新启动后继续存在。

    这是安全的,但有一个警告。准备好的事务是危险的,因为它们会持有锁并阻止VACUUM 清理(就像所有其他事务一样),但它们是持久的并且会一直存在,直到您明确删除它们。所以你需要一些软件,一个分布式事务管理器,它是安全的并且可以跟踪所有分布式事务。这个事务管理器可以在一些中断后清理所有准备好的事务。

    【讨论】: