介绍
我正在用 C++ 编写交易程序。该程序涉及拥有用户余额和订单簿。订单簿基本上是一组买卖订单。提交订单时,比如买单,匹配引擎会搜索是否有该价格或更低价格的卖单。如果是这样的话,交易就会发生。交易减去较小订单的数量,并将剩余数量添加到下一个买家/卖家的订单簿中。
上下文
当用户提交订单时,C++ 程序:
- 读取数据库以查看用户是否有足够的余额。
- 在内存中执行交易(在程序中)
- 修改数据库,在一个事务中,它读取余额,读取订单簿,修改它们并持久化它们 + 在数据库中写入日志条目(2 个条目)。
如您所见,对于交易程序,原子性和一致性是零容忍的。否则会出现双花。
但是,我可以容忍在数据库上只使用一层缓存,这将确保一致性和原子性(通过编程),但会推迟持久性。
我正在为我的程序使用 PostgreSQL。此外,我对数据库的所有访问都使用索引。更改默认索引算法只会降低性能。那里几乎没有标准化。我所做的唯一规范化是将余额表与用户表分开。其他一切都是不可分割的。
问题
这很慢。我在 Intel 4930k(8 核,16 线程,超频到 4.5 GHz)上几乎不能每秒处理 100 个事务,我至少需要每秒 1000 个事务。
我不确定问题出在哪里,但我听说数据库不太擅长修改数据。那么,如何在不危及 ACID 合规性的情况下优化该系统呢?
我的解决方案,我想问一下
我想在数据库上实现一个软件层,这是唯一允许直接访问数据库的层。该层将数据库缓存在内存中,并定期将所有更改刷新到数据库以进行持久化。但是这样做让我觉得我正在做数据库应该做的事情。
这是一件很常见的事情吗?我正在计划正确的解决方案吗?
从评论编辑:
我可以承受丢失仅在内存中的交易。原因是交易在任何时间点都应该发生或不发生。它发生在某个时刻还是在那之后的一秒钟都没有关系。我的问题是检查以这种方式缓存是否是有效的解决方案以及是否值得。这似乎是一个有效的解决方案。我只是不确定它有多大帮助以及为什么数据库不这样做(或者不可配置为这样做)?
显然我误解了上面的规范化这个词。我理解归一化是“拆分表,让它们更有意义”,所以为了性能,归一化更糟糕(同样,我可能误解了)。唯一被拆分的表是我上面提到的那个,所以我不是写入 4 个表,而是写入 4 个表并读取用户表。这就是为什么我不太相信拆分该表是原因。但也许我必须重新审视设计。我不是那里的专家。
我不确定我是否可以提供 DML,因为我使用ODB 作为 ORM。我必须看看我是否可以提取它们。当我到达系统位置时(几个小时后),我将很快提供 DDL 语句。
使用 ORM 是否意味着它没有希望?如果我失去 20-30% 的性能,我可以忍受它(现在)。与手动编写查询相比,我是否应该期望性能下降 10 倍(从 1000 tps 到 100)?好像过分了
pgbench -t 10000
在单个线程上产生 2350 个事务/秒(我的应用程序使用单个线程,因为每个订单簿都可以采用单个线程,这很好)。
编辑 2:
我选择下面的答案是因为它实现了我正在寻找的第二层解决方案。但是我仍然没有每秒最佳事务数,这意味着我仍然必须研究我拥有的模式和操作。
您的问题在逻辑上似乎是不一致的,因为容忍持久性延迟和“实际上我可以承受丢失仅在内存中的事务”的定义违反了“ACID”中的“D”。
如果你真的只想“ACI”兼容,你可以关闭“synchronous_commit”。
请注意,这意味着您可以向交易员报告他们的交易已执行,但后来发现糟糕,实际上并没有执行。它仍然是原子的和一致的,因为交易双方和现金余额的变化都将一起消失。但这仍然可能是糟糕的客户体验。
如果您决定不能在关闭“synchronous_commit”的情况下运行,您至少应该在关闭它的情况下进行测试。测试结果将指示您应该如何进行进一步优化。