我在citext
中将PostgreSQL
用于所有文本列类型。我想知道citext
的效果。
我对具有b树索引的文本列执行了简单的WHERE
语句基准,但是在查询成本方面我看不到任何差异。
例如:
Select * From table_text where a = '1';
Select * From table_citext where a= '1';
这些查询的查询费用相同。
据我了解,citext
按原样存储字符串,但不将其转换为小写。因此,当在WHERE
子句中使用一个值时,它会在lower
函数中使用b树索引的每个节点中的每个比较(我使用b树索引)。
如果这就是我所说的,应该会导致性能问题,但事实并非如此。
PostgreSQL如何做到这一点?
PostgreSQL如何在b树索引中存储citext
列值?
答案 0 :(得分: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 INDEX
花费了citext
列更长的时间(它必须执行很多比较),但是查询花费的时间大约相同。
原因是,如果使用索引扫描,则只需要进行很少的比较:对于要访问的2-3个索引块中的每一个,您都将执行二进制搜索,并且可能必须重新检查表中的表行。位图索引扫描的情况。