Простой запрос MariaDB становится очень медленным - PullRequest
0 голосов
/ 01 августа 2020

У меня есть этот простой запрос

SELECT  item_code, stock_value, name, warehouse
    FROM  `tabStock Ledger Entry` sle
    WHERE  posting_date <= '2020-08-01'
      AND  warehouse = 'bom'
    ORDER BY  timestamp(posting_date, posting_time) DESC,
              Creation DESC 

Это занимает почти 3 секунды для 1000 записей ... без предложения Order By требуется менее 1 se c для запроса всей таблицы. в самой таблице в настоящее время содержится более 1 млн записей.

show processlist всегда застревает в состоянии создания индекса сортировки.

Запрос объяснения показал:

+------+-------------+-------+------+------------------------------+-----------+---------+-------+--------+----------------------------------------------------+
| id   | select_type | table | type | possible_keys                | key       | key_len | ref   | rows   | Extra                                              |
+------+-------------+-------+------+------------------------------+-----------+---------+-------+--------+----------------------------------------------------+
|    1 | SIMPLE      | sle   | ref  | posting_sort_index,warehouse | warehouse | 563     | const | 127740 | Using index condition; Using where; Using filesort |
+------+-------------+-------+------+------------------------------+-----------+---------+-------+--------+----------------------------------------------------+

индексы таблицы следующим образом:

   +-----------------------+------------+-------------------------------+--------------+--------------+--
| Table                 | Non_unique | Key_name                      | Seq_in_index | Column_name  | C
+-----------------------+------------+-------------------------------+--------------+--------------+--
| tabStock Ledger Entry |          0 | PRIMARY                       |            1 | name         | A
| tabStock Ledger Entry |          1 | item_code                     |            1 | item_code    | A
| tabStock Ledger Entry |          1 | parent                        |            1 | parent       | A
| tabStock Ledger Entry |          1 | posting_sort_index            |            1 | posting_date | A
| tabStock Ledger Entry |          1 | posting_sort_index            |            2 | posting_time | A
| tabStock Ledger Entry |          1 | posting_sort_index            |            3 | name         | A
| tabStock Ledger Entry |          1 | voucher_no_voucher_type_index |            1 | voucher_no   | A
| tabStock Ledger Entry |          1 | voucher_no_voucher_type_index |            2 | voucher_type | A
| tabStock Ledger Entry |          1 | warehouse                     |            1 | warehouse    | A
| tabStock Ledger Entry |          1 | warehouse                     |            2 | posting_date | A
+-----------------------+------------+-------------------------------+--------------+--------------+--

Добавлен индекс для (дата_поста, склад) или (склад, дата_поста), но это не помогает .. похоже, что запрос всегда использует сортировку файлов

innodb_buffer_pool_size - 2Гб. .из 4G RAM. Я думаю, этого достаточно ... это не мега гигантская база данных

SHow Create table следующим образом:

+-----------------------+---------------------------------------------------------------
| Table                 | Create Table
+-----------------------+---------------------------------------------------------------
| tabStock Ledger Entry | CREATE TABLE `tabStock Ledger Entry` (
  `name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
  `creation` datetime(6) DEFAULT NULL,
  `modified` datetime(6) DEFAULT NULL,
  `modified_by` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `owner` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `docstatus` int(1) NOT NULL DEFAULT 0,
  `parent` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `parentfield` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `parenttype` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `idx` int(8) DEFAULT NULL,
  `actual_qty` decimal(18,6) NOT NULL DEFAULT 0.000000,
  `warehouse` varchar(140) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `qty_after_transaction` decimal(18,6) NOT NULL DEFAULT 0.000000,
  `fiscal_year` varchar(140) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `company` varchar(140) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `serial_no` longtext COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `incoming_rate` decimal(18,6) NOT NULL DEFAULT 0.000000,
  `stock_queue` text COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `item_code` varchar(140) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `voucher_detail_no` varchar(140) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `project` varchar(140) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `stock_value_difference` decimal(18,6) NOT NULL DEFAULT 0.000000,
  `stock_uom` varchar(140) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `voucher_type` varchar(140) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `batch_no` varchar(140) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `valuation_rate` decimal(18,6) NOT NULL DEFAULT 0.000000,
  `posting_date` date DEFAULT NULL,
  `voucher_no` varchar(140) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `stock_value` decimal(18,6) NOT NULL DEFAULT 0.000000,
  `is_cancelled` varchar(140) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `posting_time` time(6) DEFAULT NULL,
  `outgoing_rate` decimal(18,6) NOT NULL DEFAULT 0.000000,
  `_comments` text COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `_liked_by` text COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `_assign` text COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `_user_tags` text COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `to_rename` int(1) NOT NULL DEFAULT 1,
  PRIMARY KEY (`name`),
  KEY `item_code` (`item_code`),
  KEY `parent` (`parent`),
  KEY `posting_sort_index` (`posting_date`,`posting_time`,`name`),
  KEY `voucher_no_voucher_type_index` (`voucher_no`,`voucher_type`),
  KEY `warehouse` (`warehouse`,`posting_date`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=COMPRESSED
+-----------------------+---------------------------------------------------------------

БОЛЬШЕ

Я удалил функцию отметки времени и закажите только posting_date .. запустите запрос из консоли mariadb .. наконец, потребовалось всего <2 se c, чтобы загрузить более 200 000 результирующих строк. </p>

Но mariadb-slow.log показал мне по-другому, если бы я запустил тот же запрос из веб-приложения .. он взял> 4se c для того же результата. Что может вызвать это?

# User@Host: _5839b22099d630d5[_5839b22099d630d5] @ localhost [127.0.0.1]
# Thread_id: 685  Schema: _5839b22099d630d5  QC_hit: No
# Query_time: 4.101652  Lock_time: 0.000055  Rows_sent: 226657  Rows_examined: 226657
# Rows_affected: 0
SET timestamp=1596384652;
SELECT item_code, stock_value, name, warehouse
                FROM `tabStock Ledger Entry` sle
                WHERE posting_date <= '2020-08-02'  AND warehouse = 'bom'
                ORDER BY posting_date DESC;

ОБЪЯСНЕНИЕ В JSON

{
  "query_block": {
    "select_id": 1,
    "table": {
      "table_name": "sle",
      "access_type": "range",
      "possible_keys": [
        "warehouse",
        "posting_date",
        "posting_sort_index",
        "warehouse_2"
      ],
      "key": "warehouse_2",
      "key_length": "567",
      "used_key_parts": ["warehouse", "posting_date"],
      "rows": 126605,
      "filtered": 100,
      "attached_condition": "sle.warehouse <=> 'bom' and sle.posting_date <= '2020-08-02' and sle.warehouse = 'bom'"
    }
  }

1 Ответ

0 голосов
/ 01 августа 2020

Добавьте этот составной индекс:

INDEX(warehouse, posting_date)

(И, если у вас в настоящее время есть INDEX(warehouse), отбросьте его.)

Для дальнейшего обсуждения укажите SHOW CREATE TABLE и полный EXPLAIN в тексте (не на изображении).

Подробнее о создании индекса: http://mysql.rjweb.org/doc.php/index_cookbook_mysql

Время:

  • Могли быть различия в кешировании. При запуске таймингов запустите запрос дважды. Первый может выполнять операции ввода-вывода, в которых второй не нуждается.

  • Когда он использовал индекс, ему нужно было 1000 раз обратиться к BTree данных. Эти случайные выборки могли быть попаданиями на диск.

  • Другое ... Какое значение innodb_buffer_pool_size; это таблица InnoDB; сколько у вас оперативной памяти; Насколько велика (в ГБ) таблица?

Подробнее

timestamp(posting_date, posting_time) DESC, Creation DESC

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

posting_date DESC, posting_time DESC, Creation DESC

В общем, неуклюже разбивать datetime на два столбца, и гораздо проще вытаскивать части из DATETIME (или TIMESTAMP) при необходимости. Я рекомендую объединить posting_date и posting_time в один DATETIME(6).

После этого я рекомендую добавить

INDEX(warehouse, posting_datetime)

Внимание: posting_date <= '2020-08-01' включает в себя все 1 августа. Posting_datetime <= '2020-08-01' включает только <code>microsecond of Aug 1. If you meant to exclude all of Aug 1, then change <= <code>to <`; тогда оба варианта работают «правильно». </p>

Оптимизатор может предпочесть этот индекс, который я рекомендовал.

2G..из 4G RAM

Верно; убедитесь, что вы не меняете местами.

1000 записей

Что вы делаете с 1000 записей? Это много для веб-пользователя.

без улучшений только 0,1 se c для запроса всей таблицы

Это озадачивает. Укажите EXPLAIN FORMAT=JSON SELECT ... - это должно предоставить дополнительную информацию.

More-take 2

Rows_sent: 226657 Rows_examined: 226657

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

Есть еще кое-что ...

  • Я думаю, что он избегает сортировки, но в EXPLAIN этого нет деталь. Не могли бы вы запустить ANALYZE FORMAT=JSON SELECT ... и попробовать с «трассировкой оптимизатора» (если она доступна в вашей версии).

  • Размер таблицы относительно buffer_pool_size еще предстоит изучить. С 2G buffer_pool и таблицей, которая может быть примерно такого размера, я беспокоюсь, что запрос привязан к вводу / выводу.

Чтобы догнать этот последний элемент, есть две возможности. Каждый будет стремиться помочь этому одному запросу, не обязательно помогая другим. Этот запрос почему-то «более важен»?

  • Индекс покрытия: INDEX(warehouse, posting_date, item_code, stock_value, name). (Если вы попробуете это, введите EXPLAIN, чтобы подтвердить, что этот индекс используется.)

  • Измените PRIMARY KEY и мой индекс на:

      PRIMARY KEY(warehouse, posting_date, item_code, stock_value, name),
      UNIQUE(name)
    

Текущий ПК (имя) все еще индексируется, и его уникальность подтверждает, что мой ПК по-прежнему будет уникальным. Но он также будет «кластеризовать» данные, так что запрос не будет прыгать по таблице.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...