这是我参与「第五届青训营」伴学笔记创作活动的第 13 天
初步实现
由于大项目的组长根据组员的能力和拥有的时间协商最后决定使用大单体,加大部分数据存入MySQL的方法。暂时没有加入集中缓存(如Redis)的打算。这就表明我们的初始实现是比较原始的直操数据库服务器。我负责的部分是互动接口的评论列表和评论操作部分,分别是返回对应视频的所有评论按时间排序和用户添加和删除评论。后者是需要全程登录操作的而前者可以不登录但是如果用户登录了就可以看哪些评论用户是自己关注的用户。项目组协商的数据表中评论表保存评论ID,用户ID,视频ID和评论内容,其中三种ID都有索引。因此,我就直接使用表连接将评论表和用户表内容合并,通过视频ID检索最后得到视频评论和评论用户的信息,同时如果请求是登录用户还会访问关注关系表返回其关注的所有用户,再通过建立集合对所有评论的用户设置是否已关注的值。在评论操作中,主要就是根据读到的用户操作做相应的数据库表插入和删除,因为视频表中有关于评论数的属性,因此还有更新这个属性的值。
可以看出上面的初步实现问题非常的多,因此我要对此进行进一步改进。
当前问题和未来修改计划
没有检查和限制评论字符串
这部分我应该在控制层调用前检查字符串的合法性,是否有非法字符,字符串是否过长。这里由于UTF-8是一种变长编码,所以先检查字符串没有非法编码然后按codepoint数确定评论内容大小。经过调研我发现抖音的评论长度限制为100,那我也可以使用这个。
数据库操作的效率和一致性问题
在发布和删除评论时,由于视频表中有属性评论数,因此理论上要保证数据一致性两个表的操作同步进行修改需要引入事务,但是这样对性能影响会比较大,因此这部分我并没有在同一事务中进行,不知道是不是需要修改。
由于使用集中式的缓存管理,并且项目组员之间的编码沟通不是那么方便,我准备使用本地级缓存,保存评论列表,添加评论可以直接在缓存上操作,删除评论进行缓存清除可能可以提高查询性能。
操作参数在业务数据中的合法性问题和外键约束
最近我发现由于我实现的部分业务代码中,当要进行评论发布操作时,没有对发布评论所关联的视频做合法性检验。在进行评论删除操作时,这种问题就不会带来太大的问题,因为这样在删除时检查条件这一步就会发现不一致的问题,不会造成不一致的问题。但是,发布评论时如果没有进行视频的合法性检查的话,那么就有问题了,这时这个评论会成为一个不存在的,甚至是未来可能存在的视频的评论,影响数据的一致性。好的一点是,我们的项目组长在建立项目的时候使用了外键约束关联了评论表中的视频ID和用户ID,这样即使出现了上述的非法请求操作也可以在数据库写入操作时由于外键约束导致操作失败返回错误,保证了一致性,坏处是当数据库的并发量和数据量较大时,插入操作对性能的消耗很大。
同时在阿里的代码规范的数据库篇中也提到,在阿里数据库的中不允许使用外键和其相关的级联操作,因为外键对性能有影响对分布式的数据库架构比如分库分表也有等扩展性也有阻碍,因此所有外键的操作都要放到业务层代码中做。而且如果通过数据库的外键约束检查参数的合法性,当产生大量恶意的评论发布请求时所有的压力都会到达数据库端,而且在评论列表接口是无法区分视频不存在和视频没有评论这两者的,这是比较危险的,因此我应该补上这部分的在业务代码上的检查。
但是,如何检查出非法的请求参数呢?这里根据接口的几个参数可以看出,对于用户ID由于JWT本身带有保证数据完整性的能力,因此用户ID本身是没有非法问题的,用户ID一般都是存在于数据库中的合法用户ID。而涉及删除评论时的评论ID本来就是表的主键,且删除时也要保证评论表中的三个索引满足请求的参数值,并且请求需要传输的数据也比较小,再加上用户的鉴权信息,可以针对性地限流,这部分也比较好控制。因此主要需要检查参数合法性的就是可以不需要用户鉴权就可以请求的评论列表操作和将请求发送到数据库要发送的数据量比较大的评论发布请求。这里,就是指检查请求参数中的视频ID,我们要确定这个请求的视频ID是合法的,否则认为请求失败直接返回。
问题是怎样检查视频ID是否合法,最简单的方法当然是做数据库查询,好处当然是简单且如果使用事务一致性会得到保证,但是这样显然增加了一次数据库读访问。那么缓存能否减少这次访问呢?答案是不太行,因为缓存不命中不能证明视频不存在。这与缓存穿透场景类似。因此使用布隆过滤器可能是一个比较好的选择。
增加限流
上面的请求合法性检查确定了请求会进行验证,但是只检查合法性本身也是消耗资源的,但是恶意请求是大量的,因此要对接口访问进行限流。