PostgreSQL citext如何存储在b-tree索引中?小写还是原样?

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

我在citext中使用PostgreSQL用于所有文本列类型。我想知道citext表现。

我在具有b树索引的文本列上执行了简单的WHERE语句基准测试,但我在查询成本方面没有看到任何差异。

例如:

Select * From table_text where a = '1';

Select * From table_citext where a= '1';

这些查询具有相同的查询成本。

据我了解,citext存储字符串,而不将其转换为小写。因此,当在WHERE子句中使用某个值时,它会对b-tree索引的每个节点中的每个比较使用lower函数(我使用了b-tree索引)。

如果这就像我说的那样,这应该会导致性能问题,但事实并非如此。

PostgreSQL如何实现这一目标? PostgreSQL如何在b-tree索引中存储citext列值?

postgresql query-performance case-insensitive b-tree-index
1个回答
1
投票

citext在输入时存储,不会转换为小写。这也适用于存储为b树索引键。

魔术发生在citext的比较函数中:

/*
 * citextcmp()
 * Internal comparison function for citext strings.
 * Returns int32 negative, zero, or positive.
 */
static int32
citextcmp(text *left, text *right, Oid collid)
{
    char       *lcstr,
               *rcstr;
    int32       result;

    /*
     * We must do our str_tolower calls with DEFAULT_COLLATION_OID, not the
     * input collation as you might expect.  This is so that the behavior of
     * citext's equality and hashing functions is not collation-dependent.  We
     * should change this once the core infrastructure is able to cope with
     * collation-dependent equality and hashing functions.
     */

    lcstr = str_tolower(VARDATA_ANY(left), VARSIZE_ANY_EXHDR(left), DEFAULT_COLLATION_OID);
    rcstr = str_tolower(VARDATA_ANY(right), VARSIZE_ANY_EXHDR(right), DEFAULT_COLLATION_OID);

    result = varstr_cmp(lcstr, strlen(lcstr),
                        rcstr, strlen(rcstr),
                        collid);

    pfree(lcstr);
    pfree(rcstr);

    return result;
}

所以是的,这应该会产生一些开销。它的成本还取决于数据库的默认排序规则。

我将使用没有索引的查询来演示这一点。我正在使用德语整理:

SHOW lc_collate;
 lc_collate 
------------
 de_DE.utf8
(1 row)

首先使用text

CREATE TABLE large_text(t text NOT NULL);

INSERT INTO large_text
   SELECT i||'text'
   FROM generate_series(1, 1000000) AS i;

VACUUM (FREEZE, ANALYZE) large_text;

\timing on

SELECT * FROM large_text WHERE t = TEXT 'mama';
 t 
---
(0 rows)

Time: 79.862 ms

现在与citext相同的实验:

CREATE TABLE large_citext(t citext NOT NULL);

INSERT INTO large_citext
   SELECT i||'text'
   FROM generate_series(1, 1000000) AS i;

VACUUM (FREEZE, ANALYZE) large_citext;

\timing on

SELECT * FROM large_citext WHERE t = CITEXT 'mama';
 t 
---
(0 rows)

Time: 567.739 ms

所以citext慢了七倍。

但不要忘记,这些实验中的每一个都进行了一次顺序扫描,并进行了一百万次比较。 如果使用索引,差异将不明显:

CREATE INDEX ON large_text (t);

Time: 5443.993 ms (00:05.444)

SELECT * FROM large_text WHERE t = CITEXT 'mama';
 t 
---
(0 rows)

Time: 1.867 ms


CREATE INDEX ON large_citext (t);

Time: 28009.904 ms (00:28.010)

SELECT * FROM large_citext WHERE t = CITEXT 'mama';
 t 
---
(0 rows)

Time: 1.988 ms

你看到CREATE INDEXcitext列上需要更长的时间(它必须执行大量的比较),但查询大约需要相同的时间。

原因是如果使用索引扫描,则只需要很少的比较:对于您访问的2-3个索引块中的每一个,您执行二进制搜索,并且您可能必须重新检查在某个情况下找到的表行。位图索引扫描。

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