Поиск активных записей периодически замедляется, несмотря на наличие индекса в столбце where - PullRequest
0 голосов
/ 09 октября 2019

У меня есть простая новая конечная точка API, которая включает в себя запрос моей новой установочной и заполненной таблицы - inventory_products.

Схема таблицы inventory_products:

CREATE TABLE `inventory_products` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `inventory_product_id` binary(16) DEFAULT NULL,
  `product_id` binary(16) DEFAULT NULL,
  `status` enum('ACTIVE','INACTIVE','DELETED') DEFAULT 'ACTIVE',
  `priority` int(11) DEFAULT '0',
  `inventory_service_id` tinyint(3) DEFAULT NULL,
  `created_at` datetime DEFAULT NULL,
  `updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  UNIQUE KEY `uc_inventory_product_id` (`inventory_product_id`),
  KEY `idx_product_status` (`product_id`,`status`)
) ENGINE=InnoDB AUTO_INCREMENT=76312817 DEFAULT CHARSET=utf8;

Конечная точка API в основном делаетниже:

def lookup_inventory_service_id
    return render_error_response(AUTH_ERROR, {status: 403}) unless client.name == PERMITTED_NAME

    ip_fetch_start = Time.now
    inventory_product = InventoryProducts.find_by_inventory_product_id(resource_attributes[:inventory_product_id])
    Rails.logger.info({inventory_product_id: resource_attributes[:inventory_product_id], inventory_product_fetch_time_ms: (Time.now - ip_fetch_start)*1000}.as_json)
    return head :not_found unless inventory_product
....

Проблема: поиск инвентаризации_продукта (find_by_inventory_product_id) - это стандартная функция, предоставляемая ActiveRecord Rails (я не перезаписывал ее в своей модели). Эта функция занимает от 10 мс, а иногда и 650 мс (об этом я узнал из журналов, которые я добавил)Почему в некоторых случаях это занимает так много времени, а в других - меньше, несмотря на наличие индекса Mysql для столбца, используемого при поиске?

Я упомянул опись_инвент_продукта как уникальный ключ вмоя схема и MySQL-запрос, запущенный вышеупомянутой функцией, используют inventory_product_id в качестве индекса из приведенного ниже оператора объяснение .

SELECT  inventory_products.*
    FROM  inventory_products
    WHERE  inventory_products.inventory_product_id = 0x3a288cdce78d44618eadd72e240f26a4
    LIMIT  1

 id select_type      table       type  key                key_len ref rows Extra
 1     SIMPLE inventory_products const uc_inventory_product_id 17 const  1 NULL

Что-то не так в моей схеме? Должен ли я явно упоминать в схеме запас_продукта как индекс mysql? Что-то вроде :

KEY `idx_product_status` (`product_id`,`status`)

Любая помощь очень ценится, так как я долго борюсь с этой проблемой и не могу найти решение. Заранее спасибо !!

Я использую Mysql 5.6 и рельсы - 3.2.8. Кроме того, мое приложение rails работает на сервере Tomcat (версия - Apache Tomcat / 7.0.16) внутри jruby (1.7.0) /

Ответы [ 3 ]

0 голосов
/ 09 октября 2019

Возможно, вам придется поискать следующие вещи, чтобы понять, почему производительность индекса низкая.

  • Высокая загрузка и меньше свободной памяти. Вы можете легко получить эти данные в консоли AWS, если вы используете RDS. Ваша индексная таблица может постоянно выгружаться из основной памяти из-за недостатка памяти. Увеличение объема ОЗУ экземпляра или переосмысление индексов, используемых в приложении, решит эту проблему - ref
  • Увеличение глубины очереди. Ваш экземпляр может использовать IOPS на максимальном уровне, а запросы могут сокращаться, и, следовательно, увеличиваться глубина очереди - проверьте баланс пакета в консоли RDS для получения дополнительной информации - увеличение размера экземпляра или выбор положения IOPS решит эту проблему
  • Масштаб записей в таблицу - если таблица принимает записи с высокой скоростью, поскольку таблицы индексов необходимо обновлять (также учитывая блокировку, необходимую для обеспечения уникальности для столбца с уникальным индексом) при вставке или обновлении, необходимо проверитьмасштаб, в котором производится запись в эту таблицу. Если это проблема, горизонтальное масштабирование может быть решением, при котором вы читаете из нескольких реплик чтения, но записываете только в мастер
0 голосов
/ 16 октября 2019

У вас есть UNIQUE(inventory_product_id), что является оптимальным индексом для вашего запроса.

Даже в холодной системе для извлечения нужной строки потребуется не более 10 обращений к диску. Это может занять 100 мс на жестком диске;быстрее на SSD. В нормально работающей системе большинство блоков будет кэшироваться в buffer_pool, поэтому 100 мс будут редкими.

Если что-то еще происходит в таблице, могут возникнуть помехи в блокировках, ЦП, I /O и т. Д. Тем не менее, это растяжение, чтобы увидеть 630ms. Что такое среднее ?

Двоичный код (16) подразумевает, что вы используете какой-то хэш или UUID? Это означает, что кэширование не очень полезно, следовательно, ваш минимум 10 мс.

Какое значение innodb_buffer_pool_size? Сколько у вас оперативной памяти? Какая версия MySQL?

0 голосов
/ 09 октября 2019

Я не вижу индекса в вашем файле схемы (я думаю, что это файл схемы?), Просто ссылка на уникальный ключ

Двойную проверку легко выполнить, просто запустите rails g migration addIndexToInventoryProductIds. Это создаст файл миграции, который вы можете заполнить:

class addIndexToInventoryProductIds < ActiveRecord::Migration[5.2]

  def up
    add_index :inventory_products, :inventory_product_id
  end

  def down
    remove_index :inventory_products, :inventory_product_id
  end
end

После этого запустите rake db:migrate - если не произойдет ошибка, индекс там не существует.

...