与 SQLite 相比,Firebird COUNT 非常慢

Cor*_*ore 4 firebird

过去几个月我一直在使用 Firebird,今天一位客户抱怨性能问题。

我检查了客户的数据库,他们在一张表中有 167,77,216 行。因此,我将该表克隆到另一个空数据库中,并加载虚拟记录并使用简单的查询进行测试

select count(journal_id) from acc_journal
Run Code Online (Sandbox Code Playgroud)

花了14.28秒。

之后我创建了一个 SQLite 数据库并加载了这些数据,花了 2.216 秒。

这是 Firebid 的本质还是我错过了什么?

火鸟表结构:

CREATE TABLE ACC_JOURNAL (
  JOURNAL_ID       INTEGER GENERATED BY DEFAULT AS IDENTITY NOT NULL,
  ACCOUNT_HEAD_ID  INTEGER,
  REF_ID           INTEGER,
  REF_TYPE         VARCHAR(20),
  DESCRIPTION      VARCHAR(512),
  DEBIT            DECIMAL(18,4),
  DEBIT_EX         DECIMAL(18,4),
  CREDIT           DECIMAL(18,4),
  CREDIT_EX        DECIMAL(18,4),
  CURRENCY_ID      INTEGER,
  STATUS           VARCHAR(20),
  EX_RATE          DECIMAL(18,4),
  ENTRY_DATE       VARCHAR(20),
  ENTRY_TIME       VARCHAR(20),
  BRANCH_CODE      VARCHAR(20),
  LAST_SYNCED      VARCHAR(20),
  LAST_UPDATED     DECIMAL(18,4),
  ENTRY_DATE2      VARCHAR(20),
  /* Keys */
  PRIMARY KEY (JOURNAL_ID)
);
Run Code Online (Sandbox Code Playgroud)

SQLite表结构:

CREATE TABLE ACC_JOURNAL (
    ACCOUNT_HEAD_ID INTEGER,
    REF_ID          INTEGER,
    REF_TYPE        VARCHAR (20),
    DESCRIPTION     VARCHAR (512),
    DEBIT           DECIMAL (18, 4),
    DEBIT_EX        DECIMAL (18, 4),
    CREDIT          DECIMAL (18, 4),
    CREDIT_EX       DECIMAL (18, 4),
    CURRENCY_ID     INTEGER,
    STATUS          VARCHAR (20),
    EX_RATE         DECIMAL (18, 4),
    ENTRY_DATE      VARCHAR (20),
    ENTRY_TIME      VARCHAR (20),
    BRANCH_CODE     VARCHAR (20),
    LAST_SYNCED     VARCHAR (20),
    LAST_UPDATED    DECIMAL (18, 4),
    ENTRY_DATE2     VARCHAR (20),
    JOURNAL_ID      INTEGER         PRIMARY KEY AUTOINCREMENT
);
Run Code Online (Sandbox Code Playgroud)

Mar*_*eel 5

不幸的是,这是因为 Firebird 必须进行全表扫描才能获取计数。我不熟悉 SQLite 的工作原理,但快速搜索似乎表明 SQLite 可以使用索引来获取计数(例如,count(*)如果使用 ,则使用主键索引或列上的索引count(columnname),尽管我还没有验证这是否实际上是正确的)。

Firebird之所以需要进行全表扫描,是因为Firebird是一个MVCC(多版本并发控制)数据库,但记录可见性信息不包含在其索引中。因此,为了能够知道记录对您的交易可见并因此应包含在计数中,Firebird 需要读取实际记录。而且由于查询是无条件计数,因此使用索引然后检查每条记录会比仅读取每条记录慢。

但是,您可以采取一些措施来提高性能:

  • 使用count(*)而不是count(journal_id).

    使用count(*)只会计算记录,而会计算不为空的count(journal_id)记录。journal_idFirebird 不使用not null主键的状态,因此它会检查每条记录的该列是否不为空。在我的系统上,更改为可count(*)将查询时间减少大约 25%。

  • 使用更大的页面尺寸。

    您的示例数据库的页面大小为 4096,将其增加到页面大小 16384 可以将示例查询(使用count(journal_id))的执行时间减少大约 20%,而count(*)(与页面大小 4096 上的原始查询相比)减少更多超过50%。

然而,鉴于无条件的整体性能较差count,主要建议是避免使用无条件count