Как PostgreSQL обрабатывает get() в Django для точного соответствия пути к файлу и какова его производительность?

Я использую Django с базой данных PostgreSQL, и у меня есть модель, которая хранит пути к файлам (в частности, пути к файлам S3) в поле CharField, вот так:

class File(models.Model):
    file_path = models.CharField(max_length=255)

Я часто получаю запись одного файла с помощью метода Django get() с точным совпадением пути к файлу S3:

file = File.objects.get(file_path='/bucket/path/to/file.txt')

Это выражается в следующем SQL-запросе:

SELECT * FROM my_table WHERE file_path = '/bucket/path/to/file.txt' LIMIT 1;

Я хочу понять, что происходит под капотом, когда PostgreSQL обрабатывает этот запрос. А именно:

1.  How does PostgreSQL handle the comparison of strings when using the = operator on a CharField with an S3 file path?
2.  Does PostgreSQL behave differently if there’s an index on the file_path column, and how does it decide between using an index or performing a sequential scan?
3.  Is it beneficial to create an index on the file_path field for better performance,

Если вы знаете, как работает ORM в Django, вы увидите, что он просто преобразует синтаксическую нотацию Model в обычный текст SQL, например:

File.objects.get(file_path='/bucket/path/to/file.txt')

Он будет преобразован в нечто вроде следующего:

SELECT * FROM "appname_file" WHERE "file_path" = '/bucket/path/to/file.txt' LIMIT 1;

В этом случае, если file_path не является индексом, SQL-движок выполняет Execution Plan и выполняет операцию под названием: FULL SCAN, что означает, что он наверняка просканирует все строки в таблице.

Если вы решите создать индекс file_path, он будет делать нечто совсем другое, потому что он хранит нечто под названием Data pages or Leaf nodes, которое содержит отображение того, где выделено подмножество записей, в результате чего он запросит data_pages и оттуда получит уникальный идентификатор для этой строки и не будет выполнять FULL SCAN.

Размышления:

  • Для вашего случая использования, если у вас не так много индексов, я думаю, что это хороший случай, чтобы создать один.
  • Вы можете оценить производительность, создавая план, используя ключевое слово EXPLAIN в sql, и вы можете проанализировать результаты.
  • Если вы хотите углубиться в это, вы можете использовать EXPLAIN ANALYZE, это даст вам не только план выполнения, но и время выполнения.
  • Вы можете использовать Django для получения плана выполнения следующим образом:
from django.db import connection
sql = 'EXPLAIN ANALYZE SELECT * FROM appname_file WHERE file_path=%s LIMIT 1;'
params = ['/bucket/path/to/file.txt']
with connection.cursor() as cursor:
    cursor.execute(sql, params):
    plan = cursor.fetchall()

print(plan)

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

Вернуться на верх