当前位置: 首页 > 面试题库 >

PostgreSQL行读取锁

通鸿风
2023-03-14
问题内容

假设我有一个名为取款的表(id,金额,user_id,状态)。

每当我开始提款时,流程如下:

  • 验证用户是否有足够的余额(按收到的金额之和-取款金额之和计算)
  • 插入行,其金额为user_id,status =``结束’‘。
  • 通过gRPC调用第三方软件以发起提款(实际上是汇款),等待响应
  • 如果我们收到肯定的答复,请立即更新状态为“已完成”的行;如果提款失败,则删除该条目。

但是,我在此流程中存在并发问题。假设用户在〜50 ms的差异内发出了2次全额提款请求:

请求1

  • 用户有足够的余额
  • 创建提款(余额= 0)
  • 更新提款状态

请求2(约50毫秒后)

  • 用户有足够的余额(这是不正确的,尚未存储其他插入内容)
  • 创建提款(余额=负)
  • 更新提款状态

目前,我们正在使用redis将提款锁定在特定用户(如果提款在x
ms以内),以避免这种情况,但这不是最可靠的解决方案。在我们目前使用当前解决方案开发针对企业的API的过程中,我们将阻止可能同时提出的提款请求。是否有任何方法可以锁定并确保随后的插入查询根据Withdrawals表的user_id等待?


问题答案:

这是事务隔离的属性。关于它的文章很多,我强烈建议您在“设计数据密集型应用程序”中进行概述。我发现它是加深我个人理解的最有用的描述。

默认的postgres级别是READ COMMITTED,它允许这些并发事务中的每一个都具有相似的含义(资金可用状态),即使它们应该是相关的。

解决此问题的一种方法是将每个事务标记为“ SERIALIZABLE”一致性。

SERIALIZABLE当前事务的所有语句只能看到在该事务中执行第一个查询或数据修改语句之前提交的行。如果并发可序列化事务之间的读取和写入模式会导致这种事务的任何串行(一次一次)执行都不可能发生的情况,则其中一个将回滚,并出现serialization_failure错误。

这应该以可用性为代价来增强应用程序的正确性,即在这种情况下,第二笔交易将不允许修改记录,并且将被拒绝,这将需要重试。对于POC或低流量应用程序,这通常是完全可以接受的第一步,因为您可以立即确保正确性。

同样,在以上引用的书中,我认为还有一个有关ATM句柄可用性的示例。他们考虑到了这种竞争情况,如果用户无法连接到集中式银行,但用户限制了最大提款额,以使爆炸半径最小化,则用户会透支!

解决此问题的另一种体系结构方法是使事务脱机并使它们异步,以便将每个用户调用的事务发布到队列中,然后通过让队列的单个使用者自然避免任何竞争情况。这里的权衡类似,只有一个工作人员可以提供固定的吞吐量,但这确实有助于解决当前的正确性问题:P

跨机器的锁定(例如在postgres /
grpc中使用redis)称为分布式锁定,并且有很多关于它的内容https://martin.kleppmann.com/2016/02/08/how-
to-do-distributed-locking.html



 类似资料:
  • 我试图在RDD中将PostgreSQL 9.6中的一个表读取到Spark 2.1.1中,我在Scala中有以下代码。 但是,它返回以下错误: 组织。阿帕奇。火花SparkException:作业因阶段失败而中止:阶段1.0中的任务0失败4次,最近的失败:阶段1.0中的任务0.3丢失(TID 7,10.0.0.13,执行者1):组织。postgresql。util。PSQLException:列索引

  • 问题内容: 我有一个.gz格式的文件。读取此文件的Java类是GZIPInputStream。但是,此类不会扩展Java的BufferedReader类。结果,我无法逐行读取文件。我需要这样的东西 尽管我创建了扩展java的Reader或BufferedReader类并使用GZIPInputStream作为其变量之一的类。 但是,这在我使用时不起作用 有人可以建议如何进行.. 问题答案: 装饰器的

  • 稳定性: 2 - 稳定的 require('readline') 模块提供了一个接口,用于从可读流(如 process.stdin)读取数据,每次读取一行。 它可以通过以下方式使用: const readline = require('readline'); 例子,readline 模块的基本用法: const readline = require('readline'); const rl

  • 问题内容: 我有一个查询,该查询返回一行,其中一行包含一个字符串数组(): 有没有简单的方法可以直接将其读入Golang切片中?例如 产生: 问题答案: 正如Victor在原始帖子的评论中提到的那样,该帖子以的解释很好地回答了这个问题。 直接从链接中获取: 要将Postgres数组值读入Go切片,请使用: { log.Fatal(err) } 我也已经在自己的项目中使用了此功能,因此可以确认它是否

  • 我在postgresql 9.2.4中设置了一个配置参数,在Datastudio中的方式如下 我可以使用以下方法访问Datastudio中值 psqlexception:错误:无法识别的配置参数“foo.test”