使用JSON代替规范化数据,这种方法正确吗?

7
有微博帖子和与之相关的投票/表情符号,它们都存储在MySQL innoDB表中。需要两种类型的页面:
(A) 列表页面,包含许多微博以及它们的投票数/表情符号数,显示在单个页面上(例如25条)。
例如:
THE GREAT FUNNY POST 不是很有趣的内容,但是在本意上应该是一个有趣的帖子。Lorem ipsum dolor sit amet, consectetur adipiscing elit。Phasellus euismod consequat pellentesque。.....READ MORE.... (3) 赞,(5) 无聊,(7) 微笑
+ 同一页面上还有24条帖子。
(B) 永久链接页面,包含单个微博及其详细的投票数/表情符号。
例如:
THE GREAT FUNNY POST 不是很有趣的内容,但是在本意上应该是一个有趣的帖子。Lorem ipsum dolor sit amet, consectetur adipiscing elit。Phasellus euismod consequat pellentesque。Quisque viverra adipiscing auctor。Mauris ut diam risus, in fermentum elit。Aliquam urna lectus, egestas sit amet cursus et, auctor ut elit。Nulla tempus suscipit nisi, nec condimentum dui fermentum non。In eget lacus mi, ut placerat nisi。 (You, Derp 和 1 人赞了这篇文章),(5) 无聊,(7) 微笑
第一种方法:
表#1:
post_id | post_content | post_title | creation_time 

用于存储投票、点赞和表情符号的表格#2:

action_id | post_id | action_type | action_creator | creation_time

显示一页帖子或单个帖子。首先查询第一张表以获取帖子,然后查询第二张表以获取与帖子相关的所有动作。每当进行投票等操作时,都会在post_actions表中插入一个记录。
第二种方法:
表#1:
post_id | post_content | post_title | creation_time | action_data 

在这里,action_data可以是类似于{ "likes" : 3,"smiles":4 ...}的东西。

表格#2:

action_id | post_id | action_type | action_creator | creation_time

显示帖子页面时,只查询第一个表以获取帖子和动作数据,以显示具有详细操作的单个帖子,查询第二个表以获取与帖子相关的所有操作。每当进行投票等操作时,都会向“post_actions”表中插入一条记录,并更新表#1的“action_data”字段以存储更新的计数。
假设有100K篇帖子和10倍的操作,即创建了100万或更多个操作。第二种方法是否提供了好处?除了必须阅读、修改和更新JSON信息之外,还有什么缺点吗?
根据反馈添加更多信息:
1. Python脚本将读取、写入数据。 2. MySQL DB服务器将不同于Web服务器。 3. 由于帖子创建而导致的写入较少,即每天大约10000次。但是由于操作(如投票、喜欢、表情符号)可能更高,因此假定每秒最多有50次写入。 4. 我关心两种方法的读/写性能比较以及第二种方法的注意事项,以及它在未来可能出现的问题。

1
你没有提供关于整个系统架构的很多信息;谁将解释JSON数据?一切都在同一个服务器内还是强烈分布式系统?预计有多少读取与写入?最后,你是否对仅从数据库检索(而不是分析/显示)数据的简单情况进行了基准测试?然后你应该能够自己回答这些问题。 - Argeman
@Argeman 基准测试只会给我性能比较,这是肯定要做的。我的担忧更多地是关于数据库设计和相关问题,可能会遇到的问题。 - DhruvPathak
好的,那就算了我的评论... - Argeman
3
我认为将聚合数据保存为 JSON 不值得麻烦。保存它可能是值得的,但为什么不只是向“Table1”添加总列呢?例如:ALTER TABLE Table1 ADD COLUMN total_likes INTEGER;这样做也更容易自动维护信息,例如通过触发器。 - LSerni
4个回答

