Оптимизация запроса mysql с полнотекстовым индексом и другими столбцами с индексами - PullRequest
0 голосов
/ 12 февраля 2020

Я выполняю следующий запрос для таблицы с 100 000+ строк. Я пытаюсь выяснить, как использовать полнотекстовый индекс и использовать индекс для других столбцов, которые к нему добавлены. (Таблица создания для phppos_items находится внизу)

SELECT name
FROM `phppos_items`
WHERE (MATCH (phppos_items.name, phppos_items.item_number, product_id, description)
       AGAINST ('"Search* "' IN BOOLEAN MODE)
          or phppos_items.item_id = 'Search')
  and phppos_items.deleted=0 and system_item = 0;

Это занимает 0,21 секунды; что в порядке; но он не полностью проиндексирован.

Вот объяснение (как вы можете видеть, оно проверяет 58 188 строк):

mysql> EXPLAIN SELECT name FROM `phppos_items` WHERE (MATCH (phppos_items.name, phppo                                                                          LEAN MODE) or phppos_items.item_id = 'Search') and phppos_items.deleted=0 and system_item = 0;
+----+-------------+--------------+------------+------+-------------------------------------+---------+---------+-------+-------+----------+-------------+
| id | select_type | table        | partitions | type | possible_keys                       | key     | key_len | ref   | rows  | filtered | Extra       |
+----+-------------+--------------+------------+------+-------------------------------------+---------+---------+-------+-------+----------+-------------+
|  1 | SIMPLE      | phppos_items | NULL       | ref  | PRIMARY,deleted,deleted_system_item | deleted | 4       | const | 58188 |     2.00 | Using where |
+----+-------------+--------------+------------+------+-------------------------------------+---------+---------+-------+-------+----------+-------------+

Если я изменю запрос на:

SELECT name
FROM `phppos_items`
WHERE (MATCH (phppos_items.name, phppos_items.item_number, product_id, description) AGAINST ('"Search* "' IN BOOLEAN MODE))

Это занимает 0,0005 секунд и полностью индексируется. См. Объяснение:

