草庐IT

mysql - 为 MySQL 中不断增长的表创建摘要表的最佳实践

coder 2023-10-22 原文

我有一个名为 transactions 的表,其中包含约 2000 万条记录。这张 table 每秒都在增长。

我用以下方法计算用户当前余额:

SELECT sum(`amount`) FROM `transactions` WHERE `user_id` = 1000;

我在我的 Web 应用程序的顶部栏中显示用户当前余额,用户可以看到他/她有多少余额!

显然每次用户浏览我的网页应用页面时,都必须执行上述查询来计算当前用户余额!

我想创建一个汇总表来获取当前用户余额,而无需查询具有约 2000 万条记录的 transactions 表!

请注意,在我们的工作流程中,一个用户可能同时进行多个交易(一个用户甚至可能在一秒钟内进行多个交易)是很常见的。

我认为我们有两种方法:

第一种方法

创建具有如下一对一关系的汇总表:

ID  |  user_id  |  current_balance
1   |  1000     |      8590
2   |  1001     |      235
3   |  1002     |      3780
... |  ...      |      ...

每次在 transactions 表中插入一条新记录时,我们都会触发一个存储过程来更新汇总表中的用户 current_balance

我不知道这种方法是否破坏了 MySQL 的一致性!

第二种方法

创建具有如下一对多关系的汇总表:

ID  |  user_id  |  amount
1   |  1000     |   8590    <--- it's the initial user balance
2   |  1001     |   235     <--- it's the initial user balance
3   |  1002     |   3780    <--- it's the initial user balance
4   |  1000     |   50
5   |  1000     |   -30
6   |  1001     |   10
7   |  1002     |   60
8   |  1000     |   -45

我们每晚清除汇总表(例如在 00:00 AM)并重新计算 transactions 表中所有用户的当前余额,并将它们插入到汇总表中 table 。要确定用户的当前余额,我们只需要这样做:

SELECT sum(`amount`) FROM `users_balance` WHERE `user_id` = 1000;

但是我对这种方法有些担心。如果某些用户恰好在我们重新计算用户当前余额并将其放入汇总表的时间进行交易怎么办! (正好在 00:00 AM)

这种方法会破坏一致性吗?


如果您知道此工作流程的更好做法,请告诉我。

附言

我们的网络应用程序是一个 SMS 面板,用户可以通过它发送/接收/等等。直接通过面板或 API 发送短信。我们有一些用户每天发送 100 万条或更多短信!

每次发送 SMS 时,都必须在 transactions 表中插入一条新记录。

我知道 2000 万条记录不是什么大问题,我们可以通过索引获得良好的性能,但正如我上面提到的,它是一个不断增长的表。我很确定明年 transactions 表中将有数亿条记录。

最佳答案

正如您所解释的,您正在为每个用户保持平衡。

最好的办法是编写执行两个查询的应用程序代码,可能在一个事务中,但也可能不是。

一个查询:

      UPDATE balances
         SET current_balance = current_balance - 1 
       WHERE user_id = 1000

该查询本身无需任何事务即可保持一致性。

(Edit) 它查找 balances 表中 user_id=1000 的行,然后从 的值中减去 1 >current_balance 在该行中,读取、修改,然后写入该行。您可以根据需要在 INSERTUPDATE 查询中对列值进行此类算术运算。

其他查询

      INSERT INTO transactions (columns) VALUES (values)

按照您解释应用程序的方式,听起来您的业务完整性取决于我在第一个查询中调用 balances 的表。 transactions 表是用户事件的日志,用于解释客户余额如何变成现在的样子。因此,如果您让您的应用程序按顺序执行我建议的两个查询,您将拥有出色的余额值和足够好的日志记录。这是构建事务数据库的好方法。

为什么您的余额应该与交易日志分开维护?如果您想给客户 100 条免费消息怎么办?如果您想在一天中的特定时间开始对消息收取额外费用怎么办?如果客户要求对她认为处理不当的一批消息进行退款怎么办?如果您从交易表中获取余额,您将不得不将各种奇怪的东西放入该表中以处理不断变化的业务规则。

如果我是你,我会把 balances 表的更新埋在触发器中吗?我不会。我会让它成为你申请的一部分。更容易查看、更容易调试等。

关于mysql - 为 MySQL 中不断增长的表创建摘要表的最佳实践,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43846485/

