0

我对关系数据库真的很陌生。我正在从事一个涉及财务的项目,因此我希望不会同时发生任何影响平衡的操作,并且我想使用锁来实现这一点,但是我不确定如何使用它们。我现在的愿景:我想为每个操作创建一个单独的表,并在 users 表中创建一个 balance 字段,其值将来自所有相关表。可悲的是,我实际上永远不会更新现有记录 - 只会添加它们。我想确保在这些表中一次只为每个用户插入一条记录。例如:3 个事务同时发生,因此 3 条记录将被添加到任何相关表中。其中两条记录具有相同的用户 ID,即我的 users 表的外键,另一条具有不同的。我希望将具有相同外键的记录流水线化,并且可以随时完成另一个记录。我如何实现这一目标?有没有更好的方法来解决这个问题?

4

2 回答 2

0

我希望任何影响平衡的行为不会同时发生

为什么?

我想用锁来实现

为什么?

给你一个反例。假设您想避免出现负账户余额。当用户提取 500 美元时,如何在没有锁的情况下进行建模。

UPDATE accounts
   SET balance = balance - 500
 WHERE accountholderid = 42
   AND balance >= 500

这在没有任何显式锁的情况下工作,并且对于并发访问是安全的。您必须检查更新计数,如果为 0,您将透支帐户。

(我知道 MySQL 仍然会获取行锁)

拥有一个分类帐仍然是有意义的,但即使在那里,我对锁的需求也不是很明显。

于 2019-04-20T15:51:05.743 回答
0
  1. 用于ENGINE=InnoDB您的所有桌子。
  2. 使用事务:

    BEGIN;
    do all the work for a single action
    COMMIT;
    

单个操作的经典示例是从一个帐户中取出资金并将其添加到另一个帐户。删除将包括检查透支,在这种情况下,您将有代码ROLLBACK而不是COMMIT.

您获得的锁确保单个操作的所有内容要么完全完成,要么什么都不做。这甚至适用于系统在BEGIN和之间崩溃的情况COMMIT

如果没有 begin 和 commit,但 autocommit=ON,每条语句都被 begin 和 commit 隐式包围。那就是上UPDATE一个答案中的示例是“原子”。但是,如果从一个帐户中扣除的钱需要添加到另一个帐户,如果在 之后发生崩溃会发生UPDATE什么?钱不见了。所以,你真的需要

 BEGIN;
 if not enough funds, ROLLBACK and exit
 UPDATE to take money from one account
 UPDATE to add that money to another account
 INSERT into some log or audit trail to track all transactions
 COMMIT;

在每一步之后检查——回滚并对任何意外错误采取规避措施。

如果两个(或更多)动作同时发生会怎样?

  • 一个等待另一个。
  • 有一个死锁,并且一个 ROLLBACK 被强制执行。

但是,在任何情况下,数据都不会被弄乱。

进一步说明......在某些情况下,您需要FOR UPDATE

BEGIN;
SELECT some stuff from a row FOR UPDATE;
test the stuff, such as account balance
UPDATE that same row;
COMMIT;

FOR UPDATE其他线程说:“请不要把手放在这一行上,我可能会更改它;请等我完成。” 没有FOR UPDATE,另一个线程可能会潜入并耗尽您认为存在的钱的帐户。

评论你的一些想法:

  • 对于许多用户及其帐户来说,一张表通常就足够了。它将包含每个帐户的“当前”余额。我提到了一个“日志”;那将是一个单独的表格;它将包含一个“历史”(而不仅仅是“当前”信息)。
  • FOREIGN KEYs在这个讨论中大部分是无关紧要的。它们有两个目的:验证另一个表是否有应该存在的行;并隐式创建一个INDEX以使检查更快。
  • 流水线?如果您每秒执行的“事务”不超过一百个,那么BEGIN..COMMIT您只需要担心逻辑。
  • “同时”和“同时”是误用的术语。两个用户“同时”访问数据库的可能性很小——考虑浏览器延迟、网络延迟、操作系统延迟等。再加上大多数这些步骤迫使活动进入单个文件的事实。网络强制一条消息先于另一条消息到达那里。同时,如果您的“交易”之一需要 0.01 秒,谁在乎“同时”请求是否必须等待它完成。关键是我所描述的将在需要时强制“等待”以避免弄乱数据。

尽管如此,仍然可能有一些“同时”——如果事务没有触及相同的行,那么从BEGIN到到的几毫秒COMMIT可能会重叠。考虑一下几乎同时发生的两笔交易的时间线:

BEGIN;  -- A
pull money from Alice  -- A
      BEGIN;   -- B
      pull money from Bobby  -- B
give Alice's money to Alan  -- A
      give Bobby's money to Betty  --B
COMMIT;   --A
      COMMIT;  --B
于 2019-04-28T16:55:12.600 回答