Как PostgreSQL citext хранится в индексе b-дерева?Строчные или как есть? - PullRequest
2 голосов
/ 10 апреля 2019

Я использую citext в PostgreSQL для всех типов текстовых столбцов.Меня интересует производительность citext.

Я выполнил простые WHERE тесты операторов над текстовыми столбцами, имеющими индекс b-дерева, но я не увидел каких-либо различий в стоимости запроса.

Например:

Select * From table_text where a = '1';

Select * From table_citext where a= '1';

Эти запросы имеют одинаковую стоимость запроса.

Насколько я понимаю, citext хранит строку в том виде, в каком она есть, без преобразования ее в нижний регистр.Поэтому, когда значение используется в предложении WHERE, оно использует функцию lower для каждого сравнения в каждом узле индекса b-дерева (я использовал индекс b-дерева).

Если этокак я уже сказал, это должно было вызвать проблемы с производительностью, но это не так.

Как PostgreSQL достигает этого?
Как PostgreSQL хранит citext значения столбцов в индексе b-дерева?

1 Ответ

1 голос
/ 10 апреля 2019

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 блоков индекса, к которым вы обращаетесь, вы выполняете двоичный поиск, и вам, возможно, придется повторно проверить строку таблицынайдено в случае сканирования индекса растрового изображения.

...