Эмуляция безопасной транзакции SEQUENCE в MySQL
Мы много используем MySQL с механизмом хранения и транзакциями InnoDB, и у нас возникла проблема: нам нужен хороший способ эмуляции Oracle SEQUENCE в MySQL. Требования:
- поддержка concurrency
- безопасность транзакций
- максимальная производительность (что означает минимизацию блокировок и взаимоблокировок)
Нам все равно, если некоторые из значений не будут использоваться, т.е. пробелы в порядке. Существует простой способ архивирования, создавая отдельную таблицу InnoDB со счетчиком, однако это означает, что он примет участие в транзакции и будет вводить блокировки и ждать. Я собираюсь попробовать таблицу MyISAM с ручными замками, любыми другими идеями или передовыми методами?
Ответы
Ответ 1
Если автоинкремент недостаточно хорош для ваших нужд, вы можете создать механизм атомной последовательности с n именованными последовательностями, например:
Создайте таблицу для хранения ваших последовательностей:
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 фактически определяется при выборе select.
Ответ 2
Правильный способ сделать это указан в руководстве MySQL:
UPDATE child_codes SET counter_field = LAST_INSERT_ID(counter_field + 1);
SELECT LAST_INSERT_ID();
Ответ 3
Мы являемся компанией с высокими транзакционными играми и нуждаемся в таких решениях для наших нужд. Одной из особенностей последовательностей 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";
Ответ 4
Не будет ли столбец идентификатора MySQL в таблице?
CREATE TABLE имя_таблицы
( id INTEGER AUTO_INCREMENT ПЕРВИЧНЫЙ КЛЮЧ
)
Или вы хотите использовать его для чего-то другого, кроме как вставки в другую таблицу?
Если вы пишете, используя также процедурный язык (вместо простого SQL), то другой вариант должен состоять в создании таблицы, содержащей одно целое (или длинное целое) значение и хранимую процедуру, которая блокировала его, выбранную из он, увеличил его и разблокировал, прежде чем возвращать значение.
(Примечание. Всегда возвращайте значение до того, как вы вернете значение. Это максимизирует вероятность того, что вы не получите дубликатов, если есть ошибки, или оберните все это в транзакции.)
Затем вы вызывали бы это независимо от основной вставки/обновления (чтобы он не попадал в какие-либо транзакции, автоматически созданные механизмом вызова), а затем передавайте его как параметр в любом месте, где вы хотите его использовать.
Поскольку он не зависит от остальной части материала, который вы делаете, он должен быть быстрым и избегать проблем с блокировкой. Даже если вы видели ошибку, вызванную блокировкой (маловероятно, если вы не перегружаете базу данных), вы можете просто называть ее вторым/третьим временем.