Heim  >  Artikel  >  php教程  >  用户参与记录存储的演变

用户参与记录存储的演变

WBOY
WBOYOriginal
2016-06-13 11:28:22899Durchsuche

有这样一个应用场景:用户有两个连续的操作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:
我还没想到,期待你的分享

小结

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


Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn