2009-04-30 4 views
10

Мы много используем MySQL с механизмом хранения и транзакциями InnoDB, и у нас возникла проблема: нам нужен хороший способ эмуляции SEQUENCE в Oracle. Требования: - параллелизм поддержка - безопасность транзакций - максимальная производительность (что означает сведение к минимуму замки и тупиков)Эмуляция безопасной транзакции SEQUENCE в MySQL

Мы не волнует, если некоторые из значений не будет использоваться, то есть пробелы в последовательности в порядке. Существует простой способ архивирования, создавая отдельную таблицу InnoDB со счетчиком, однако это означает, что он примет участие в транзакции и будет вводить блокировки и ждать. Я собираюсь попробовать таблицу MyISAM с ручными замками, любыми другими идеями или передовыми методами?

ответ

14

Если автоинкрементный не достаточно хорошо для ваших потребностей, вы можете создать атомный механизм последовательности с п названных последовательностями, такими как это:

Создать таблицу для хранения последовательности:

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.

+0

Спасибо - это более или менее то, что мы придумали после всех - хранимая процедура, которая делает почти это. –

+1

В случае, если запись не существует, используйте этот запрос. 'INSERT INTO sequence (seq_name, seq_current) VALUES ('foo', (@next: = 1)) ON DUPLICATE KEY UPDATE seq_current = (@next: = seq_current + 1);' then call 'SELECT @next;' – iwat

+0

вы можете объединить комментарий iwat с ответом qu1j0t3 в: 'INSERT INTO sequences2 (seq_name, seq_current) VALUES ('Foo', LAST_INSERT_ID (1)) дублированием KEY UPDATE seq_current = last_insert_id (seq_current + 1);' затем либо вызовите 'SELECT LAST_INSERT_ID();', либо если вы используете драйвер приложения, используйте встроенный вызов автоматического инкремента (например, 'Statement # getGeneratedKeys' в jdbc) – Alden

0

Не будет ли столбец идентификатора MySQL на столе?

CREATE TABLE table_name ( ID INTEGER AUTO_INCREMENT PRIMARY KEY )

Или вы хотите использовать его для чего-то другого, чем просто вставить в другую таблицу?

Если вы пишете, используя также процедурный язык (вместо простого SQL), то другим вариантом будет создание таблицы, содержащей одно целое (или длинное целое) значение и хранимую процедуру, которая его блокировала от него, увеличил его и разблокировал, прежде чем возвращать значение.

(Примечание - всегда приращение, прежде чем вернуть значение - это максимально шанс не получить дубликаты, если есть ошибки -. Или обернуть все это в сделке)

Вы бы тогда назвать это независимо от вашего основной вставкой/обновлением (чтобы он не попадал в транзакции, автоматически созданные механизмом вызова), а затем передавайте его как параметр в любом месте, где вы хотите его использовать.

Поскольку он не зависит от остального материала, который вы делаете, он должен быть быстрым и избегать проблем с блокировкой. Даже если вы видели ошибку, вызванную блокировкой (маловероятно, если вы не перегружаете базу данных), вы можете просто назвать ее второй/третий раз.

+0

Спасибо, ваш второй подход, вероятно, что я имел в виду более или менее , и детали помогают. Любые комментарии к тому, какой механизм хранения лучше всего подходит для такой таблицы? –

+0

Я не очень хорошо знаю MySQL, поэтому я не могу на это по-настоящему посодействовать (я сделал это на SQL Server). Я бы сказал, что я не думаю, что это так сложно, поэтому наилучшим подходом было бы использовать то, что вы используете для остальной части вашего материала, и сохранить сложность. –

9

Правильный способ сделать это дан в MySQL manual:

UPDATE child_codes SET counter_field = LAST_INSERT_ID(counter_field + 1); 
SELECT LAST_INSERT_ID(); 
+1

Я не вижу здесь логики. 'LAST_INSERT_ID' имеет смысл только для полей с автоматическим приращением. Но если он уже автоматически увеличивается, почему нужно вручную его увеличивать? – devios1

+0

Оператор обновления не будет влиять на LAST_INSERT_ID, если он не был установлен вручную. Это можно использовать в качестве трюка для получения данных из инструкции обновления без использования отдельной пользовательской переменной. –

+0

Этот подход правильный, и комментарий @devios вводит в заблуждение. 'LAST_INSERT_ID (expr)' не имеет ничего общего с автоматическим приращением. – baf

4

Мы являемся игровой компанией с высокими транзакциями и нуждаемся в таких решениях для наших нужд. Одной из особенностей последовательностей 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"; 
+0

Hm интересно, спасибо за указатель. Является ли ON DUPLICATE KEY всегда атомарным с INSERT в MySQL? –

+0

@Michael: Да, ON DUPLICATE KEY является атомарным. – kopos

Смежные вопросы