前段时间接到一个需求,由于排期很是紧,没有认真规划就开干了。sql
需求描述起来大体是这样:门店的app首页会展现6张门店图片,之前只有A类门店,如今新增了B类门店,也须要首页图片。可是不一样于A类门店不存在状态,B类门店的首页图片须要有 上传-->审核\驳回
这样一个操做流程,分别对应图片 待审核、已经过、已驳回
三种状态。数据库
以前A类门店是直接将6张图片的url数据拼接成一个字符串,直接存放门店表中,以下图:缓存
由于B类图片存在状态,因此我建立了一个新表,将app_img_url字段拆分若干条记录,以下图:并发
修改后,对于图片的增删改查操做,须要同时操做两张表,其中有个查询门店信息的rpc接口,是高频调用,实现后的调用流程以下:app
可是我新增的查询门店图片信息实现,没有加缓存,致使上线后的次日早上,数据访问量一个小时内激增近千万。数据库设计
头皮发麻。高并发
赶忙加上缓存,紧急部署,然鹅并无访问量并无所有降下来。又开始排查,发现两点缘由:性能
冷静分析一波,忽然感受被本身蠢哭了!以前之因此将门店信息和图片信息分红两个sql查询,是由于在其余地方实现了查询图片信息的接口,直接复用,当时还沾沾自喜,感受本身遵循了开闭原则。。。伪代码以下:url
public Class ***ServiceImpl { ... // 查找门店信息 Shop shop = getShopInfo(); // 查找图片信息 (新增代码) List<Img> imgs = listImgInfo(); //设置图片信息 (新增代码) shop.setImgs(imgs); ... }
这样只是我写的代码最少,可是却不是遵循了开闭原则,而是对原有接口进行了修改,彻底能够在SQL查询中关联两张表,这样就不会有上面的问题了。流程以下:spa
可是这样作仍是有一个问题,那就是当查询量大的时候,left join对性能的损耗仍是很大的。
这样我就考虑到是否将图片的url在保存的时候,在两张表都保存(只针对已经过状态的图片,由于查询只查找已经过状态的图片),这样查询rpc接口彻底不用作任何修改,和原来的逻辑同样,只改变的了增删改的逻辑,而增删改是低频操做。
上述bug是多方面缘由致使的:
缘由1暂且不论,着重讨论一下缘由2和缘由3。
开闭原则(Open-Closed Principle, OCP):一个软件实体应当对扩展开放,对修改关闭。即软件实体应尽可能在不修改原有代码的状况下进行扩展。
从开闭原则的定义能够看出,个人第一次修改,很明显没有遵循这一原则,在工做实践中,大部分状况下不遵循设计原则并不会形成很大的麻烦,倒霉的是,我赶上了少部分状况。
因此对于一些普通的业务代码,遵循设计原则,可能会对代码后期维护有利,可是收益通常并不明显。
可是对于一些高频率高并发的接口,尤为是其余系统提供的接口,遵循设计原则就变得尤其重要。由于这些接口通常调用频率会很高,同时后期扩展变更的概率大。
这一点指的是,原来的数据库设计就没有为后期扩展留出余量,一个门店对应多个图片,这种一对多的问题,通常状况下,应该建立两张表,两张表之间经过主键id或者编号关联,这样若是后期,图片数量增长了(好比由6张图片变成100张),图片类型变化了(好比须要区分图片的审核状态、区分图片的类型等等)······对于这些变化,两张表的设计扩展修改起来会方便许多。