我需要创建一个包含X个唯一值的列表,每个值代表一个资源。可能有数百万这些独特的价值观。
当应用程序需要资源时,它将使用最低的id(而不是值)取出值,使用资源,一旦完成资源,通过将值添加到列表的尾部将值返回到列表。重要的是将资源置于尾部以确保不会很快再次使用。
如果服务器出现故障,为了防止系统失去对值的跟踪,将使用的值添加到简单的in_use_value表中。
我的解决方案是在MySQL中创建一个循环FIFO列表。 我有一个表,其中包含列表中的最大元素数,一个用于跟踪列表前面的header_id字段,以及一个用于跟踪结尾的tail_id。标头和尾部id字段使用modula包装。
示例:
id INT(10)unsigned 值VARCHAR(45)
max_num_values INT(10) header_id tail_id
问题是,为了删除或插入值,我需要锁定控制表行以安全地更改tail / header id字段。 由于应用程序是多线程的,因此控制表上会有很多争用。
有没有人对我是否正确接近这个有另一种想法或建议?我已经考虑过将表分成多个较小的循环FIFO列表,方法是将控制表分成映射到每个列表的多行,但我不知道如何从控制表中获取随机行,而行中的一行被锁定了。
任何建议都将不胜感激。
答案 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 插入一行。