mysql> EXPLAIN SELECT name FROM `phppos_items` WHERE (MATCH (phppos_items.name, phppos_items.item_number, product_id, description) AGAINST ('"Search* "' IN BOOL
+----+-------------+--------------+------------+----------+---------------+-------------+---------+-------+------+----------+-----------------------------------+
| id | select_type | table        | partitions | type     | possible_keys | key         | key_len | ref   | rows | filtered | Extra                             |
+----+-------------+--------------+------------+----------+---------------+-------------+---------+-------+------+----------+-----------------------------------+
|  1 | SIMPLE      | phppos_items | NULL       | fulltext | full_search   | full_search | 0       | const |    1 |   100.00 | Using where; Ft_hints: no_ranking |
+----+-------------+--------------+------------+----------+---------------+-------------+---------+-------+------+----------+-----------------------------------+

Вот таблица создания:

mysql> show create table phppos_items;
+--------------+---------------------------------------------------------------------+
| Table        | Create Table                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
+--------------+---------------------------------------------------------------------+
| phppos_items | CREATE TABLE `phppos_items` (
  `name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `category_id` int(11) DEFAULT NULL,
  `supplier_id` int(11) DEFAULT NULL,
  `manufacturer_id` int(11) DEFAULT NULL,
  `item_number` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `product_id` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `ecommerce_product_id` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `ecommerce_product_quantity` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `description` text COLLATE utf8_unicode_ci NOT NULL,
  `size` varchar(255) COLLATE utf8_unicode_ci NOT NULL DEFAULT '',
  `tax_included` int(1) NOT NULL DEFAULT '0',
  `cost_price` decimal(23,10) NOT NULL,
  `unit_price` decimal(23,10) NOT NULL,
  `promo_price` decimal(23,10) DEFAULT NULL,
  `start_date` date DEFAULT NULL,
  `end_date` date DEFAULT NULL,
  `reorder_level` decimal(23,10) DEFAULT NULL,
  `expire_days` int(10) DEFAULT NULL,
  `item_id` int(10) NOT NULL AUTO_INCREMENT,
  `allow_alt_description` tinyint(1) NOT NULL,
  `is_serialized` tinyint(1) NOT NULL,
  `override_default_tax` int(1) NOT NULL DEFAULT '0',
  `is_ecommerce` int(1) DEFAULT '1',
  `is_service` int(1) NOT NULL DEFAULT '0',
  `is_ebt_item` int(1) NOT NULL DEFAULT '0',
  `commission_percent` decimal(23,10) DEFAULT '0.0000000000',
  `commission_percent_type` varchar(255) COLLATE utf8_unicode_ci DEFAULT '',
  `commission_fixed` decimal(23,10) DEFAULT '0.0000000000',
  `change_cost_price` int(1) NOT NULL DEFAULT '0',
  `disable_loyalty` int(1) NOT NULL DEFAULT '0',
  `deleted` int(1) NOT NULL DEFAULT '0',
  `last_modified` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `ecommerce_last_modified` timestamp NULL DEFAULT NULL,
  `tax_class_id` int(10) DEFAULT NULL,
  `replenish_level` decimal(23,10) DEFAULT NULL,
  `system_item` int(1) NOT NULL DEFAULT '0',
  `max_discount_percent` decimal(15,3) DEFAULT NULL,
  `max_edit_price` decimal(23,10) DEFAULT NULL,
  `min_edit_price` decimal(23,10) DEFAULT NULL,
  `custom_field_1_value` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `custom_field_2_value` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `custom_field_3_value` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `custom_field_4_value` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `custom_field_5_value` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `custom_field_6_value` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `custom_field_7_value` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `custom_field_8_value` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `custom_field_9_value` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `custom_field_10_value` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `required_age` int(10) DEFAULT NULL,
  `verify_age` int(1) NOT NULL DEFAULT '0',
  `weight` decimal(23,10) DEFAULT NULL,
  `length` decimal(23,10) DEFAULT NULL,
  `width` decimal(23,10) DEFAULT NULL,
  `height` decimal(23,10) DEFAULT NULL,
  `ecommerce_shipping_class_id` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `long_description` longtext COLLATE utf8_unicode_ci NOT NULL,
  `allow_price_override_regardless_of_permissions` int(1) DEFAULT '0',
  `main_image_id` int(10) DEFAULT NULL,
  `only_integer` int(1) NOT NULL DEFAULT '0',
  `is_series_package` int(1) NOT NULL DEFAULT '0',
  `series_quantity` int(10) DEFAULT NULL,
  `series_days_to_use_within` int(10) DEFAULT NULL,
  `is_barcoded` int(1) NOT NULL DEFAULT '1',
  `default_quantity` decimal(23,10) DEFAULT NULL,
  `disable_from_price_rules` int(1) DEFAULT '0',
  `last_edited` timestamp NULL DEFAULT NULL,
  `info_popup` text COLLATE utf8_unicode_ci,
  `item_inactive` int(1) DEFAULT '0',
  PRIMARY KEY (`item_id`),
  UNIQUE KEY `item_number` (`item_number`),
  UNIQUE KEY `product_id` (`product_id`),
  KEY `phppos_items_ibfk_1` (`supplier_id`),
  KEY `deleted` (`deleted`),
  KEY `phppos_items_ibfk_3` (`category_id`),
  KEY `phppos_items_ibfk_4` (`manufacturer_id`),
  KEY `phppos_items_ibfk_5` (`ecommerce_product_id`),
  KEY `description` (`description`(255)),
  KEY `size` (`size`),
  KEY `reorder_level` (`reorder_level`),
  KEY `cost_price` (`cost_price`),
  KEY `unit_price` (`unit_price`),
  KEY `promo_price` (`promo_price`),
  KEY `last_modified` (`last_modified`),
  KEY `name` (`name`),
  KEY `phppos_items_ibfk_6` (`tax_class_id`),
  KEY `deleted_system_item` (`deleted`,`system_item`),
  KEY `custom_field_1_value` (`custom_field_1_value`),
  KEY `custom_field_2_value` (`custom_field_2_value`),
  KEY `custom_field_3_value` (`custom_field_3_value`),
  KEY `custom_field_4_value` (`custom_field_4_value`),
  KEY `custom_field_5_value` (`custom_field_5_value`),
  KEY `custom_field_6_value` (`custom_field_6_value`),
  KEY `custom_field_7_value` (`custom_field_7_value`),
  KEY `custom_field_8_value` (`custom_field_8_value`),
  KEY `custom_field_9_value` (`custom_field_9_value`),
  KEY `custom_field_10_value` (`custom_field_10_value`),
  KEY `verify_age` (`verify_age`),
  KEY `phppos_items_ibfk_7` (`main_image_id`),
  KEY `item_inactive_index` (`item_inactive`),
  FULLTEXT KEY `full_search` (`name`,`item_number`,`product_id`,`description`),
  FULLTEXT KEY `name_search` (`name`),
  FULLTEXT KEY `item_number_search` (`item_number`),
  FULLTEXT KEY `product_id_search` (`product_id`),
  FULLTEXT KEY `description_search` (`description`),
  FULLTEXT KEY `size_search` (`size`),
  FULLTEXT KEY `custom_field_1_value_search` (`custom_field_1_value`),
  FULLTEXT KEY `custom_field_2_value_search` (`custom_field_2_value`),
  FULLTEXT KEY `custom_field_3_value_search` (`custom_field_3_value`),
  FULLTEXT KEY `custom_field_4_value_search` (`custom_field_4_value`),
  FULLTEXT KEY `custom_field_5_value_search` (`custom_field_5_value`),
  FULLTEXT KEY `custom_field_6_value_search` (`custom_field_6_value`),
  FULLTEXT KEY `custom_field_7_value_search` (`custom_field_7_value`),
  FULLTEXT KEY `custom_field_8_value_search` (`custom_field_8_value`),
  FULLTEXT KEY `custom_field_9_value_search` (`custom_field_9_value`),
  FULLTEXT KEY `custom_field_10_value_search` (`custom_field_10_value`),
  CONSTRAINT `phppos_items_ibfk_1` FOREIGN KEY (`supplier_id`) REFERENCES `phppos_suppliers` (`person_id`),
  CONSTRAINT `phppos_items_ibfk_3` FOREIGN KEY (`category_id`) REFERENCES `phppos_categories` (`id`),
  CONSTRAINT `phppos_items_ibfk_4` FOREIGN KEY (`manufacturer_id`) REFERENCES `phppos_manufacturers` (`id`),
  CONSTRAINT `phppos_items_ibfk_6` FOREIGN KEY (`tax_class_id`) REFERENCES `phppos_tax_classes` (`id`),
  CONSTRAINT `phppos_items_ibfk_7` FOREIGN KEY (`main_image_id`) REFERENCES `phppos_item_images` (`image_id`)
) ENGINE=InnoDB AUTO_INCREMENT=20000966 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci |
+--------------+-------------------------------------------------------------------------------------------------------------
1 row in set (0.00 sec)

1 Ответ

1 голос
/ 12 февраля 2020

Здесь существует серьезное несоответствие типов данных:

`item_id` int(10) NOT NULL AUTO_INCREMENT,
phppos_items.item_id = 'Search'

Особенно беспорядочно пытаться оптимизировать:

WHERE (MATCH (phppos_items.name, phppos_items.item_number, product_id, description)
       AGAINST ('"Search* "' IN BOOLEAN MODE)
          or phppos_items.item_id = 'Search')
  and phppos_items.deleted=0
  and system_item = 0;

По сути, единственный способ выполнить запрос - это проверить каждый строка таблицы. Кроме того, тесты FT любят находиться «на месте водителя», но это не позволяет.

Первый шаг - избавиться от OR:

( SELECT ...
    WHERE MATCH(..) AGAINST(..)
      AND phppos_items.deleted=0
      AND system_item = 0 )
UNION DISTINCT
( SELECT ...
    WHERE phppos_items.item_id = 'Search'
      AND phppos_items.deleted=0
      AND system_item = 0 )

Первый SELECT выполнит тест FT (очень быстро), затем отфильтрует все строки на основе 0-тестов.

Второй SELECT будет просто использовать PRIMARY KEY(item_id) (, если у вас нет опечатка! ) и проверка нумерации c item_id на ноль и, по-видимому, невозможности найти строки.

Затем UNION соберет два набора результатов, выполнит дедупликацию и очень быстро выдаст результаты .

(Превращение OR в UNION - это общая техника оптимизации; она кажется особенно полезной для вашего запроса.)

Мне редко бывает полезно иметь более 2 УНИКАЛЬНЫХ клавиш стол. Вы уверены, что у вас есть 3?

...