7
我建议要么将所有的点赞/投票数据(聚合和原子)存储在表1中并完全放弃表2,要么使用2个没有聚合数据的表,同时依赖JOIN语法、巧妙的查询和良好的索引。为什么?因为否则当发生评论/投票/点赞时,你会一直查询并写入两个表。假设每个帖子有10个与展示互动相关的操作,我真的建议把所有数据存储到一个表中,也许为每种操作增加一个额外的列。你可以使用JSON或简单地对数组使用serialize(),这应该更快。最终选择哪种解决方案将高度取决于您获得多少操作以及您想如何使用它们。使用解决方案1很容易获取1篇文章的所有操作并且非常快速,但在其中搜索会变得混乱。另一方面,解决方案2需要更多的空间、小心的查询编写和索引。

Muller,我不需要在json内部查询或搜索元素。感谢您的回答。 - DhruvPathak

2

假设系统中有更多的读操作而不是写操作,我可以想到几种方法来解决这个问题。您可以利用社交网络网站实际上并不需要一致的数据,只需要最终一致性,只要每个用户都能看到他/她的操作是一致的。

选项1:

在Table#1中为每种操作类型添加列,并在每次发生新操作时递增它们。通过这种方式,主页列表非常快速。

Table#1

post_id | post_content | post_title | creation_time | action1_count | action2_count | action3_count | ...

这种方法的亮点在于,当查看永久链接时,您不需要从表格#2中查询所有帖子的所有操作。只需查询最近5个任何操作和查看者执行的所有操作即可。在此处查看灵感:如何在一个选择中获取每个类别的最新2个项目(使用mysql)
选项#2。 这与您的第一种方法类似,但将操作计数编写到Redis哈希集或仅作为JSON对象写入memcache中。在主页加载时查询它们非常快。缺点是如果redis(并且始终是memcached)重新启动,则需要重新初始化它们,或者只有在有人从永久链接视图中查看页面时才这样做。

2
在开始之前,我想说的是,除非你已经有统计数据表明在列表页面查询时没有Join会提高性能,否则Option 2来源于试图过早优化,我建议使用Option 1。
Option 2的主要问题在于维护,每次需要更改内容时,你都必须在两个地方进行更改,为了修复错误或将旧记录填充到新字段中,你需要在数据库端执行字符串操作(通常情况下)。
根据我的经验,Option 2在性能方面的好处微乎其微,至少对于这样短的查询来说,查询数据库的大部分延迟都来自连接到远程服务器。
此外,如果你正确地抽象出查询,移动两种方法之间(或使用其他方法,例如缓存最常见的条目)将变得容易,首先使用最简单的方法(即Option 1),然后在了解当前实现问题的信息后进行更改(这些问题可能不是你现在所认为的)。
为了清晰起见,以下是Option 1的优缺点(与Option 2相反):

Option 1

优点

  • 更快的写入速度。
  • 更易于维护
  • 较小的存储要求
  • 无数据重复

缺点

  • 列表读取速度较慢。

-1
重要的一点是插入/删除/更新之间的性能差异。插入比删除/更新快得多。因此,我会选择最小化删除/更新的解决方案。
表#1将看起来像第一个选项: post_id | post_content | post_title | creation_time 表#2几乎相同,没有action_idpost_id | action_type | action_creator | creation_time 表二将在列post_id、action_type和action_creator中拥有一个映射复合索引。
映射复合索引的两个顺序对于快速查询非常重要。因为即使不使用索引的所有部分,索引也会工作。这是下面的查询将起作用 select ... from table_2 where post_id = 1 and action_type = 2 但以下查询将不起作用 select ... from table_2 where post_id = 1 and action_creator = 2 简单的解释是,要使用类似树形结构的映射复合索引,您需要使用树中上面的所有部分。也就是说,如果没有查询post_id和action_type,就无法查询“action_creator”以使用该索引。
-post_id  
    |--action_type  
          |--action_creator             

然而,现在您可以执行查询并始终命中复合索引,并且您大多数情况下都会向表#1和表#2插入。

如果由于“操作”数量很高而导致表#2非常庞大,则可以在将来对表进行分区,其中分区基于post_id。由于大多数时间用户会访问较新的条目,因此您可以“优先考虑”具有更快磁盘和更大内存缓存的DB中的一个分区。或者以后使用http://memcached.org/之类的东西优化数据库前端。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接