Запросы UPDATE в ваших предыдущих двух вопросах ( вопрос 1 , вопрос 2 ) попадают в таблицу «people» с помощью PRIMARY KEY с блокировкой на уровне строк. Это то, что я сказал еще в вопросе 1 6 июня 2011 г. 10:03
Все транзакции проходят первичный ключ. Поскольку PRIMARY является кластеризованным индексом в InnoDB, ключ PRIMARY и сама строка находятся вместе. Таким образом, прохождение строки и и ПЕРВИЧНЫЙ КЛЮЧ - это одно и то же. Следовательно, любая блокировка индекса на PRIMARY KEY также является блокировкой на уровне строк.
Что-то еще не было рассмотрено, что может приписать медлительность индексам: использование индексов NON-UNIQUE в InnoDB. Каждый индексированный поиск в InnoDB, использующий неуникальные индексы, также имеет идентификатор строки каждой строки, присоединенной к неуникальному ключу. RowID в основном происходит из кластерного индекса . Обновление неуникальных индексов ДОЛЖНО ВСЕГДА взаимодействовать с кластерным индексом, ДАЖЕ ЕСЛИ В ТАБЛИЦЕ НЕ ИМЕЕТ ПЕРВИЧНОГО КЛЮЧА.
Еще одна вещь, о которой стоит подумать, это процесс управления узлами BTREE в индексе. Иногда требуется разделение страниц по узлам. Все записи в узле BTREE неуникальных индексов содержат неуникальные поля ПЛЮС rowID внутри кластерного индекса. Чтобы должным образом смягчить разбиение таких страниц BTREE без нарушения целостности данных, строка, связанная с rowID, должна иметь внутреннюю блокировку на уровне строки.
Если таблица «people» имеет много неуникальных индексов, подготовьтесь к тому, чтобы большое количество страниц индекса в табличном пространстве, а также крошечные маленькие строки блокировок время от времени подкрадывались к вам.
Есть еще один фактор, который не так очевиден: ключевое население
Иногда, когда индекс заполняется, значения ключей, составляющие индексы, могут со временем становиться однобокими и приводить к тому, что MySQL Query Optimizer переключается с поиска по ключевым словам на сканирование индекса и, наконец, на полное сканирование таблицы. То, что вы не можете контролировать, если вы не перепроектируете таблицу с новыми индексами, чтобы компенсировать однобокость ключей. Пожалуйста, предоставьте структуру таблицы для таблицы people, количество таблиц people и выходные данные show indexes для таблицы people .
Даже если в запросах используется только PRIMARY KEY, однобокость ключей в неуникальных индексах все еще требует балансировки BTREE и разделения страниц. Такое управление BTREE приведет к заметному замедлению из-за периодических блокировок на уровне строк, которые вы не собирались делать.
ОБНОВЛЕНИЕ 2011-06-14 22:19
Запросы из вопроса 1
UPDATE people SET company_id = 1610, name = '<name>', password = '<hash>',
temp_password = NULL, reset_password_hash = NULL, email = '<redacted>@yahoo.com',
phone = NULL, mobile = '<phone>', iphone_device_id = 'android:<id>-<id>',
iphone_device_time = '2011-06-06 05:35:09', last_checkin = '2011-06-06 05:24:42',
location_lat = <lat>, location_long = -<lng>, gps_strength = 3296,
picture_blob_id = 1190,
authority = 1, active = 1, date_created = '2011-04-13 20:21:20',
last_login = '2011-06-06 05:35:09', panic_mode = 0,
battery_level = NULL, battery_state = NULL WHERE people_id = 3125
UPDATE people SET company_id = 1610, name = '<name>', password = '<hash>',
temp_password = NULL, reset_password_hash = NULL, email = '<redacted>@yahoo.com',
phone = NULL, mobile = '<phone>', iphone_device_id = 'android:<id>-<id>-<id>-<id>',
iphone_device_time = '2011-06-06 05:24:42', last_checkin = '2011-06-06 05:35:07',
location_lat = <lat>, location_long = -<lng>, gps_strength = 3296,
picture_blob_id = 1190,
authority = 1, active = 1, date_created = '2011-04-13 20:21:20',
last_login = '2011-06-06 05:35:09', panic_mode = 0,
battery_level = NULL, battery_state = NULL WHERE people_id = 3125
Представьте себе последовательность событий
- Найдите строку по ПЕРВИЧНОМУ КЛЮЧУ
- Блокировка строки и кластеризованного индекса
- Создать данные MVCC для всех обновляемых столбцов
- Индексируются четыре столбца (email, company_id, iphone_device_id, picture_blob_id)
- Каждый индекс требует управления BTREE
- В одном и том же пространстве транзакций шаги 1-5 пытаются повторить в одной и той же строке, обновляя одни и те же столбцы (отправьте одинаковые сообщения в обоих запросах, одинаковые company_id в обоих запросах, одинаковые picture_blob_id в обоих запросах, разные iphone_device_id)
Запросы из вопроса 2
UPDATE people SET iphone_device_id=NULL
WHERE iphone_device_id='iphone:<device_id_blah>' AND people_id<>666;
UPDATE people SET company_id = 444, name = 'Dad', password = '<pass>',
temp_password = NULL, reset_password_hash = NULL, email = '<redacted>@gmail.com',
phone = NULL, mobile = NULL, iphone_device_id = 'iphone:<device_id_blah>',
iphone_device_time = '2011-06-06 19:12:29', last_checkin = '2011-06-07 02:49:47',
location_lat = <lat>, location_long = <lng>, gps_strength = 66,
picture_blob_id = 1661,
authority = 1, active = 1, date_created = '2011-03-20 19:18:34',
last_login = '2011-06-07 11:15:01', panic_mode = 0, battery_level = 0.55,
battery_state = 'unplugged' WHERE people_id = 666;
Эти два запроса еще более запутаны, поскольку первый запрос обновляет все, кроме people_id 666. Сотни строк мучительно блокируются только первым запросом. Второй запрос обновляет people_id 666, запустив 5 последовательность событий. Первый запрос выполняет те же 5 последовательностей событий в каждой задействованной строке, кроме people_id 666, но индекс для iphone_device_id находится на курсе с двумя разными запросами. Кто-то должен блокировать страницы BTREE в порядке очереди.
Перед лицом этих двух пар запросов на курсе коллизий возможно блокирование одних и тех же BTREE-страниц в одном индексе может быть мучительным опытом для InnoDB или любой ACID-совместимой СУБД. Таким образом, замедление индекса является уделом этих пар запросов, если только вы не можете гарантировать, что запросы выполняются с AUTOCOMMIT = 1 или разрешением грязного чтения (хотя подобные коллизии делают READ-COMMITTED и READ-UNCOMMITED кошмаром для MVCC).
ОБНОВЛЕНИЕ 2011-06-15 10:29
@RedBlueThing: В запросах из вопроса 2 первый запрос является запросом диапазона, поэтому достигается много блокировок строк. Также обратите внимание, что оба запроса пытаются заблокировать одну и ту же страницу идентификатора пространства 0, а не 4611, а n бит 152 блокируется в ПЕРВИЧНОМ КЛЮЧЕ, он же кластерный индекс.
Чтобы убедиться, что ваше приложение работает, по крайней мере, на основе ожидаемой вами серии событий, есть два варианта, которые вы можете попробовать:
Вариант 1) Преобразовать эту таблицу в MyISAM (по крайней мере, на сервере разработки). Каждое UPDATE, INSERT и DELETE будет навязывать полную блокировку таблицы в порядке очереди.
Вариант 2) Попробуйте использовать уровень изоляции SERIALIZABLE . Это заблокирует все предполагаемые строки в режиме SHARED.
Последовательность событий, которую вы ожидаете, будет либо нарушена, либо будет успешной при использовании этих двух альтернативных вариантов. Если оба эти параметра не сработают, вам нужно будет просмотреть свое приложение и определить приоритетность порядка выполнения ваших запросов. Установив этот приоритет, вы можете просто отменить эти параметры (для варианта 1 вернитесь к InnoDB, для варианта 2 вернитесь к уровню изоляции по умолчанию [прекратить использование SERIALIZABLE]).