我有一个中央数据库,用于处理用户信用,多个服务器对其进行读写操作。应用程序位于这些服务器之上,通过对每个请求执行以下操作来提供用户请求:
1. check if user has enough credit for the task by reading from db. 2. perform the time consuming request 3. deduct a credit from user account, save the new credit count back to db.
应用程序使用数据库的乐观锁定。所以可能会发生以下情况
1. request a comes in, see that user x has enough credit, 2. request b comes in, see that user x has enough credit, 3. a performs work 4. a saves the new credit count back to db 5. b performs work 6. b tries to save the new credit count back to db, application gets an exception and fails to account for this credit deduction.
使用悲观锁定时,应用程序将需要显式锁定用户帐户以保证独占访问权限,但由于系统具有许多并发请求,因此这种KILL性能。
那么这个信用系统的新设计会是什么呢?
答案 0 :(得分:1)
这是两个"锁定"避免使用InnoDB锁定机制的机制有两个原因:
BEGIN...COMMIT
花费的时间要长。计划A。(这假设竞争条件很少,在极少数情况下,步骤2浪费的时间是可以接受的。)
START TRANSACTION;
ROLLABCK
如果没有,则中止。)COMMIT;
START..COMMIT
是InnoDB交易的东西。如果竞争条件导致' x'如果在步骤4中没有信用,您将ROLLBACK
而不执行第4步和第5步。
计划B。(这更复杂,但您可能更喜欢它。)
Locks
用于锁定。它包含user_id和时间戳。START TRANSACTION;
Locks
,则中止(ROLLBACK
并退出)。INSERT INTO Locks
Locks
中的user_id和current_timestamp(因此"锁定"' x')。COMMIT;
DELETE FROM Locks WHERE user_id = 'x';
(autocommit=1
就足够了。)潜在问题:如果处理在步骤6中死亡,没有绕过释放锁定,那么该用户将永远被锁定。解决方案'是定期检查Locks
是否有任何非常'的时间戳。旧。如果找到任何,则假定处理已经死亡,并删除行。
答案 1 :(得分:1)
你没有明确说明你想要达到的目标,所以我认为你不想执行这项工作只是为了意识到由于信用不足而导致这种做法是徒劳的。
在步骤(1)上实施信用保留,并将工作(2)和扣除(3)与保留相关联。这样低信用用户就不会通过步骤(1)。
由于在事后乐观锁定中检测到碰撞,我认为它不符合假设。
在不知道架构的情况下绝对不可能分辨,但我认为这是对杀死性能的夸大其词。您可以巧妙地合并MySQL InnoDB transaction isolation levels和locking reads,而不是完全独占锁定用户帐户。例如,使用SELECT ... LOCK IN SHARE MODE
设置共享锁并允许读取其他事务。
Rick关于任务需要更长时间的警告,MySQL会等待(innodb_lock_wait_timeout
)适用于此。
答案 2 :(得分:1)
你想要The Escrow Transactional Method。
您在记录每个更新过程中的一些信用额后记录剩余的信用额度,并将信用额发送给(即托管)。一个流程重试,直到成功增加信贷的交易通过它所需要的东西发出,并减少它所需要的信贷;只有当这会使信用保持非负面时,它才会成功。然后它进行长时间的计算。无论计算的成功与否,它都会应用减少信用额度的交易。但是在成功的同时它也会增加资产,而在失败时会增加信贷。
答案 3 :(得分:1)
使用除MySQL之外的所有真实数据库引擎中的timestamp / rowversion方法。
您可以通过这种方式使用MySQL模拟它们。有一个TIMESTAMP列(已更新),每当更新行时都会更新。选择该列以及您需要的其余数据。使用返回的时间戳作为WHERE子句中的条件,以便只有在时间戳仍与读取行时相同时才更新行。
UPDATE table SET col1 = value WHERE id = 1 AND updated = timestamp_value_read
现在,当您运行更新并且时间戳不匹配时,将不会执行更新。您可以通过使用受影响的行来测试这一点,如果更新了零行,那么您就知道在读取和写入之间修改了该行。在您的代码中处理这种情况,无论哪种方式最适合您的应用程序和用户。