【问题标题】:Best practice for database structure to track if user opened a certain content?数据库结构跟踪用户是否打开某个内容的最佳实践?
【发布时间】:2014-11-02 16:28:39
【问题描述】:

目前我正在建立一个网站,其中包含大量活动和非活动内容。发布内容时,它处于活动状态,一段时间后变为非活动状态。我想跟踪在活动期间打开内容的所有用户。将这些信息存储在数据库中的最佳方式是什么?

我需要查询数据库以下问题: 1. 哪些用户打开了特定的内容?这很少被管理员查询,每个内容可能会被管理员查询两到三遍。 2. 特定用户打开了哪些活动内容? 3. 特定用户打开了哪些非活动内容(打开时处于活动状态但同时处于非活动状态的内容)?这两个查询比第一个查询更频繁(可能每两到三周)。用户可以在他/她的个人资料中查看他/她打开了哪些内容。

首先我想到了一个包含三列的数据库表:“用户 ID”、“内容 ID”、“状态”。其中“状态”对于活动内容为真,对于非活动内容为假。 “用户 ID”和“内容 ID”是主键,“状态”是来自内容表的外键。但后来我想到了这张桌子的大小。在某些年份(希望)可能有 200 万个节点和 25 万个用户,因此可能有 5000 亿行......我认为这会严重降低性能。

于是我想到了另一种方法:我创建了两个数据库表,一个叫做“content_opened_by_users”,另一个叫做“user_opened_content”。前者将包含两个列:“content id”和“users”。其中“users”是一个包含所有用户id的序列化数组,它在同一行中打开了相应的内容id。第二个表将包含三列:“用户 ID”、“活动内容”、“非活动内容”。其中“活动内容”和“非活动内容”也是序列化数组,其中包含相应用户打开的活动/非活动内容的内容 ID。 因此,每当用户打开内容时,服务器都会从“content_opened_by_users”表中加载相应的“users”-array,并从“user_opened_content”表中加载相应的“active content”-array。如果用户 id 不存在于“users”-array 中,它将被添加,如果 content id 不存在于“active content”-array 中,它也将被添加。然后两个数组都将在数据库中被覆盖。 现在,如果我查询所有打开某个内容的用户,我会从“content_opened_by_users”表中得到一个数组。如果我查询由特定用户打开的所有活动/非活动节点,我会从“user_opened_content”表中获得“活动内容”和“非活动内容”数组。然后我检查“活动内容”数组是否包含一些同时处于非活动状态的内容 ID,并将它们传输到“非活动内容”数组并将它们传递回数据库。

我知道,我在这两个表中创建了冗余数据,但我希望这会提高性能。

那么,这是一种方便的方式来完成用户跟踪吗?还是有其他更有效的方法?

我欢迎任何建议! 非常感谢。 丹尼尔

【问题讨论】:

    标签: php mysql database user-tracking


    【解决方案1】:

    首先,您可能想了解一下database normalization

    为了跟踪您描述的信息,我会为用户、内容和访问使用单独的表格,如下所示:

    table    | columns
    -------------------
    users    | id, login, ...
    content  | id, title, active, ...
    access   | id, user_id, content_id, timestamp, ...
    

    然后,您可以存储用户和内容项目,而不管谁打开了哪个项目。 access 表包含有关哪个用户在哪个时间打开了哪个内容项的条目。这允许您跟踪大量统计信息,而无需存储有关用户打开项目的任何信息。

    主要的查询是:

    1. 哪些用户打开了一个项目

      SELECT DISTINCT users.login FROM access JOIN users ON access.user_id = users.id JOIN content ON access.content_id = content.id WHERE content.title LIKE '%test%'
      
    2. 用户打开的活动内容

      SELECT DISTINCT content.title FROM access JOIN users ON access.user_id = users.id JOIN content ON access.content_id = content.id WHERE content.active = true AND user.login = 'testuser'
      
    3. 用户打开的非活动内容

      SELECT DISTINCT content.title FROM access JOIN users ON access.user_id = users.id JOIN content ON access.content_id = content.id WHERE content.active = false AND user.login = 'testuser'
      

      假设只能对活动内容进行访问。要跟踪状态更改的时间,请添加另一个表,您可以使用该表查找上次更改的时间戳并与访问条目进行比较。

    请注意,这些查询根本没有优化。您还应该添加相应的FOREIGN KEY constraints 以确保数据一致性并考虑添加额外的索引以提高性能。

    【讨论】:

    • 感谢您的快速回复!抱歉,我没有提到,我也有两个单独的用户表和内容表。所以一般来说,用户/内容管理的所有东西都已经正常工作了。您对访问表的建议似乎与我的第一个想法相似。正如我所提到的,我担心性能。假设在某些年份有数百万个项目和数千个用户。如果每个用户都打开了每个项目(好吧,这完全不现实,但我只是想做好准备),访问表中将有数十亿个条目......
    • ...我猜,这会大大降低性能,对吧?
    • 这取决于... MySQL 的最大行数真的很高。因此,只要您添加正确的索引,每天有数百次访问的数千名用户都不是问题。
    • 我真的需要访问表中的索引吗?我会将 user_id 和 content_id 设置为主键,所以我不需要 access_id 字段。并且主键总是被索引...或者我应该分别索引 user_id 和 content_id 吗?
    • 访问表中的列应该有foreign key constraints,这需要索引才能工作。 mysql使用它们来匹配来自不同表的数据。但是,稍后更改索引并不是什么大问题。顺便说一句:像Doctrine 这样的框架会自动生成这些约束。
    猜你喜欢
    • 2011-10-03
    • 2012-05-01
    • 2023-04-08
    • 1970-01-01
    • 1970-01-01
    • 2018-05-17
    • 2016-09-21
    • 2012-07-19
    • 1970-01-01
    相关资源
    最近更新 更多