Рубрики
Без рубрики

Как индексировать столбцы JSON с помощью MySQL

Узнайте, как индексировать данные JSON с помощью виртуальных столбцов MySQL и ускорить SQL-запросы, необходимые для извлечения атрибутов JSON.

Автор оригинала: Vlad Mihalcea.

Вступление

В этой статье я собираюсь объяснить, как мы можем индексировать столбцы JSON при использовании MySQL.

В то время как другие системы реляционных баз данных предоставляют индексы GIN (Обобщенный инвертированный индекс), MySQL позволяет вам индексировать виртуальный столбец, который отражает выражение пути JSON, которое вы хотите индексировать.

Таблица базы данных

Давайте предположим, что у нас есть следующая база данных книга таблица:

| id | isbn           | properties                                                     |
|----|----------------|----------------------------------------------------------------|
| 1  | 978-9730228236 |{                                                               |
|    |                |   "price":44.99,                                               |
|    |                |   "title":"High-Performance Java Persistence",                 |
|    |                |   "author":"Vlad Mihalcea",                                    |
|    |                |   "reviews":[                                                  |
|    |                |   {                                                            |
|    |                |      "date":"2017-11-14",                                      |
|    |                |      "rating":5,                                               |
|    |                |      "review":"Excellent book to understand Java Persistence", |
|    |                |      "reviewer":"Cristiano"                                    |
|    |                |   },                                                           |
|    |                |   {                                                            |
|    |                |      "date":"2019-01-27",                                      |
|    |                |      "rating":5,                                               |
|    |                |      "review":"The best JPA ORM book out there",               |
|    |                |      "reviewer":"T.W"                                          |
|    |                |   },                                                           |
|    |                |   {                                                            |
|    |                |      "date":"2016-12-24",                                      |
|    |                |      "rating":4,                                               |
|    |                |      "review":"The most informative book",                     |
|    |                |      "reviewer":"Shaikh"                                       |
|    |                |   }                                                            |
|    |                |   ],                                                           |
|    |                |   "publisher":"Amazon"                                         |
|    |                |}                                                               |
|----|----------------|----------------------------------------------------------------|

Тип столбца свойстваjson , поэтому мы можем хранить объекты JSON как свойства книги.

Запрос столбцов MySQL JSON без индекса

Если мы попытаемся отфильтровать одну запись по ее связанному заголовку атрибуту, расположенному внутри свойств JSONObject:

SELECT
  isbn
FROM book
WHERE
  properties ->> "$.title" = 'High-Performance Java Persistence'

Сканирование всей таблицы будет использоваться для фильтрации всех записей, найденных в таблице книга :

{
  "query_block": {
    "select_id": 1,
    "cost_info": {
      "query_cost": "50.25"
    },
    "table": {
      "table_name": "book",
      "access_type": "ALL",
      "rows_examined_per_scan": 500,
      "rows_produced_per_join": 500,
      "filtered": "100.00",
      "cost_info": {
        "read_cost": "0.25",
        "eval_cost": "50.00",
        "prefix_cost": "50.25",
        "data_read_per_join": "140K"
      },
      "used_columns": [
        "isbn",
        "properties"
      ],
      "attached_condition": "(
        json_unquote(
          json_extract(
            `high_performance_sql`.`book`.`properties`,'$.title'
          )
        ) = 'High-Performance Java Persistence'
      )"
    }
  }
}

Тип доступа ВСЕ означает, что все страницы были отсканированы, что также подтверждается атрибутом rows_examined_per_scan , поскольку в таблице книга у нас всего 500 записей.

Добавьте виртуальный столбец MySQL для индексирования выражения пути JSON

В MySQL единственный способ индексировать выражение JSONPath-это добавить виртуальный столбец, который отражает рассматриваемое выражение пути, и создать индекс для виртуального столбца.

По этой причине мы собираемся добавить следующий заголовок виртуальный столбец в нашу книгу таблицу:

ALTER TABLE 
  book 
ADD COLUMN 
  title VARCHAR(50) 
