zoukankan      html  css  js  c++  java
  • 用户參与记录存储的演变

    有这样一个应用场景:用户有两个连续的操作A和操作B,必须是操作A完毕后才干运行操作B,假设操作A没有完毕就触发了操作B,则显示用户须要先运行操作A,即在操作B运行须要查询操作A是否运行过。这里引申出来的问题是,记录用户參与记录,提供针对用户和操作的查询方法。当不同的数据量时,我们的存储方案会大不同样,随着数据的增长,方案不断演变。

    1、数据量较小,用户操作行为固定:
    存储:MySQL
    方案:我们以UID为key,一行一个用户,每一个用户包含的用户作为列存储,比方UID=100,固定存储为操作A和操作B,则表结构大致例如以下:
    table_operation
    uid operation_a operation_b
    100 1 1

    假设我们要查询用户是否參与A或B时,直接使用SQL: SELECT * FROM table_operation WHERE uid=100 AND action_a=1就能够达成目标。

    问题:用户操作固定,扩展较难,假设须要添加用户操作行为,则须要添加字段或添加表存储,添加字段的方法在一定的数据量级下面(比方100万)是可行的,假设行为间无关,则添加表存储方案的表现会非常不错。

    2、数据量较小、用户操作行为不固定:
    与场景1相比,当前场景除了uid这个变量,添加了用户操作变量,即我们须要关注用户和用户操作两个变量。
    存储:MySQL
    方案1:添加操作表,生成操作id,用户操作行为表存储uid和oid。当用户运行一个新的操作时就在操作行为表插入一条记录。其表结构大致例如以下:

    table_operation_info
    oid name
    1 operation_a
    2 operation_b

    table_operation
    uid oid
    1 1
    1 2

    当须要查询用户1是否运行过操作A时,使用SQL:SELECT * FROM table_operation WHERE oid=1 AND oid=1。
    问题:当用户的操作行为较多时,用户操作行为增长速度非常快,数据量也为逐渐增大,可能MySQL单表无法负载。解决方式在兴许场景中说明。

    3、数据量较大,用户行为固定
    存储:MySQL
    方案:与场景1相比,当前场景不同在于数据量比场景1大,数据量大到MySQL单表负载只是来。此方案解决的就是这个问题,当单表太大时,性价比較高的方法通常是採用分表。我们当前场景的变量是uid,仅仅要根据uid按水平分表就可以。

    4、数据量较大,用户行为不固定
    存储: MySQL
    方案1:此方案应用于用户的操作行为能够分类的情况,即在场景1的基础上添加两次分表操作,按操作行为类分表和按用户分表。当前方案中我们须要应对两个变量:操作行为和用户。两次分表分别相应这两个变量,按业务规则做操作行为的分表操作,按用户id水平切分降低数据量。

    方案2:此方案是全然的水平分表操作,在场景2的方案基础上,按用户水平切分。

    5、数据量超大
    存储: MySQL
    方案1:分库分表,此时一个库已经无法满足需求,规则根据前面的场景实现,根据实际的需求能够考虑把不同的库放不同的机器上。
    方案2:在分库分表的基础上,按位存储,由于一个操作行为有没有运行过是一个是否的状态,即0,1状态,因此我们能够用一个位来存储,64位能够存储64个操作行为的标记。

    其他存储
    key-value数据库
    我们的需求实际上并不须要太多的关系型数据库的功能,简单的 k-v数据库就能够实现我们的功能,而且在性能上也会有所提升,毕竟做得少,会快。
    先无论是选择基于内存的,还是非内存的(能够依据实际需求来选择,也能够是热点数据在内存,沉默数据在非内存中),如果我们有足够的空间存储。
    方案1:
    以uid+oid为key,值能够存储状态,也能够仅仅存储是否參与(0和1),可是会存在key太多的情况,特别是当数据量超大时,uid的个数*oid的个数,可能是你无法相像的量级。
    方案2:
    一般来说,用户操作行为的数据量全然小于用户的量级,而且用户操作行为的数据可控。假设要降低key的个数,我们能够使用oid+用户分区索引id作为key,这里所谓的用户分区索引是指将用户以某个数量分成一个区,全部的用户都记录在这个这个区间内,比方以10000为一个区间,则uid为1到9999的用户分到区间0,这里能够以1和0存储用户是否运行了此操作,一个key相应的value初始化存储10000个0。当uid=100的用户运行了某操作,则将第100个0置为1。
    方案3:
    在方案2的基础上,将10000个0转换为10000个01位,如果一个位存储50位,则总共仅仅须要200个。
    方案4:
    当用户量超大时,大多数的用户对于某个操作可能都是没有參与的,则在方案3的基础上我们添加简单的稀疏矩阵压缩,给每一个存储位加入索引,当存储值不为0时才会存储。
    方案5:
    我还没想到,期待你的分享

    小结

    •随着数据量的增大,总的思路是分冶,当一个表搞不定时分表,当一个库搞不定时分库,当一台机器搞不定时加机器。
    •对于不同的存储介质选择须要考虑成本和需求,全部的选择都是平衡后的结果。
    •节省空间,按位存储。
    •不要过早优化。

  • 相关阅读:
    偶串_牛客网
    制造回文_牛客网
    字典树(前缀树)的实现
    动态规划LeetCode174地下城游戏
    动态规划LeetCode64最小路径和
    动态规划LeetCode300最长上升子序列
    动态规划LeetCode120三角形最小路径和
    Zabbix 监控sqlserver
    如何回收VCSA 6自带的vPostgres数据库空间
    领益科技:导出Wireless组中的成员
  • 原文地址:https://www.cnblogs.com/bhlsheji/p/4260983.html
Copyright © 2011-2022 走看看