繁体   English   中英

乐观锁定用户信用管理

[英]optimistic locking user credit management

我有一个中央数据库,用于处理多个服务器的用户信用读写操作。 该应用程序位于这些服务器之上,通过对每个请求执行以下操作来满足用户请求:

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.

使用悲观锁定,应用程序将需要显式获取用户帐户的锁定以保证独占访问,但是由于系统有许多并发请求,因此这会降低性能。
那么对于这种学分制来说,什么是好的新设计呢?

这里有两个“锁定”机制,可以避免使用InnoDB的锁定机制,原因有两个:

  • 花费比在InnoDB的BEGIN...COMMIT中花费的时间更长的任务。
  • 以与开始时不同的程序(或不同的网页)结束的任务。

计划A。 (这是假设比赛条件很少见,在这种罕见情况下,第二步所浪费的时间是可以接受的。)

  1. (相同)通过从db中读取来检查用户是否对任务有足够的功劳。
  2. (相同)执行耗时的请求
  3. (添加) START TRANSACTION;
  4. (添加)再次检查用户是否有足够的信用额度。 ROLLABCK ,如果没有则中止。)
  5. (与旧#3相同)从用户帐户中扣除信用,将新信用计数保存回db。
  6. (添加) COMMIT;

START..COMMIT是InnoDB事务的东西。 如果在第4步中竞赛条件导致'x'不能获得积分,则您将ROLLBACK而不执行第4步和第5步。

计划B。 (这比较复杂,但是您可能更喜欢。)

  1. 有一个表Locks用于锁定。 它包含user_id和时间戳。
  2. START TRANSACTION;
  3. 如果user_id在Locks ,则中止( ROLLBACK并退出)。
  4. INSERT INTO Locks将user_id和current_timestamp Locks (从而“锁定”“ x”)。
  5. COMMIT;
  6. 执行处理(原始步骤1、2、3)
  7. DELETE FROM Locks WHERE user_id = 'x'; (这里的autocommit=1就足够了。)

潜在的问题:如果处理在步骤6中终止,而无法解决解锁问题,则该用户将永远被锁定。 “解决方案”是定期检查Locks是否有“很旧”的时间戳。 如果找到任何内容,则假定该处理已终止,然后删除该行。

您没有明确说明要实现的目标,所以我想您不想执行该工作只是为了意识到由于信用低而使它徒劳无功。

无锁

对步骤(1)实行信用保留,并将工作(2)和扣除额(3)与保留相关联。 这样,信用度较低的用户就不会通过步骤(1)。

乐观锁

由于在乐观锁定后发现了碰撞,因此我认为这不符合假设。

悲观锁定

在不了解模式的情况下不可能确切地说出来,但是我认为这是夸大性能的夸大其词。 您可以巧妙地合并MySQL InnoDB 事务隔离级别,并以比完全独占锁定用户帐户更好的粒度锁定读取 例如,使用SELECT ... LOCK IN SHARE MODE设置共享锁并允许读取其他事务。

里克关于任务需要花费更长的时间才能使MySQL等待的警告( innodb_lock_wait_timeout )在这里适用。

您需要托管交易方法

您可以为每个更新过程分配一些信贷,然后将信贷分配给他们(即代管),以记录剩余的信贷。 一个过程重试直到成功,该事务增加了需要的信贷额度,而减少了需要的信贷额度; 它只有在使信用不为负的情况下才能成功。 然后进行长时间的计算。 不管计算成功与否,它都将应用减少已发放信贷的交易。 但是成功则增加资产,失败则增加剩余的信用。

使用您在除MySQL之外的所有实际数据库引擎中都可以找到的时间戳/行转换方法。

您可以通过这种方式使用MySQL进行仿真。 有一个TIMESTAMP列(已更新),该列在每行更新时都会更新。 选择该列以及所需的其余数据。 将返回的时间戳记用作WHERE子句中的条件,以便仅在时间戳记与读取行相同时才更新行。

UPDATE table SET col1 = value WHERE id = 1 AND updated = timestamp_value_read

现在,当您运行更新并且时间戳不匹配时,将不执行任何更新。 您可以通过使用受影响的行来对此进行测试,如果更新了零行,那么您知道该行在读写之间进行了修改。 以哪种代码最适合您的应用程序和用户的方式处理该条件。

时间戳教程

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM