Pull to refresh

Comments 25

В последнее время, всё чаще, использую для индексирования хранимые вью (MS SQL). Из плюсов наглядность, агрегации и группировки. Буду признателен если, кто поделиться минусами данного подхода.

  1. Многочисленные ограничения на indexed view

  2. Невозможность alter table, которые там используются, из-за schemabinding. Конечно, view можно пересоздать, но если записей очень много, это долго

Это да. Ограничения принял как данность, в принципе они логичные. Хотя обязательное использование count_big для аггрегации количества - это по моему лишнее.

Вот сейчас вспомнил неприятный момент с хранимыми вью - нужно обязательно приписывать with(noexpand) при выборке из них. Вью может не подхватиться, хотя указана явно, и раскрыться в запрос.

А это ограничение standard edition

ещё индексы на вьюхах в онлайне не построить.

Аналогичное писал на примере PostgreSQL с пояснительными картинками: https://habr.com/ru/company/tensor/blog/488104/

Еще добавлю пример исключения для низкоселективных полей, когда они используются для сортировки - например, по иерархии "папки в начале": idx(parent::int, isbranch::bool) -> ... WHERE parent = $1 ORDER BY isbranch DESC LIMIT 20;

Создается ощущение что индексы все бывают только B-tree. В оракле, например, для низкоселективных полей есть bitmap. Наверняка в этих системах тоже есть аналоги какие-то. И рассуждать о применимости индекса только из селективности как-то однобоко.

Вот эта часть из статьи как раз подходит к битмап-индексам: "Селективность колонки мала, но селективность набора многих колонок высока. Если все эти колонки используются в WHERE, то такой индекс будет полезен."

Сила битмап-индексов в том, что они хорошо сочетаются друг с другом (а маски нулей и единиц, соответсвенно, накладываются). А недостаток – слишком много строк блокируется при модификации данных, поэтому такие индексы, как правило, не используют в OLTP-системах, а только в хранилищах.

UFO just landed and posted this here

Если в колонке дата/время не на последнем месте, то проверьте

Добавлю от себя исключение из недавнего на работе: если индекс не для сравнения, а для сортировки, то он вполне может начинаться с даты. `ORDER BY date, id` может быть использован для досортировки данных с одинаковой датой (это полезно, например, для пагинации, чтобы одни и те же строки не попали на разные страницы)

Есть три замечания по поводу PostgreSQL: 1) колонку с низкой селективностью, по которой используется предикат в запросе, очень часто надо добавлять не в индекс, а в предикат частичного индекса (при этом уменьшается размер индекса); 2) если неселективная колонка не меняет значения и значения не слишком перемешаны в хипе, может помочь brin-индекс (особенно начиная с 14й версии, в которой появились multirange), в худшем случае, он не сильно много места и ресурсов на поддержку отъест, по сравнению с другими типами (но лучше проверять, на сколько он хорош в конкретном случае); 3) Postgres, никто ведь не пишет "MS SQ".

У MS SQL Server есть отвратительная особенность: при отключенном версионировании( по разным причинам) сервер делает Lock на чтение данных! В этих случаях используется обходной путь чтобы избежать блокировок строятся индексы с кучей колонок в include секции. Выглядит уродливо, но альтернативы в виде read uncommited еще хуже.

Напишите пожалуйста подробнее. Что значит отключение версионирования? И какой лок накладывается? Впервые слышу про эту проблему...

На самом деле там идёт firehorse, как это вроде называют майкрософтовцы, а не Лок.

Своя терминология в MS продуктах это отдельная песня. Но я имел ввиду именно lock т.к. письмо которое приходит после deadlock где одна транзакция меняет данные, а вторая читает данные содержит следующий текст

A deadlock occurred on server NNNNNNN. Full details are available in the SQLdm Desktop Client.

Хотя вполне может быть что одна команда разработчиков MS SQL Server называет firehorse, в то время как другая lock.

