使用JSON而不是规范化数据,这种方法是否正确?

时间:2012-10-19 09:03:39

标签: mysql json innodb denormalization

在MySQL innoDB表中都有微博帖子和与之关联的投票/表情符号。 需要两种类型的页面:

(A)包含许多微博的列表页面以及他们的投票数/表情符号在单页上计算(比如25)。

E.g。

  

伟大的有趣的帖子

     

不是那么有趣的内容,意味着有趣的帖子。 Lorem ipsum dolor坐下来,精致的adipistur elit。 Phasellus euismod consequat pellentesque。 .....阅读更多....

     

(3)喜欢,(5)无聊,(7)微笑

。 +同一页面上的更多帖子。

(B)永久链接页面包含一个具有详细投票和投票计数/表情符号的微博。

  

伟大的有趣的帖子

     

不是那么有趣的内容,意味着有趣的帖子。 Lorem ipsum dolor坐下来,精致的adipistur elit。 Phasellus euismod consequat pellentesque。 Quisque viverra adipiscing auctor。 Mauris ut diam risus,在fermentum elit。 Aliquam urna lectus,egestas sit amet cursus et,auctor ut elit。 Nulla tempus suscipit nisi,nec condimentum dui fermentum non。在eget lacus mi,ut placerat nisi。

     

(你,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表和action_data中进行插入 更新表#1的字段以存储更新的计数。

假设有100K帖子和10x动作I.e.创建了100万或更多操作。 方法#2是否有益处?除了必须阅读,修改和更新JSON信息之外,它有什么缺点吗? 无论如何,有哪种方法可以遵循并进一步改进?

根据反馈添加更多信息:

  1. Python脚本将读取,写入数据。
  2. MySQL数据库服务器与网络服务器不同。
  3. 由于帖子创建而写的内容很低I.e.每天10000。但是那些由于操作原因可能更高,假设由于投票,喜欢,表情等行为而每秒最多写入50次。
  4. 我关注的是第二种方法的读写性能比较和第二种方法的问题,以及将来可能会遇到的问题。

4 个答案:

答案 0 :(得分:7)

我建议将所有喜欢/投票数据(聚合和原子)存储在表1中并完全丢弃表2 OR 以使用2个没有聚合数据的表,同时依赖{{1语法,聪明的查询和良好的索引。

为什么呢?因为在发表评论/投票/喜欢时,您将一直在查询并一直写入两个表。假设每个帖子只有10个动作用于显示交互,我真的将它全部存储到1个表中,可能为每种动作制作一个额外的列。您可以在数组上使用JSON或简单JOIN,这应该更快一些。

您最终选择的解决方案将高度依赖于您获得的操作数量以及您希望如何使用它们。使用解决方案1可以轻松获得1个帖子的所有操作并且速度非常快但在内部搜索会很麻烦。另一方面,解决方案2占用更多空间,仔细查询和编写索引。

答案 1 :(得分:2)

假设从系统中读取的内容远多于写入,我可以想到几种方法。您可以利用这样一个事实,即社交网站确实不需要拥有一致的数据,只要每个用户始终如一地看待他/她的行为,就最终保持一致。

选项#1。

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

表#1

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

这种方法很酷的是,在查看永久链接时,您无需查询表#2中的所有帖子。只查询最后5个观察者所做的任何动作和所有动作。在这里查看灵感:How to get the latest 2 items per category in one select (with mysql)

选项#2。

这就像您的第一种方法,但写入操作在Redis哈希集中计算,或者简单地作为内存缓存的JSON对象。它可以快速点亮主页加载中的那些。缺点是,如果重新启动redis(并且总是在memcached时),则需要重新初始化它们,或者只是在有人从固定链接视图查看页面时执行此操作。

答案 2 :(得分:2)

之前我会说选项2源于尝试过早优化,除非你已经有统计数据表明在列表页面中没有用于查询的连接将提高性能,我坚持使用选项1。

选项2的主要问题是维护,每次你需要更改某些东西时,你必须在两个地方更改它,并且为了修复错误,或者用新字段填充旧记录,发布您必须在数据库端执行字符串操作(通常)。

根据我的经验,选项2在性能方面的优势将是微乎其微的,查询数据库时的大部分延迟(至少是哪些此类简短查询)将来自连接到远程服务器。

此外,如果您正确地抽象查询,在两种方法之间移动(或使用其他方法,例如缓存最常用的条目)将变得非常容易,使用最简单的方法(即选项1)然后在获得有关当前实施问题的信息时更改它(这不太可能是您现在的想法)。

为清楚起见,这里列出了选项1的优点和缺点(与选项2相反):

选项1

优点

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

缺点

  • 列表读取速度较慢。

答案 3 :(得分:-1)

重要的一点是插入/删除/更新之间的性能差异。插入比删除/更新快得多。因此,我会选择最小化删除/更新的解决方案。

表#1看起来像第一个选项:
post_id | post_content | post_title | creation_time

表#2几乎相同,没有action_id post_id | action_type | action_creator | creation_time

表2将在post_id,action_type和action_creator列中有一个map复合索引。

地图复合索引的两个顺序对于快速查询很重要。因为即使不是索引的所有部分都使用索引也是如此。这是下面的查询将起作用 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进行分区时对表进行分区。由于大部分时间用户都会点击较新的条目,因此您可以通过更快的磁盘和更大的内存缓存来“优先”分区。或者稍后在数据库前面使用http://memcached.org/之类的东西进行优化。