这是我参与「第五届青训营」伴学笔记创作活动的第 18 天
评论数据量分析
根据之前的两篇打卡阅读文章分析这个和这个,我想要计算一下在不进行分库分表的情况下,单机MySQL存储评论表的最大行数,由此来计算一下当前系统下的服务承载能力。首先,我们已经假定了每个页为16k bytes的容量,并假定其中元信息和页目录(Page Directory)占用1k bytes的页空间,剩下的15k bytes的空间可以用来存储数据,我们的id是bigint类型8 bytes的,那么一个非叶节点最多存约1920个子节点假设b+树3层,非叶节点有两层,那在第三层的非叶节点中就可以保存个叶节点,又因为我们评论表的列有id,行创建时间,行更新时间,行删除时间,视频id,用户id,评论内容,评论发布时间。除了变长的评论内容外,其他的列的占用空间量都为8 bytes,假设对于字符串的UTF-8编码都按最长的4 bytes计算,长度按我们认为规定的评论长度100计算那么一个数据行占用的空间就是bytes结合之前的大约15k bytes的存储数据的空间,每个叶子节点可以存放的行数最少为33个,如果假定平均的字符串长度都在比较常见的200 bytes左右那么可以存的行数就为60个,那么结合之前计算出的叶子节点的最大数量,就可以得到大概121,651,200到221,184,000行记录,即整个系统在所有视频上可以存放的评论数加起来也就是1.2亿至2.2亿条。再根据一个b站文章根据av号推断的视频数量推断13年时大概10万视频量,当然这里不包含单个视频大量分p的情况,这种情况在以前的b站还是很常见的(当年的b站很多视频源用的都是其他视频站,还有超长视频战渣浪防止审核删除版权视频和超长后黑防止视频2压的骚操作,但是这些一般不会影响评论数量)。结合我自己在12年10月初注册的账户id为5开头的6位数且站长碧诗的id为2,我推测13年以前大概能有的用户在60万左右,活跃用户应该更少。那么根据b站当时的视频量结合我们给出的大概总评论量得到平均每个视频可以分得2000条评论,鉴于b站这种弹幕网站一般弹幕量要远大于评论量应该认为弹幕量代表我们要估计的评论量,在我的印象中当时b站比较火的视频一般可能会到达5000到8000的弹幕量后面可能会被清弹幕到3000弹幕,而当时b站的热门新番一般会有1800到3000的弹幕量,结合评论的分布应该比较符合正态分布,单个MySQL的单个表可以存储的评论函数应该就是勉强可以承载一个成长初期的视频网站的数据量。
底层存储方式
对于使用InnoDB作为底层存储引擎的MySQL,其优势是支持ACID,但是其实对于评论及相关操作,对于ACID的要求不是特别高,而且不客气地说视频站的评论数据大部分价值不大(相对于视频的点赞收藏和播放量来说)。因此,使用效率更高的存储系统来存储这部分数据可能是更好地选择,这样既可以减轻MySQL的压力又可以想办法提高操作和查询性能,是值得一试的。根据我的调研,当前比较适合文本存储的NoSQL可能主要是MongoDB和elasticsearch,但是我感觉相对于我现在的应用场景可能使用MongoDB会更好一点。之后我可能会将这部分数据转换到MongoDB中进行存储和查询。
没有Double Check Lock带来的问题
在之前的代码中可以看到,我们在本地缓存中没有查到想要的相应缓存时,会释放本地缓存的互斥锁进行远程查询并在返回有效数据后将结果存入本地缓存,但是由于之前释放过锁,这部分就可能发生典型的多次重复赋初值的问题。之所以释放锁也是为了减少查询本地缓存被阻塞的时间,因此加会锁去避免这种情况不太好,但是这也确实会带来热点数据的缓存击穿问题,因此我们需要在初始化缓存时加入粒度更细的锁,这就需要借鉴redis中实现分布式锁的思想,通过发现指定的key的value值是否是自己的身份证明来实现这种相应键的加锁操作这种细粒度的锁。如果存在相应键并且不是自己的身份证明,那么认为已经有人进行了初始化操作,这时采取等待一段时间再去缓存中检索而不是远程访问数据的方式来获取数据才是更明智地选择。
检测视频是否真正存在的代码带来的缓存击穿问题
之前的代码中,在进行操作前必须进行操作的视频的合法性检查,但是这部分检查是必须访问数据库的,但是我们知道数据库的查找是远程数据库访问是耗时的,并且由于这部分每次读取和操作前都要进行,调用的非常频繁所以减少这部分的耗时对读取和操作都有加速。但是,因为一个视频不在缓存只能证明视频不在缓存中,但是不能证明视频不在数据库中,缓存中没找到就只能到数据库中二次确认了,这种情况一般在合法操作序列中还好,因为操作是有一定局部性的,当下面的操作进行后相应的视频信息就会在缓存中出现了,也就不需要远程访问了。麻烦出在非法操作上,这时缓存穿透攻击就会在这部分代码中显现,因为每次访问最后都会穿透到数据库中最后返回不存在。这时在缓存中加入可以表达视频不存在的缓存表示就比较重要了,因此这部分我需要再加入对于缓存穿透防御的代码。