GENERATED ALWAYS AS (
  properties ->> \"$.title\"
)

Как вы можете видеть, столбец title сопоставляется с выражением $.title пути в столбце свойства JSON.

Далее мы добавим индекс в столбец title , например:

CREATE INDEX book_title_idx ON book (title)

И при повторном выполнении предыдущего SQL-запроса мы теперь получаем следующий план выполнения:

{
  "query_block": {
    "select_id": 1,
    "cost_info": {
      "query_cost": "0.35"
    },
    "table": {
      "table_name": "book",
      "access_type": "ref",
      "possible_keys": [
        "book_title_idx"
      ],
      "key": "book_title_idx",
      "used_key_parts": [
        "title"
      ],
      "key_length": "203",
      "ref": [
        "const"
      ],
      "rows_examined_per_scan": 1,
      "rows_produced_per_join": 1,
      "filtered": "100.00",
      "cost_info": {
        "read_cost": "0.25",
        "eval_cost": "0.10",
        "prefix_cost": "0.35",
        "data_read_per_join": "288"
      },
      "used_columns": [
        "isbn",
        "properties",
        "title"
      ]
    }
  }
}

Не то, чтобы атрибут ключ ссылался на только что созданный индекс book_title_idx , что означает, что SQL-запрос не использует индекс для фильтрации записей book , что также подтверждается атрибутом rows_examined_per_scan , поскольку была отсканирована одна строка индекса.

Обновление индексированного атрибута JSON

Если вы обновите индексированный атрибут JSON:

UPDATE
  book
SET
  properties = JSON_SET(
    properties,
    '$.title',
    'High-Performance Java Persistence, 2nd edition'
  )
WHERE
  isbn = '978-9730228236'

Вы увидите, что виртуальный столбец title соответствующим образом обновлен:

| id | isbn           | properties                                                     | title                                          |
|----|----------------|----------------------------------------------------------------|------------------------------------------------|
| 1  | 978-9730228236 |{                                                               | High-Performance Java Persistence, 2nd edition |
|    |                |   "price":44.99,                                               |                                                |
|    |                |   "title":"High-Performance Java Persistence, 2nd edition",    |                                                |
|    |                |   "author":"Vlad Mihalcea",                                    |                                                |
|    |                |   "reviews":[                                                  |                                                |
|    |                |   {                                                            |                                                |
|    |                |      "date":"2017-11-14",                                      |                                                |
|    |                |      "rating":5,                                               |                                                |
|    |                |      "review":"Excellent book to understand Java Persistence", |                                                |
|    |                |      "reviewer":"Cristiano"                                    |                                                |
|    |                |   },                                                           |                                                |
|    |                |   {                                                            |                                                |
|    |                |      "date":"2019-01-27",                                      |                                                |
|    |                |      "rating":5,                                               |                                                |
|    |                |      "review":"The best JPA ORM book out there",               |                                                |
|    |                |      "reviewer":"T.W"                                          |                                                |
|    |                |   },                                                           |                                                |
|    |                |   {                                                            |                                                |
|    |                |      "date":"2016-12-24",                                      |                                                |
|    |                |      "rating":4,                                               |                                                |
|    |                |      "review":"The most informative book",                     |                                                |
|    |                |      "reviewer":"Shaikh"                                       |                                                |
|    |                |   }                                                            |                                                |
|    |                |   ],                                                           |                                                |
|    |                |   "publisher":"Amazon"                                         |                                                |
|    |                |}                                                               |                                                |
|----|----------------|----------------------------------------------------------------|------------------------------------------------|

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

Вывод

Типы столбцов JSON могут быть очень полезны, особенно если вы внедряете журнал аудита .

Хотя добавление индекса GIN в столбцы JSON в MySQL запрещено, вы можете определить виртуальный столбец и добавить к нему индекс.

Однако вам необходимо убедиться, что атрибут JSON, который вы используете для индексирования, является высокоселективным, так как в противном случае компонент database engine может не использовать индекс при создании плана выполнения SQL.