如何从 Firebird SQL 数据库中删除大数据

How to delete large data from Firebird SQL database

我有一个非常大的数据库(至少对我而言)- 超过 1 000 000 条记录,我需要删除所有时间戳低于某个值的记录。例如:

DELETE FROM table WHERE TS < 2020-01-01;

我面临的问题是,事务完成后,如果它完全完成,是数据库没有响应且不可用。如果没有上述问题,我如何删除这么多记录?

我是新手,到目前为止,我只使用过 1000-10000 行的数据库,我用来删除记录的命令没有引起问题。

根据您在问题中的描述和评论,问题与 Firebird 中垃圾收集的工作方式有关。 Firebird 是所谓的 Multi-Version Concurrency Control (MVCC) 数据库,您对行(记录)所做的每一次更改(包括删除)都将创建该记录的新版本,并使以前的版本可用于在该事务之前启动的其他事务已提交更改。

如果先前版本的记录中不再有事务 'interested',则该先前版本有资格进行垃圾回收。 Firebird 有两个垃圾收集选项:cooperative(所有服务器模式都支持)和background(SuperServer 支持),第三个combined 两种模式(这是 SuperServer 的默认设置)。

background 模式是一个清理垃圾的专用线程,如果发现垃圾,它会通过活动语句发出信号。

cooperative模式下,看到垃圾的语句也是必须清理的语句。当语句在大型更新或删除后立即执行完整 table 扫描时,这可能会特别昂贵。该语句不仅会查找并返回行,还会重写数据库页面以清除这些垃圾。

另请参阅幻灯片 Garbage collection mechanism and sweep in details

有一些可能的解决方案:

  1. 如果您使用的是 SuperServer,请更改策略,将 firebird.conf 中的设置 GCPolicy 设置为 background

    此解决方案的缺点是收集所有垃圾可能需要更长的时间,但最大的好处是执行垃圾收集工作不会减慢事务。

  2. 提交一个产生大量垃圾的事务后,执行一个执行完整 table 扫描(例如 select count(*) from table)的语句以触发垃圾收集,使用单独的 worker线程不阻塞你的进程的其余部分。

    只有在没有对那些旧记录版本感兴趣的活动交易时,此选项才真正起作用。

  3. 创建一个 backup 数据库(不需要恢复,除了验证备份是否正常工作)。

    默认情况下(除非您指定 -g 选项来禁用垃圾收集),gbak 工具将在备份期间执行垃圾收集。这与选项 2 具有相同的限制,因为它有效,因为 gbak 相当于 select * from table

  4. 使用 gfix -sweep.

    执行数据库的 'sweep'

    这与前两个选项有类似的限制

  5. 对于不会导致垃圾回收速度减慢的连接,请指定连接选项 isc_dpb_no_garbage_collect(详细信息因驱动程序和连接库而异)。

    如果您为所有连接指定此选项,并且您的策略是 cooperative(因为它已配置,或者您使用的是经典或超经典服务器模式),则没有垃圾收集将发生,这也可能导致最终减速,因为引擎将不得不扫描更长的记录版本链。这可以通过使用前两个选项执行垃圾收集来缓解。

  6. 不是真正删除记录,而是在您的应用程序中引入软删除以将记录标记为已删除,而不是真正删除它们。

    要么永久保留这些记录,要么在以后真正删除它们,例如通过计划作业 运行 在数据库没有负载的时候删除它们,并包括之前的选项之一来触发垃圾回收。

实际上,由于垃圾收集器和工作线程之间的高度紧张,后台垃圾收集正是导致“无响应数据库”行为的原因。协作式 GC 可能会减慢操作速度,但会保持数据库“响应”。至少对于版本 2.5。

另一个原因是有很多重复项的坏索引。此类索引通常对查询无用,应该简单地删除。如果这不是一个选项,它们可以在删除之前停用并在单独的事务之后重新激活(作为副作用,激活将导致完全垃圾收集)。

当然,最好的选择是保留所有数据。 1kk 记录对于体面硬件上设计良好的数据库来说并不算多。