有关mysql - 为 MySQL 中不断增长的表创建摘要表的最佳实践的更多相关文章

  1. ruby-on-rails - 使用 Ruby on Rails 进行自动化测试 - 最佳实践 - 2

    很好奇,就使用ruby​​onrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提

  2. ruby - 如何在 Ruby 中顺序创建 PI - 2

    出于纯粹的兴趣,我很好奇如何按顺序创建PI,而不是在过程结果之后生成数字,而是让数字在过程本身生成时显示。如果是这种情况,那么数字可以自行产生,我可以对以前看到的数字实现垃圾收集,从而创建一个无限系列。结果只是在Pi系列之后每秒生成一个数字。这是我通过互联网筛选的结果:这是流行的计算机友好算法,类机器算法:defarccot(x,unity)xpow=unity/xn=1sign=1sum=0loopdoterm=xpow/nbreakifterm==0sum+=sign*(xpow/n)xpow/=x*xn+=2sign=-signendsumenddefcalc_pi(digits

  3. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  4. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  5. ruby - 使用 Vim Rails,您可以创建一个新的迁移文件并一次性打开它吗? - 2

    使用带有Rails插件的vim,您可以创建一个迁移文件,然后一次性打开该文件吗?textmate也可以这样吗? 最佳答案 你可以使用rails.vim然后做类似的事情::Rgeneratemigratonadd_foo_to_bar插件将打开迁移生成的文件,这正是您想要的。我不能代表textmate。 关于ruby-使用VimRails,您可以创建一个新的迁移文件并一次性打开它吗?,我们在StackOverflow上找到一个类似的问题: https://sta

  6. ruby-on-rails - 无法使用 Rails 3.2 创建插件? - 2

    我对最新版本的Rails有疑问。我创建了一个新应用程序(railsnewMyProject),但我没有脚本/生成,只有脚本/rails,当我输入ruby./script/railsgeneratepluginmy_plugin"Couldnotfindgeneratorplugin.".你知道如何生成插件模板吗?没有这个命令可以创建插件吗?PS:我正在使用Rails3.2.1和ruby​​1.8.7[universal-darwin11.0] 最佳答案 随着Rails3.2.0的发布,插件生成器已经被移除。查看变更日志here.现在

  7. ruby - 如何使用 RSpec::Core::RakeTask 创建 RSpec Rake 任务? - 2

    如何使用RSpec::Core::RakeTask初始化RSpecRake任务?require'rspec/core/rake_task'RSpec::Core::RakeTask.newdo|t|#whatdoIputinhere?endInitialize函数记录在http://rubydoc.info/github/rspec/rspec-core/RSpec/Core/RakeTask#initialize-instance_method没有很好的记录;它只是说:-(RakeTask)initialize(*args,&task_block)AnewinstanceofRake

  8. ruby - 为什么 SecureRandom.uuid 创建一个唯一的字符串? - 2

    关闭。这个问题需要detailsorclarity.它目前不接受答案。想改进这个问题吗?通过editingthispost添加细节并澄清问题.关闭8年前。Improvethisquestion为什么SecureRandom.uuid创建一个唯一的字符串?SecureRandom.uuid#=>"35cb4e30-54e1-49f9-b5ce-4134799eb2c0"SecureRandom.uuid方法创建的字符串从不重复?

  9. ruby - 有人可以帮助解释类创建的 post_initialize 回调吗 (Sandi Metz) - 2

    我正在阅读SandiMetz的POODR,并且遇到了一个我不太了解的编码原则。这是代码:classBicycleattr_reader:size,:chain,:tire_sizedefinitialize(args={})@size=args[:size]||1@chain=args[:chain]||2@tire_size=args[:tire_size]||3post_initialize(args)endendclassMountainBike此代码将为其各自的属性输出1,2,3,4,5。我不明白的是查找方法。当一辆山地自行车被实例化时,因为它没有自己的initialize方法

  10. ruby - 使用多个数组创建计数 - 2

    我正在尝试按0-9和a-z的顺序创建数字和字母列表。我有一组值value_array=['0','1','2','3','4','5','6','7','8','9','a','b','光盘','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','','u','v','w','x','y','z']和一个组合列表的数组,按顺序,这些数字可以产生x个字符,比方说三个list_array=[]和一个当前字母和数字组合的数组(在将它插入列表数组之前我会把它变成一个字符串,]current_combo['0','0','0']

随机推荐