设计原则与代码实践(记一次线上bug)

设计原则与代码实践(记一次线上bug)

bug前因后果

前段时间接到一个需求,由于排期很是紧,没有认真规划就开干了。sql

需求描述起来大体是这样:门店的app首页会展现6张门店图片,之前只有A类门店,如今新增了B类门店,也须要首页图片。可是不一样于A类门店不存在状态,B类门店的首页图片须要有 上传-->审核\驳回 这样一个操做流程,分别对应图片 待审核、已经过、已驳回 三种状态。数据库

以前A类门店是直接将6张图片的url数据拼接成一个字符串,直接存放门店表中,以下图:缓存

image-20191212105059917.png

由于B类图片存在状态,因此我建立了一个新表,将app_img_url字段拆分若干条记录,以下图:并发

image-20191212104540975.png

修改后,对于图片的增删改查操做,须要同时操做两张表,其中有个查询门店信息的rpc接口,是高频调用,实现后的调用流程以下:app

image-20191212112104126.png

可是我新增的查询门店图片信息实现,没有加缓存,致使上线后的次日早上,数据访问量一个小时内激增近千万。数据库设计

头皮发麻。高并发

赶忙加上缓存,紧急部署,然鹅并无访问量并无所有降下来。又开始排查,发现两点缘由:性能

  1. 图片增删改逻辑复杂,多个地方均有调用,缓存添加的不全;
  2. 有至关一部分门店没有门店图片,致使缓存穿透。

冷静分析一波,忽然感受被本身蠢哭了!以前之因此将门店信息和图片信息分红两个sql查询,是由于在其余地方实现了查询图片信息的接口,直接复用,当时还沾沾自喜,感受本身遵循了开闭原则。。。伪代码以下:url

public Class ***ServiceImpl {
    ...
    // 查找门店信息
    Shop shop = getShopInfo();
    // 查找图片信息  (新增代码)
    List<Img> imgs = listImgInfo();
    //设置图片信息  (新增代码)
    shop.setImgs(imgs);
    ...
}

这样只是我写的代码最少,可是却不是遵循了开闭原则,而是对原有接口进行了修改,彻底能够在SQL查询中关联两张表,这样就不会有上面的问题了。流程以下:spa

image-20191212141224470.png

可是这样作仍是有一个问题,那就是当查询量大的时候,left join对性能的损耗仍是很大的。

这样我就考虑到是否将图片的url在保存的时候,在两张表都保存(只针对已经过状态的图片,由于查询只查找已经过状态的图片),这样查询rpc接口彻底不用作任何修改,和原来的逻辑同样,只改变的了增删改的逻辑,而增删改是低频操做。

总结

上述bug是多方面缘由致使的:

  1. 排期紧,没有认真规划需求的实现逻辑;
  2. 修改对外提供的rpc接口不够谨慎,直接在原代码中添加了修改逻辑。
  3. 原来的设计自己存在问题。

缘由1暂且不论,着重讨论一下缘由2和缘由3。

修改对外提供的rpc接口不够谨慎

开闭原则(Open-Closed Principle, OCP):一个软件实体应当对扩展开放,对修改关闭。即软件实体应尽可能在不修改原有代码的状况下进行扩展。

从开闭原则的定义能够看出,个人第一次修改,很明显没有遵循这一原则,在工做实践中,大部分状况下不遵循设计原则并不会形成很大的麻烦,倒霉的是,我赶上了少部分状况。

因此对于一些普通的业务代码,遵循设计原则,可能会对代码后期维护有利,可是收益通常并不明显。

可是对于一些高频率高并发的接口,尤为是其余系统提供的接口,遵循设计原则就变得尤其重要。由于这些接口通常调用频率会很高,同时后期扩展变更的概率大。

原来的设计自己存在问题

这一点指的是,原来的数据库设计就没有为后期扩展留出余量,一个门店对应多个图片,这种一对多的问题,通常状况下,应该建立两张表,两张表之间经过主键id或者编号关联,这样若是后期,图片数量增长了(好比由6张图片变成100张),图片类型变化了(好比须要区分图片的审核状态、区分图片的类型等等)······对于这些变化,两张表的设计扩展修改起来会方便许多。

相关文章
相关标签/搜索