Эмуляция безопасной транзакций SEQUENCE в MySQL
-
03-07-2019 - |
Вопрос
Мы часто используем MySQL с механизмом хранения и транзакциями InnoDB и столкнулись с проблемой:нам нужен хороший способ эмулировать ПОСЛЕДОВАТЕЛЬНОСТИ Oracle в MySQL.Требования:- Поддержка параллелизма - Безопасность транзакций - максимальная производительность (что означает минимизация замков и тупиков)
Нас не волнует, если некоторые значения не будут использоваться, т.е.пропуски в последовательности — это нормально.Существует простой способ добиться этого, создав отдельную таблицу InnoDB со счетчиком, однако это означает, что она будет участвовать в транзакции и введет блокировки и ожидание.Я подумываю попробовать стол MyISAM с ручными замками, есть ли еще идеи или рекомендации?
Решение
Если автоинкремент недостаточно хорош для ваших нужд, вы можете создать механизм атомарной последовательности с помощью н именованные последовательности следующим образом:
Создайте таблицу для хранения ваших последовательностей:
CREATE TABLE sequence (
seq_name varchar(20) unique not null,
seq_current unsigned int not null
);
Предполагая, что у вас есть строка для «foo» в таблице, вы можете атомарно получить идентификатор следующей последовательности следующим образом:
UPDATE sequence SET seq_current = (@next := seq_current + 1) WHERE seq_name = 'foo';
SELECT @next;
Никаких замков не требуется.Оба оператора должны выполняться в одном сеансе, чтобы локальная переменная @next фактически определялась, когда происходит выбор.
Другие советы
Как это сделать, описано в Руководство по MySQL:
UPDATE child_codes SET counter_field = LAST_INSERT_ID(counter_field + 1);
SELECT LAST_INSERT_ID();
Мы являемся игровой компанией с высоким уровнем транзакций, и нам нужны такого рода решения для наших нужд.Одной из особенностей последовательностей Oracle было также значение приращения, которое также можно было установить.
В решении используется DUPLICATE KEY
.
CREATE TABLE sequences (
id BIGINT DEFAULT 1,
name CHAR(20),
increment TINYINT,
UNIQUE KEY(name)
);
Чтобы получить следующий индекс:
Абстрагируйте следующее с помощью хранимой процедуры или функции sp_seq_next_val(VARCHAR)
:
INSERT INTO sequences (name) VALUES ("user_id") ON DUPLICATE KEY UPDATE id = id + increment;<br/>
SELECT id FROM sequences WHERE name = "user_id";
Разве столбец MySQL Identity в таблице не справится с этим?
Create Table Table_Name (ID Integer Auto_Increment Первичный ключ)
Или вы хотите использовать его для чего-то другого, а не просто для вставки в другую таблицу?
Если вы также пишете, используя процедурный язык (вместо просто SQL), тогда другим вариантом будет создание таблицы, содержащей одно целое (или длинное целое) значение, и хранимую процедуру, которая ее блокирует, выбирает из нее, увеличивает его и разблокировал, прежде чем вернуть значение.
(Примечание: всегда увеличивайте значение перед возвратом значения — это максимизирует вероятность отсутствия дубликатов в случае ошибок — или оберните все это в транзакцию.)
Затем вы должны вызвать это независимо от вашей основной вставки/обновления (чтобы оно не попадало в какие-либо транзакции, автоматически созданные механизмом вызова), а затем передать его в качестве параметра туда, где вы хотите его использовать.
Поскольку это не зависит от остальных действий, которые вы делаете, оно должно выполняться быстро и избегать проблем с блокировкой.Даже если вы увидели ошибку, вызванную блокировкой (маловероятно, если вы не перегружаете базу данных), вы можете просто вызвать ее второй или третий раз.