Pull to refresh
220
23.1
Егор Рогов @erogov

Пользователь

Send message

Конечно делитесь! Спасибо, что нашли время этим заняться.

Они, конечно, похожи на уровне общей идеи, но на этом сходство и заканчивается.

Как известно, достаточно развитая технология неотличима от колдунства (:

Полноценный inmemory с колоночным хранением, когда появится, изменит ситуацию с индексированием. Но, во-первых, индексы это все равно не отменяет, во-вторых, inmemory — не панацея, а в-третьих, в ближайшем будущем нам это не грозит, не в 10-ке точно.

На здоровье.
Градус будем повышать постепенно, так что до крепких напитков доберемся не скоро. Но, надеюсь, по пути тоже будет интересно.

Тут скорее не в плюсах дело, а в особенностях реализации. Если интересно, можно заглянуть в слайды и демонстрацию темы № 3 нашего курса DBA2, там все нарисовано подробно.

HOT-то работает с незапамятных времен, с 8.3 начиная. Но работает он (что раньше, что сейчас) так: если какое-то поле есть хотя бы в одном индексе, то при его изменениях будут обновляться все индексы, независимо от того, есть в них это поле или нет. Как я понимаю, на это Убер и наступил.

Все значения a, конечно, придется перебрать. То есть, грубо говоря, индекс придется просмотреть весь или почти весь. Но если индекс по размеру меньше таблицы, то это все равно может оказаться эффективнее, чем просматривать всю таблицу. Речь, конечно, о случае, когда на таблице нет индекса t(b), который, естественно, будет значительно эффективнее.


Постгрес так умеет:


postgres=# \d t
       Table "public.t"
 Column |  Type   | Modifiers 
--------+---------+-----------
 a      | integer | 
 b      | text    | 
 c      | boolean | 
Indexes:
    "t_a_b_idx" btree (a, b)

postgres=# set enable_seqscan = off;
SET

postgres=# explain select * from t where b = '!';
                                  QUERY PLAN                                  
------------------------------------------------------------------------------
 Bitmap Heap Scan on t  (cost=1854.54..2310.04 rows=1000 width=7)
   Recheck Cond: (b = '!'::text)
   ->  Bitmap Index Scan on t_a_b_idx  (cost=0.00..1854.29 rows=1000 width=0)
         Index Cond: (b = '!'::text)
(4 rows)

На самом деле и SELECT * FROM t WHERE b=... тоже, особенно если вместо * там несколько полей. Это может иметь смысл, если индекс покрывающий — может оказаться проще просмотреть индекс (пусть и весь), чем таблицу (тоже всю).

Если про B-дерево говорить, то конечно влияет. Потому что индексные записи будут отсортированы сначала по первому столбцу, а потом по второму. И если у вас в запросе условие на первый столбец и на второй, вы тут же находите нужное значение, просто спускаясь вниз по дереву. А если условие только на второй столбец, то вам придется просматривать весь индекс.
Но это мы вперед забегаем, про все расскажу, когда дойдем до btree.

В частичный индекс попадают только строки, подходящие по явно указанному условию (никакой автоматики на обнаружение высокочастотных значений нет). В нашем примере: create index on t(c) where c; таким условием является c.


Если планировщик, глядя на условие обращения к таблице в запросе, понимает, что нужная информация содержится в частичном индексе, он будет рассматривать его как один из возможных способов доступа к данным.
А комбинирования доступа по индексу и сканирования таблицы никогда не происходит — либо так, либо иначе, но не оба вместе.


Вот другой пример для наглядности:


postgres=# create table test as select * from t;
SELECT 100000

postgres=# create index on test(a) where a < 1000;
CREATE INDEX

Вот так индекс будет использоваться:


postgres=# explain (costs off) select * from test where a < 1000;
              QUERY PLAN               
---------------------------------------
 Bitmap Heap Scan on test
   Recheck Cond: (a < 1000)
   ->  Bitmap Index Scan on test_a_idx
(3 rows)

И вот так планировщик догадается:


postgres=# explain (costs off) select * from test where a < 100;
              QUERY PLAN               
---------------------------------------
 Bitmap Heap Scan on test
   Recheck Cond: (a < 100)
   ->  Bitmap Index Scan on test_a_idx
         Index Cond: (a < 100)
(4 rows)

А вот так — уже нельзя:


postgres=# explain (costs off) select * from test where a < 10000;
      QUERY PLAN       
-----------------------
 Seq Scan on test
   Filter: (a < 10000)
(2 rows)

Метод доступа может возвращать TID-ы по одному, а может в виде битовой карты. Такая карта не содержит никакой информации о видимости, и для ее получения не нужно заглядывать в табличные страницы.


Если получать TID-ы по одному, то может оказаться, что в итоге мы будем обращаться к одной и той же табличной странице несколько раз: сначала за одной строкой, потом за другой… Каждое обращение — это поиск страницы в буферном кэше (и при необходимости вытеснение другой страницы и чтение нужной с диска), установка нужных блокировок, в общем — накладные расходы.
При сканировании битовой карты мы читаем каждую табличную страницу только один раз.

src/backend/access/nbtree/README


We consider deleting an entire page from the btree only when it's become
completely empty of items. (Merging partly-full pages would allow better
space reuse, but it seems impractical to move existing data items left or
right to make this happen — a scan moving in the opposite direction
might miss the items if so.)

Проблема разрастания индексов есть, и связана она не только с очисткой (как в случае таблицы), но и с тем, что индексные страницы могут при необходимости разбиваться на две, но не умеют срастаться обратно.


Но если очистка у вас работает нормально, и если по прошествии месяца секция уже активно не обновляется, то однократное перестроение индексов (чтобы зазря не расходовать место) вроде бы не выглядит большой сложностью?

На здоровье, хотя «чтиво» и звучит несколько пренебрежительно.

Спасибо, рад, что понравилось. Статьи мы писали, пишем и останавливаться не собираемся (:

Всегда пожалуйста. Вторая часть запланирована на после майских.
Вообще боюсь загадывать, но надеюсь в целом выдержать темп примерно раз в месяц.

Есть pageinspect для btree, git и brin, есть gevel для gist и gin.
Про это все тоже будет, когда доживем до конкретных индексов.

Не планировал, но подумаю. Может, какой-то пример приведу.
По уму про json надо отдельную большую статью писать, равно как и про полнотекстовый поиск.

На здоровье!

Information

Rating
252-nd
Location
Москва, Москва и Московская обл., Россия
Works in
Date of birth
Registered
Activity