Кратко:

  • Оптимизатор запросов YDB составляет наилучший план выполнения запроса.
  • Для оптимизации запросов нужно получить и проанализировать план запроса.
  • План запроса можно получить через консоль управления или YDB CLI.
  • План запроса содержит информацию об обращениях к таблицам и операциях чтения и записи.
  • Типы чтения включают FullScan, Scan, Lookup и MultiLookup.
  • Типы записи включают Upsert, MultiUpsert, Erase и MultiErase.
  • План запроса из примера показывает FullScan для таблицы seasons и чтение по ключу для таблицы series.
  • Такой запрос может привести к избыточному росту нагрузки на БД и задержкам.

План запроса

Когда вы обращаетесь к БД, оптимизатор запросов YDB пытается составить наилучший, по его мнению, план выполнения запроса.
Чтобы оптимизировать свои запросы к БД с точки зрения скорости их выполнения (и/или стоимости, что актуально для бессерверного режима YDB), нужно получить и проанализировать этот план. Вы можете это сделать через консоль управления или с помощью YDB CLI.
Давайте разберём план запроса, который мы использовали на прошлом уроке в качестве примера объединения таблиц.
SELECT 
    sa.title AS season_title, 
    sr.title AS series_title, 
    sr.series_id, sa.season_id 
FROM seasons AS sa 
INNER JOIN series AS sr ON sa.series_id = sr.series_id 
WHERE sa.season_id = 1

 

Войдите в редактор SQL вашей БД и вставьте в поле ввода текст запроса. Нажмите на стрелку справа от кнопки Выполнить и в выпадающем меню выберите опцию Explain.
image
В результате внизу отобразится поле, содержащее план запроса.
image
Секция tables плана запроса содержит информацию об обращениях к таблицам. Операции чтения описываются в разделе reads, а операции записи — в разделе writes (в этом плане запроса данный раздел отсутствует).
"tables": [
  {
    "name": "/ru-central1/b1glk1805em030s2ir60/etnm1bh37e2hova06qhf/seasons",
      "reads": [
        {
          "columns": [
            "season_id",
            "series_id",
            "title"
          ],
          "scan_by": [
            "series_id (-∞, +∞)",
            "season_id (-∞, +∞)"
          ],
          "type": "FullScan"
        }
      ]
    },
    {
      "name": "/ru-central1/b1glk1805em030s2ir60/etnm1bh37e2hova06qhf/series",
      "reads": [
        {
          "columns": [
          "series_id",
          "title"
          ],
          "type": "Lookup"
        }
      ]
    }
  ]

 

Ключевой характеристикой любого обращения к таблице является его тип.
Типы чтения:
  • FullScan — полное сканирование таблицы, читаются все записи на всех шардах;
  • Scan — читается определённый диапазон записей;
  • Lookup — чтение по ключу или префиксу ключа;
  • MultiLookup — множественные чтения по ключу или префиксу ключа (такой тип обращения возможен, например, при выполнении инструкций JOIN).
Типы записи:
  • Upsert — добавление одной записи;
  • MultiUpsert — добавление нескольких записей;
  • Erase — единичное удаление по ключу;
  • MultiErase — множественные удаления.
Рассмотрим план запроса из нашего примера.
Параметр scan_by показывает, по каким колонкам выполняется scan, то есть чтение всех записей в определённом диапазоне значений. В columns перечислены колонки, значения которых будут считываться из таблицы.
Из плана запроса следует, что для таблицы seasons будет выполнен FullScan, а для таблицы series — чтение по ключу (Lookup). Тип чтения FullScan означает, что для выполнения запроса потребуется полностью прочитать всю таблицу. Если таблица большая, то такой запрос приведет к избыточному росту нагрузки на БД и задержкам, а в режиме serverless — ещё и к повышенным расходам.