MySQL FIFO фиксированный максимальный размер - PullRequest
1 голос
/ 04 февраля 2012

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

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

Чтобы система не теряла отслеживание значений в случае сбоя сервера, используемые значения добавляются в простое in_use_valueТаблица.

Мое решение заключается в создании циклического списка FIFO в MySQL.У меня есть таблица, содержащая максимальное количество элементов в списке, поле header_id для отслеживания передней части списка и tail_id для отслеживания конца.Поля заголовка и идентификатора хвоста переносятся по модулю.

Пример:

values_table

id INT (10) значение без знака VARCHAR (45)

control_table

max_num_values ​​INT (10) header_id tail_id

Проблема в том, что для удаления или вставки значения мне нужно заблокировать строку управляющей таблицы, чтобы безопасно изменить поля идентификатора хвостового / заголовочного полей.Поскольку приложение является многопоточным, в контрольной таблице будет много споров.

У кого-нибудь есть альтернативная идея или совет относительно того, правильно ли я подхожу к этому?Я думал о разделении таблицы на несколько меньших циклических списков FIFO, разбивая контрольную таблицу на несколько строк, сопоставляя их с каждым списком, но я не знаю, как получить случайную строку из контрольной таблицы, пока строка является одной из строкзаблокированы.

Любой совет будет высоко ценится.

Ответы [ 2 ]

0 голосов
/ 04 февраля 2012

Предполагая, что вы настаиваете на сохранении его в своей базе данных (и игнорируете сторону Java):

Ты все делаешь неправильно. Прежде всего, помните, что SQL по определению неупорядочен . Это означает, что нет ни «верха», ни «низа» (это указывает на то, что это так, но это не совсем так).

Вам понадобится несколько таблиц:

Resource  -- List of resource ids - this is how you limit the count.
============
id  -- autoincrement int
allowed  -- boolean flag if this resource is 'present'.  
         -- This is *not* the 'checkout' flag.
natural_key  -- if necessary

Process  -- When a 'thread' spools up, you need to register it.
         -- This is to enable monitor jobs, to clean up/restart dead jobs.
============
id  -- autoincrement int
process_natural_key  -- What the process calls itself.


Checked_out
================
process_id -- fk to Process.id
resource_id  -- fk to Resource.id
checked_out_at -- timestamp, useful for reporting, not otherwise necessary.

Checkin_History
===================
resource_id  -- fk to Resource.id
checked_in_at  -- timestamp, necessary

Вставьте ваш набор ресурсов в Resource. Установите allowed как необходимый - он есть в случае, если определенный ресурс вообще недоступен (скажем, сервер отключен).
Каждый раз, когда задание становится ненужным, зарегистрируйте его в Process. Это отдельно от любой регистрации на стороне программы (например, ведение экземпляров в ExecutorPool или что-то) - с одной стороны, это позволяет вам восстановить данные процесса, если они закодированы правильно.

Теперь вы можете «проверить» свой ресурс, сначала вставив запись, а затем запросив извлеченную запись для вашего процесса (или сразу, если ваша версия поддерживает «DATA CHANGED TABLE REFERENCE's»):

INSERT INTO Checked_out (process_id, resource_id, checked_out_at) 
SELECT a.id, b.id, CURRENT_TIMESTAMP
FROM Process as a
JOIN (SELECT b.id, COALESCE(MAX(d.checked_in_at), 
                      TIMESTAMP('0001-01-01 00:00:00') + b.id SECONDS) as stamp
      FROM Resource as b
      LEFT JOIN Checked_out as c
      ON c.resource_id = b.id
      LEFT JOIN Checkin_History as d
      ON d.resource_id = c.resource_id
      WHERE c.resource_id IS NULL
      AND b.allowed = '1'
      GROUP BY b.id) as b
ON 1 = 1
LEFT JOIN (SELECT b.id, COALESCE(MAX(d.checked_in_at), 
                           TIMESTAMP('0001-01-01 00:00:00') + b.id SECONDS) as stamp
           FROM Resource as b
           LEFT JOIN Checked_out as c
           ON c.resource_id = b.id
           LEFT JOIN Checkin_History as d
           ON d.resource_id = c.resource_id
           WHERE c.resource_id IS NULL
           AND b.allowed = '1'
           GROUP BY b.id) as c
ON c.stamp > b.stamp    
WHERE a.process_natural_key = @programInput
AND c.id IS NULL
LIMIT 1

Это было проверено и работает на экземпляре DB2 (хотя и не под нагрузкой).
Оператор также можно изменить на , а не вставить строку, если у процесса уже есть строка извлечения.

0 голосов
/ 04 февраля 2012

Я бы порекомендовал взглянуть на пакет java.util.concurrent . Ваш подход хорош, но вам нужно использовать параллельную очередь, чтобы она хорошо сочеталась с потоками.

...