产品经理不清楚我们的接口给用户返回的具体的提示信息是什么,有一些可能是程序员能看懂,但是对用户不友好的提示。产品就想看一下,如果需要修改,则修改了,用户看到的是修改后的提示。
这个需求如果是对于一个新项目来说,可以将所有返回给用户的提示,单独配置到配置文件中,产品就可以直接看配置文件,修改配置文件了。但是对于一个已经运行的线上系统来说,这样改动的话,范围太大了,再有就是有一些是第三方平台返回给我们的信息,我们不太好控制。因此就换了一种方式,在aop层对所有的用户返回进行拦截,如果拦截到之前没有记录的返回信息,需要记录下来,如果已经记录下来了,则判断后台是否设置了替换信息,如果有则替换。
整个流程相对比简单,但是查询,插入,替换的这里涉及查询效率的问题,有如下方案:
1.查询,插入,替换直接访问数据库,线上访问并发很高,数据库io受不了。
2.增加redis缓存,查询的时候,先查询redis缓存,判断之前有没有记录返回信息和替换信息,如果没有,则查询数据库,写回redis,如果数据库中也没有,则需要加redis全局锁后,插入到数据库中,然后写入redis。如果后台编辑了某条信息的替换信息,则删除redis中的缓存,让程序访问的时候自然加载到redis中。这个方式大大减轻了数据库的压力,由于redis是基于内存的,因此速度也是比较快的,但是应用服务器和redis之间的io就比较高了。
3.采用应用程序内缓存,在应用程序内增加一个全局的Map缓存,在查询的时候,查询Map中有没有记录,没有的话,查询数据库,回写Map,如果数据库中也没有,则需要加redis全局锁后,插入到数据库中,然后写入Map。如果后台编辑了某条信息的替换信息,则采用发布订阅模式的消息队列,更新每一个进程中Map的信息。
从方案1到方案3,程序反应的速度越来越快,当然逻辑也越来越复杂。方案3对于大多数情况,程序几乎没有延迟。
换个思路来看这个问题,就是空间换时间。还能不能进一步扩展这个问题呢,当然可以,就是在每一个线程中都使用一套缓存,采用localthread变量,给每一个线程都保存一个变量,连线程间的锁都不需要了。