Объясняя медленные запросы моему менеджеру
В нашей предыдущей записи блога о наблюдаемости мы объяснили, как построить комплексное представление о том, как ведут себя ваши рабочие нагрузки SQL, и о многих причинах, почему важно иметь это представление. В этом сообщении блога мы более подробно рассмотрим четыре типа SQL-запросов и то, как они могут повлиять на работу конечного пользователя.
Прежде чем идти дальше, давайте резюмируем, что это за четыре типа запросов:
Когда дело доходит до рабочих нагрузок SQL, существует два разных типа: OLTP и OLAP .
OLTP (для O н L ине Т ransactional Р rocess) рабочие нагрузки соответствуют «органической» использованием баз данных. Эти операции используются для более эффективного использования баз данных веб-сайтов, API-интерфейсов, платформ электронной коммерции и т. Д. В то время как OLAP полагается исключительно на чтение, рабочие нагрузки OLTP полагаются на все типы запросов, включая выбор, вставку, обновление и удаление. В OLTP мы хотим, чтобы запросы отвечали как можно быстрее, обычно менее чем за несколько сотен миллисекунд. Причина этой потребности в скорости состоит в том, чтобы уменьшить влияние запросов на взаимодействие с пользователем вашего приложения. В конце концов, кто любит веб-сайты, которые загружаются бесконечно? Что касается количества запросов, мы обычно считаем их тысячами в секунду.
OLAP (для O н L INE A nalytic P rocess) рабочие нагрузки используются для извлечения и анализа больших объемов данных (отсюда и название). Они являются основным инструментом, используемым программными платформами бизнес-аналитики для составления прогнозов и отчетов. Что касается запросов, рабочие нагрузки OLAP обычно полагаются исключительно на несколько избранных, которые периодически выполняются и могут занять много времени (от минут до часов).
Как видите, рабочие нагрузки OLTP и OLAP очень разные. Их сравнение похоже на сравнение гоночного автомобиля (OLTP, надеюсь) с грузовиком (OLAP).
Теперь, когда у нас есть общее представление о двух типах рабочих нагрузок, давайте сосредоточимся на OLTP, поскольку они обычно наиболее актуальны для частей ваших платформ, ориентированных на клиентов. В начале этого поста я описал четыре различных типа SQL-запросов с точки зрения их назначения. Теперь мы классифицируем их по поведению: хорошее, плохое, уродливое и медленное. Что это значит, спросите вы? Разрешите пояснить (спойлер: вы хотите, чтобы ваши запросы попадали в категорию «хорошо»)…
Как и следовало ожидать, хорошие запросы — это те, которые выполняются должным образом и отвечают относительно быстро. В OVHcloud мы определяем «быстро» как время отклика менее одной секунды для наших внутренних баз данных. Но одна секунда — это еще долгое время для ожидания ответа, особенно когда выполняется несколько запросов для загрузки одной веб-страницы. Обычно мы стремимся к 10-20 мс. Вы должны нарисовать эту «быструю линию» в зависимости от ваших настроек, ресурсов и предполагаемого использования.
Ваш бэкэнд запросит базу данных и получит ответ, скажем, через 20 мс, что оставит достаточно времени для обработки данных и отправки результата. Чем быстрее будут выполняться ваши запросы, тем счастливее будут ваши клиенты и начальник.
Когда я хочу объяснить своему боссу, почему быстрые запросы хороши, это довольно просто: быстрые запросы означают хорошее взаимодействие с пользователем, быстрый заказ, быструю оплату и большую прибыль.
С другой стороны, плохие запросы — это запросы, которые не могут быть выполнены СУБД. Причин может быть несколько: ошибка в коде, отсутствие контроля где-то в процессе и т. Д.
Возьмем пример. На вашем веб-сайте есть форма, в которой пользователи могут создать учетную запись, в которой они указывают свой возраст. В пользовательском интерфейсе текстовое поле позволяет пользователю вводить все, что он хочет, и передавать значение в виде строки. Но если ваша схема хорошо спроектирована, в поле «возраст» должно быть указано целое число. Таким образом, если пользователь пытается ввести свой возраст в виде строки в поле, а не числа, СУБД должна вернуть ошибку. Решение простое: форма пользовательского интерфейса должна проверять тип данных, заполненных в поле, и возвращать сообщение об ошибке, например «недопустимые данные», в интерфейсе пользовательского интерфейса, вместо того, чтобы ждать, пока СУБД сделает это. В подобных случаях рекомендуется разрешать только числа.
Вы можете исправить этот тип «плохого» запроса, добавив больше контроля через цепочку, используя правильный тип в пользовательском интерфейсе, с проверками во внешнем интерфейсе, промежуточном программном обеспечении, серверной части и т. Д.
Моему боссу я бы объяснил, что неверные запросы являются препятствием для клиентов, желающих использовать ваш сервис, и приводят к потере прибыли. Однако из-за их простой природы их обычно относительно легко отлаживать.
Уродливые запросы более проблематичны. Это запросы, которые иногда работают, а иногда нет из-за тупиковых ситуаций.
Тупики — обширная тема, но пока давайте будем простыми. Тупиковая ситуация возникает, когда несколько запросов ждут завершения друг друга. Давайте посмотрим на следующий пример:
Мы видим, что у нас есть два процесса, пытающихся обновить запас в рамках транзакции. Процессы №1 и №2 блокируют разные строки. Процесс №1 блокирует строку 123, а процесс №2 блокирует строку 456 на этапе 2. На этапе 3, не снимая блокировки с текущей строки, процесс №1 пытается получить блокировку для строки 456, которая уже принадлежит процесс №2, и наоборот. Чтобы завершить транзакцию, они оба ждут друг друга. Я выбрал простой пример с двумя запросами, но эта проблема может возникнуть с десятками или тысячами запросов одновременно. Общие правила при работе с транзакциями — совершать их как можно быстрее.
Сложность заключается в том, что запрос может быть совершенно правильным и работать большую часть времени, особенно в вашем конвейере CI / CD, где угловые случаи и редкие события не обязательно выявляются и тестируются. Но чем больше растет ваш бизнес, тем выше вероятность возникновения этих редких событий, поскольку вы увеличиваете количество выполняемых одновременно запросов. И, к сожалению, наиболее вероятно, что проблемы с тупиками возникают во время пиков нагрузки, вызванных продажами, праздниками и т. Д. Другими словами, именно тогда, когда вам нужно, чтобы ваш рабочий процесс работал идеально.
Чтобы объяснить это своему боссу, я бы сказал, что когда происходит взаимоблокировка, что-то не так в серверной части, в схеме базы данных или в самой логике рабочего процесса. В худшем случае проблема возникнет в самый неудобный момент, поэтому клиент не сможет взаимодействовать с вашей системой и даже не получит понятное сообщение об ошибке, которое будет сложно исправить. Чтобы понять, отладить и исправить тупиковые ситуации, нужно время. К тому времени, как вы предложите исправление, ваши клиенты будут тратить свои деньги в другом месте, или ваша поддержка будет рушиться из-за билетов и звонков.
Ваша любимая СУБД в конечном итоге убьет все запросы, но только по истечении заданного времени ожидания. И, конечно же, тайм-аут означает, что ваши клиенты будут ждать результата, прежде чем выдаст ошибку. Ага, это некрасиво…
Наконец, как вы, наверное, догадались, медленные запросы — это запросы, выполнение которых требует времени. Их очень легко описать, но не так-то просто исправить, и их нужно постоянно улучшать. Вот несколько распространенных причин медленных запросов:
В этом случае моему боссу объяснения не нужны. Медленные запросы означают более медленные вызовы API, более медленный пользовательский интерфейс и меньшее количество клиентов, достигающих стадии оформления заказа.
Исправление может быть простым: переписать запросы, найти и добавить недостающие индексы и получить только то, что необходимо. Однако уменьшить объем данных, которые должны проходить ваши запросы, немного сложнее. Это можно сделать с помощью регулярных чисток в вашей БД, архивирования, разбиения на разделы и т. Д. Но на практике вы должны хранить только актуальные и актуальные данные в ваших клиентских базах данных, чтобы избежать раздувания.
Подведем итоги и подведем итоги:
И последний совет… это не разовая работа. Вам нужно внимательно следить за этими четырьмя категориями запросов.
Вот и все, ребята! Теперь вы знаете достаточно, чтобы копаться в своих приложениях и базах данных, чтобы постоянно улучшать свои рабочие нагрузки. В конечном итоге на ваших клиентов влияют все четыре категории, поэтому я уверен, что вы знаете, почему вам нужны только хорошие запросы в своих информационных системах!
Прежде чем идти дальше, давайте резюмируем, что это за четыре типа запросов:
- Выбрать для чтения данных
- Вставка предназначена для добавления данных
- Обновление предназначено для изменения данных, которые уже существуют
- Удалить для удаления данных
my-database=# select * from customers where nic = XXX;
my-database=# insert into customers values (1, 'user-firstname', 'user-lastname', '21');
my-database=# update customers set address = 'xxx xxx' where nic = 'XXX';
my-database=# delete from customers where nic = XXX
Когда дело доходит до рабочих нагрузок SQL, существует два разных типа: OLTP и OLAP .
Рабочие нагрузки OLTP
OLTP (для O н L ине Т ransactional Р rocess) рабочие нагрузки соответствуют «органической» использованием баз данных. Эти операции используются для более эффективного использования баз данных веб-сайтов, API-интерфейсов, платформ электронной коммерции и т. Д. В то время как OLAP полагается исключительно на чтение, рабочие нагрузки OLTP полагаются на все типы запросов, включая выбор, вставку, обновление и удаление. В OLTP мы хотим, чтобы запросы отвечали как можно быстрее, обычно менее чем за несколько сотен миллисекунд. Причина этой потребности в скорости состоит в том, чтобы уменьшить влияние запросов на взаимодействие с пользователем вашего приложения. В конце концов, кто любит веб-сайты, которые загружаются бесконечно? Что касается количества запросов, мы обычно считаем их тысячами в секунду.
my-database=# insert into cart values (...); -- create a new cart
my-database=# insert into cart_content values (...); -- add items
my-database=# update cart_content set ... where ...; -- modify your cart
my-database=# select item_name, count from cart_content where ...; -- check the content
my-database=# insert into sales values (...); -- validate the cart
Рабочая нагрузка OLAP
OLAP (для O н L INE A nalytic P rocess) рабочие нагрузки используются для извлечения и анализа больших объемов данных (отсюда и название). Они являются основным инструментом, используемым программными платформами бизнес-аналитики для составления прогнозов и отчетов. Что касается запросов, рабочие нагрузки OLAP обычно полагаются исключительно на несколько избранных, которые периодически выполняются и могут занять много времени (от минут до часов).
Как видите, рабочие нагрузки OLTP и OLAP очень разные. Их сравнение похоже на сравнение гоночного автомобиля (OLTP, надеюсь) с грузовиком (OLAP).
Классифицирующие запросы: хорошие, плохие, уродливые… и медленные
Теперь, когда у нас есть общее представление о двух типах рабочих нагрузок, давайте сосредоточимся на OLTP, поскольку они обычно наиболее актуальны для частей ваших платформ, ориентированных на клиентов. В начале этого поста я описал четыре различных типа SQL-запросов с точки зрения их назначения. Теперь мы классифицируем их по поведению: хорошее, плохое, уродливое и медленное. Что это значит, спросите вы? Разрешите пояснить (спойлер: вы хотите, чтобы ваши запросы попадали в категорию «хорошо»)…
Добро
Как и следовало ожидать, хорошие запросы — это те, которые выполняются должным образом и отвечают относительно быстро. В OVHcloud мы определяем «быстро» как время отклика менее одной секунды для наших внутренних баз данных. Но одна секунда — это еще долгое время для ожидания ответа, особенно когда выполняется несколько запросов для загрузки одной веб-страницы. Обычно мы стремимся к 10-20 мс. Вы должны нарисовать эту «быструю линию» в зависимости от ваших настроек, ресурсов и предполагаемого использования.
Ваш бэкэнд запросит базу данных и получит ответ, скажем, через 20 мс, что оставит достаточно времени для обработки данных и отправки результата. Чем быстрее будут выполняться ваши запросы, тем счастливее будут ваши клиенты и начальник.
Когда я хочу объяснить своему боссу, почему быстрые запросы хороши, это довольно просто: быстрые запросы означают хорошее взаимодействие с пользователем, быстрый заказ, быструю оплату и большую прибыль.
my-database=# select firstname, lastname from customers where id = 123;
Плохо
С другой стороны, плохие запросы — это запросы, которые не могут быть выполнены СУБД. Причин может быть несколько: ошибка в коде, отсутствие контроля где-то в процессе и т. Д.
Возьмем пример. На вашем веб-сайте есть форма, в которой пользователи могут создать учетную запись, в которой они указывают свой возраст. В пользовательском интерфейсе текстовое поле позволяет пользователю вводить все, что он хочет, и передавать значение в виде строки. Но если ваша схема хорошо спроектирована, в поле «возраст» должно быть указано целое число. Таким образом, если пользователь пытается ввести свой возраст в виде строки в поле, а не числа, СУБД должна вернуть ошибку. Решение простое: форма пользовательского интерфейса должна проверять тип данных, заполненных в поле, и возвращать сообщение об ошибке, например «недопустимые данные», в интерфейсе пользовательского интерфейса, вместо того, чтобы ждать, пока СУБД сделает это. В подобных случаях рекомендуется разрешать только числа.
my-database=# insert into user values (1, 'user-firstname', 'user-lastname', 'twenty years old');
ERROR: invalid input syntax for integer: "twenty years old"
Вы можете исправить этот тип «плохого» запроса, добавив больше контроля через цепочку, используя правильный тип в пользовательском интерфейсе, с проверками во внешнем интерфейсе, промежуточном программном обеспечении, серверной части и т. Д.
Моему боссу я бы объяснил, что неверные запросы являются препятствием для клиентов, желающих использовать ваш сервис, и приводят к потере прибыли. Однако из-за их простой природы их обычно относительно легко отлаживать.
Уродливый
Уродливые запросы более проблематичны. Это запросы, которые иногда работают, а иногда нет из-за тупиковых ситуаций.
Тупики — обширная тема, но пока давайте будем простыми. Тупиковая ситуация возникает, когда несколько запросов ждут завершения друг друга. Давайте посмотрим на следующий пример:
-- STEP #1
process 1: BEGIN; -- this will start the transaction
process 2: BEGIN;
-- STEP #2
process 1: UPDATE stock SET available = 10 WHERE id = 123; -- lock row 123
process 2: UPDATE stock SET available = 0 WHERE id = 456; -- lock row 456
-- STEP #3 The ugly part starts now
process 1: UPDATE stock SET available = 1 WHERE id = 456; -- wait for lock on row 456
process 2: UPDATE stock SET available = 9 WHERE id = 123; -- wait for lock on row 123
Мы видим, что у нас есть два процесса, пытающихся обновить запас в рамках транзакции. Процессы №1 и №2 блокируют разные строки. Процесс №1 блокирует строку 123, а процесс №2 блокирует строку 456 на этапе 2. На этапе 3, не снимая блокировки с текущей строки, процесс №1 пытается получить блокировку для строки 456, которая уже принадлежит процесс №2, и наоборот. Чтобы завершить транзакцию, они оба ждут друг друга. Я выбрал простой пример с двумя запросами, но эта проблема может возникнуть с десятками или тысячами запросов одновременно. Общие правила при работе с транзакциями — совершать их как можно быстрее.
Сложность заключается в том, что запрос может быть совершенно правильным и работать большую часть времени, особенно в вашем конвейере CI / CD, где угловые случаи и редкие события не обязательно выявляются и тестируются. Но чем больше растет ваш бизнес, тем выше вероятность возникновения этих редких событий, поскольку вы увеличиваете количество выполняемых одновременно запросов. И, к сожалению, наиболее вероятно, что проблемы с тупиками возникают во время пиков нагрузки, вызванных продажами, праздниками и т. Д. Другими словами, именно тогда, когда вам нужно, чтобы ваш рабочий процесс работал идеально.
Чтобы объяснить это своему боссу, я бы сказал, что когда происходит взаимоблокировка, что-то не так в серверной части, в схеме базы данных или в самой логике рабочего процесса. В худшем случае проблема возникнет в самый неудобный момент, поэтому клиент не сможет взаимодействовать с вашей системой и даже не получит понятное сообщение об ошибке, которое будет сложно исправить. Чтобы понять, отладить и исправить тупиковые ситуации, нужно время. К тому времени, как вы предложите исправление, ваши клиенты будут тратить свои деньги в другом месте, или ваша поддержка будет рушиться из-за билетов и звонков.
process 34099 detected deadlock while waiting for ShareLock on transaction 4026716977 after 1000.042 ms
DETAIL: Process holding the lock: 34239. Wait queue: .
CONTEXT: while locking tuple (259369,24) in relation "table"
STATEMENT: SELECT col from table where ...
Ваша любимая СУБД в конечном итоге убьет все запросы, но только по истечении заданного времени ожидания. И, конечно же, тайм-аут означает, что ваши клиенты будут ждать результата, прежде чем выдаст ошибку. Ага, это некрасиво…
И медленный…
Наконец, как вы, наверное, догадались, медленные запросы — это запросы, выполнение которых требует времени. Их очень легко описать, но не так-то просто исправить, и их нужно постоянно улучшать. Вот несколько распространенных причин медленных запросов:
- Плохо написанные запросы (а в продукте этого нет?)
- Отсутствующие индексы
- Получено слишком много строк
- Слишком много данных для обработки
В этом случае моему боссу объяснения не нужны. Медленные запросы означают более медленные вызовы API, более медленный пользовательский интерфейс и меньшее количество клиентов, достигающих стадии оформления заказа.
Исправление может быть простым: переписать запросы, найти и добавить недостающие индексы и получить только то, что необходимо. Однако уменьшить объем данных, которые должны проходить ваши запросы, немного сложнее. Это можно сделать с помощью регулярных чисток в вашей БД, архивирования, разбиения на разделы и т. Д. Но на практике вы должны хранить только актуальные и актуальные данные в ваших клиентских базах данных, чтобы избежать раздувания.
Вывод
Подведем итоги и подведем итоги:
- Хорошие запросы — признак здоровой рабочей нагрузки. Чем больше у тебя есть, тем лучше
- Плохие запросы означают, что где-то что-то сломано
- Уродливые запросы ждут худшего момента, чтобы дать вам пощечину
- Медленные запросы означают, что у вас что-то работает, но есть возможности для улучшения
И последний совет… это не разовая работа. Вам нужно внимательно следить за этими четырьмя категориями запросов.
Вот и все, ребята! Теперь вы знаете достаточно, чтобы копаться в своих приложениях и базах данных, чтобы постоянно улучшать свои рабочие нагрузки. В конечном итоге на ваших клиентов влияют все четыре категории, поэтому я уверен, что вы знаете, почему вам нужны только хорошие запросы в своих информационных системах!
0 комментариев