Скорее всего это про READ_COMMITTED_SNAPSHOT - с ним читается копия c одним локом SCH-S, без него - ставятся page / row S-локи, блокирующие изменения.

Речь идёт про включение версионирования строк следующей командой:
ALTER DATABASE MyDatabase SET READ_COMMITTED_SNAPSHOT ON

Без это команды прочитать строку во второй транзакции можно с разными хинтами
- WITH(NOLOCK) читает грязные данные (read uncommited)

- WITH(READPAST) пропускает заблокированные строки.

При SET READ_COMMITTED_SNAPSHOT OFF поведение read commited можно получить только через индексное чтение (когда данные из таблицы не читаются совсем)


Я правильно понимаю что мы говорим о двух сессиях, первая обновляет строку, вторая читает ее в обычном read committed?

-- table definition
create table dbo.test1 (
    id int primary key clustered
  , data1 varchar(100)
  , data2 char(100)
  , data3 nchar(100)  
)
go

create index idx_test1_allincluded 
on dbo.test1 ( id ) include ( data1, data2, data3 )
go

insert into dbo.test1 ( id, data1, data2, data3 )
values (1, 'data1 1', 'data2 1', 'data3 1')
     , (2, 'data1 2', 'data2 2', 'data3 2')
     , (3, 'data1 3', 'data2 3', 'data3 3')
go

-- session 1
update dbo.test1 set data1 = 'new data 1' where id = 2 
go

-- session 2
select * from dbo.test1 where id = 2
go

Если да, то в данном случае не важно есть ли дополнительный индекс или нет, т.к. эксклюзивная блокировка ключа (id=2) накладывается и в кластерном индексе и в дополнительном некластерном. Вторая сессия будет ждать завершения первой в любом случае, и с дополнительным индеком и без него.

Как раз вариант будет работать без блокировки если удалить кластерный индекс

select * from dbo.test1 with(INDEX(idx_test1_allincluded)) where id = 2

MS SQL умеет индексное чтение когда данные достаються только из индекса. В этом случаее блокировки нету. Уровень изолированности read_commited

Не работает так, как вы описываете. Без кластерного индекса такое же поведение.

Все также висит X (exclusive) блокировка на некластерном индексе во время апдейта, и она не совместима с S (shared) блокировкой при чтении, вторая сессия ждет завершения первой.

Может быть описываемое вами поведение это просто баг/фича в какой-нибудь старой версии sql server?

Тестировалось на:
Microsoft SQL Server 2019 (RTM-GDR) (KB4583458) - 15.0.2080.9 (X64)   Nov  6 2020 16:50:01   
Copyright (C) 2019 Microsoft Corporation  Developer Edition (64-bit) 
on Windows 10 Pro 10.0 <X64> (Build 19044: ) 

При обосновании подхода "как не надо индексировать" прежде всего стоит сказать, что скорость select всегда достигается за счет ущерба в скорости update, insert и delete

Вот MySQL не умеет нормально использовать несколько индексов в одном запросе.

А noSQL умеют.

Как это достигается?

А noSQL умеют.

а какую именно БД вы имеете в виду? какого-то эталонного NoSQL не существует.
и какие именно запросы?


вот такие вроде все современные мейнстримовые sql (sqlite не в счёт) умеют обрабатывать с использованием двух индексов:


SELECT COUNT(*) FROM t1 WHERE key1 = 1 AND key2 = 1;

ищутся подходящие записи по одному индексу, ищутся по второму, и возвращается пересечение результатов.


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


btw, в первый раз с техникой использования нескольких индексов я встретился в foxpro (ещё под dos):
https://www.levelextreme.com/Home/ShowHeader?Activator=23&ID=8109


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

По моему Оракл умел для bitmap indexes благо это побитные AND

а какую именно БД вы имеете в виду? какого-то эталонного NoSQL не существует.и какие именно запросы?

Эластик, я более имел поисковые движки

Sign up to leave a comment.

Articles