Как 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
уникален для каждой строки.