Индекс диапазона блоков (BRIN)
Наш последний пример - найти сотрудников, которые имеют дату создания равное определенному значению. В нашем случае мы попробуем найти сотрудников, которые были созданы ранее 2023 года. Для этого изменим наши представления:
def homePageView(request):
employees = Employee.objects.filter(created__year__lte=2022)
return render(request, 'list.html', {'employees': employees})
Запустим сервер и проверим:
Теперь давайте применим индекс B-Tree в поле created, чтобы повысить скорость запроса.
Применение индекса снова сработало с точки зрения оптимизации времени выполнения. К сожалению, мы сталкиваемся с той же проблемой, что и раньше, при проверке размера индекса, который достигает около 10% общего размера таблицы.
Вывод прост - размер индекса довольно велик или, по крайней мере, может быть меньше. К счастью, есть один индекс, который идеально соответствует нашей текущей ситуации.
Индекс BRIN расшифровывается как "Block Range Index" (Индекс диапазона блокировки) и группирует значения в столбце в диапазоны страниц JSON. Эта функция PostgreSQL лучше всего работает, когда данные естественным образом сортируются на диске, что не совсем соответствует нашему примеру, у нас дата создания у всех почти одинаковая. Но главное преимущество этой ситуации заключается в том, что они отсортированы в нашей таблице по возрастанию.
Использование индекса BRIN требует импорта того же пакета, что и Hash Index:
from django.contrib.postgres.indexes import BrinIndex
class Employee(models.Model):
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)
about = models.CharField(max_length=10000)
age = models.SmallIntegerField(null=True)
created = models.DateTimeField(default=timezone.now)
work_experience = models.SmallIntegerField(default=0, null=True)
contact = models.OneToOneField(Contact, on_delete=models.CASCADE, null=True)
department = models.ForeignKey(Department, on_delete=models.CASCADE, default=None, null=True)
class Meta:
indexes = (
BrinIndex(fields=('created',), name="hr_employee_created_ix",
pages_per_range=2
),
)
def __str__(self):
return f'{self.first_name} {self.last_name}'
Выполним миграции и запустим сервер еще раз:
Когда мы проверяем запрос, мы видим, что индекс правильно использовался ядром базы данных. Обычно, время выполнения немного больше, чем при использовании индекса B-Tree. Это связано с тем, что индексы BRIN требуют больше времени, так как они проверяют все значения в контейнерах. В нашем случае время уменьшилось, так как у нас все даты создания работников одинаковые.
Плюс заключается в том, что он оптимизирует дисковое пространство, в нашем случае мы смогли использовать примерно в 5 раз меньше места.
Существует способ обеспечить лучшую оптимизацию времени выполнения индекса BRIN. Решение состоит в том, чтобы поэкспериментировать со значением pages_per_row в самом индексе. Это может немного увеличить размер индекса, поэтому здесь нужно найти баланс.
Резюме:
- Значительно уменьшает размер индекса.
- Отлично подходит для любых дат или естественно растущих значений в столбце.
- Может быть бесполезно, когда данные естественным образом не сортируются на диске.
- Не оптимизирует скорость.