MySQL FIFO固定最大大小

时间:2012-02-03 20:19:21

标签: java mysql fifo

我需要创建一个包含X个唯一值的列表,每个值代表一个资源。可能有数百万这些独特的价值观。

当应用程序需要资源时,它将使用最低的id(而不是值)取出值,使用资源,一旦完成资源,通过将值添加到列表的尾部将值返回到列表。重要的是将资源置于尾部以确保不会很快再次使用。

如果服务器出现故障,为了防止系统失去对值的跟踪,将使用的值添​​加到简单的in_use_value表中。

我的解决方案是在MySQL中创建一个循环FIFO列表。 我有一个表,其中包含列表中的最大元素数,一个用于跟踪列表前面的header_id字段,以及一个用于跟踪结尾的tail_id。标头和尾部id字段使用modula包装。

示例:

values_table

id INT(10)unsigned 值VARCHAR(45)

control_table

max_num_values INT(10) header_id tail_id

问题是,为了删除或插入值,我需要锁定控制表行以安全地更改tail / header id字段。 由于应用程序是多线程的,因此控制表上会有很多争用。

有没有人对我是否正确接近这个有另一种想法或建议?我已经考虑过将表分成多个较小的循环FIFO列表,方法是将控制表分成映射到每个列表的多行,但我不知道如何从控制表中获取随机行,而行中的一行被锁定了。

任何建议都将不胜感激。

2 个答案:

答案 0 :(得分:0)

我建议您查看java.util.concurrent包。你的方法很好,但你需要使用一个并发队列,以便它与线程很好地配合。

答案 1 :(得分:0)

假设您坚持将其持久化到数据库(并忽略java方面):

你说这一切都错了。首先,请记住,根据定义,SQL是 un-ordered 。这意味着没有“顶部”或“底部”(指标使它看起来如此,但这不是真的)。​​

你需要几张桌子:

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或其他东西中维护实例) - 首先,如果编码正确,它允许您恢复过程数据。

您现在可以通过首先插入记录来“检出”您的资源,然后查询签出到您的流程的记录(如果您的版本支持“数据更改表参考”,则一次性查看):

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实例上运行(尽管没有负载) 如果进程已经有一个签出行,该语句也可以修改为 not 插入一行。