2013-06-18 26 views
1

我需要从设置为空闲的SQLite数据库中选择一行,然后将该行标记为已采用。选择和更新至关重要的是一个原子操作,并且不存在两个用户将相同的行返回为空闲状态的风险。Laravel 4与更新的独家交易和选择

不幸的是,Laravel在拨打DB::transaction时似乎没有提供交易类型选择,如果我将它作为DB::statement运行,那么我不会获取返回的值。

另外,由于SQLite不支持变量,我不能在select和update之间存储主键,所以我不确定它是否可以根据SQLite数据库运行它。

可以使用Laravel和SQLite吗?

如果没有,那么我需要在MySQL表中存储与SQLite数据库相关的下一个空闲ID,并在该行上使用锁定。在MySQL中按行锁定是否有任何主要的语法差异? (我不能承受意外锁定整个表)

我在看类似这样的语法:(当然除了SQLite的不支持变量)

BEGIN EXCLUSIVE TRANSACTION 
    SET @free = SELECT InternalID FROM main WHERE Called = 0 LIMIT 1; 
    UPDATE main SET Called = 1 WHERE InternalID = @free; 
    SELECT * FROM main LEFT JOIN lead ON main.InternalID = lead.InternalID WHERE main.InternalID = @free; 
END TRANSACTION 

因为我敢肯定有人会问:“你为什么使用两个数据库系统!?”:

SQLite dbs包含需要灵活数量的列的数据。这些数据将在几个月内每周使用几天,然后可以归档。需要有相当多的这些“小型数据库”。这些数据无法高效地存储在MySQL中(我以前替换的系统尝试过这种方式,而且速度非常慢)。每个SQLite数据库的并发性都非常低,但至关重要的是两个用户完全没有机会获得同一行 - 这在旧系统中发生过几次。

回答

2

还是算了试图锁定表和/或做一个交易,有没有更好的这种模式:

  • 生成一个唯一的令牌
  • 分配该令牌到下一个可用记录
  • 读哪一行获得令牌,并对其进行处理

你需要完全没有锁定和交易(因为DB将您的令牌分配只有一个未使用的记录)的方式。

伪代码:

$token = time(); //Only you have very low concurrency. Otherwise use something more unique, like a GUID or an identity value from a tokens table. 

EXEC SQL: "UPDATE mytable SET token = $token WHERE token IS NULL LIMIT 1" 

EXEC SQL: "SELECT id FROM mytable WHERE token = $token" 

process($id); 
+0

谢谢!我还可以将用户的帐户ID用作我的令牌,因为每个用户一次只能使用一行。 –

+1

SQLite在更新中不支持'LIMIT',所以我不得不使用'UPDATE main SET CallerToken =? WHERE InternalID IN(SELECT InternalID FROM main WHERE CallState = 0 AND CallerToken IS NULL LIMIT 1)' –

+1

非常感谢这篇文章。您首先将所需记录标记为锁定更新,然后使用select查询它们的模式 - 非常聪明! :-)它让我感到毛骨悚然,我自己并没有得到这个想法,哈哈......无论如何,这真的帮助我解决了一个相关的问题!谢谢! – preyz