用Golang的接口查询快速实现Redis的延迟加载功能
MySQL的瓶颈和Redis的使用
在互联网应用场景中,大部分重要数据都保存在MySQL等以磁盘为存储介质的数据库里。用户使用应用时,数据从MySQL中读取,渲染到用户端。
受限于磁盘本身性能的影响,当大量用户短时间集中使用,频密的读写交替操作会产生严重堆积,缓慢的响应给用户带来很差的体验,采购SSD则意味着服务器要重新部署和增加采购费用。
故在数据库和应用之间插入Redis、Memcache等内存型存储系统,可以在不改变原有硬件的基础上,就缓解读操作产生的并发瓶颈。
但大量数据载入Redis,会对服务器的内存容量提出很高的要求。考虑到大部分时候,MySQL中存储的都是极少访问的冷数据,而热数据占的比例不是很大。所以使用延迟加载,避开一次性全部载入,是一种可以平衡两者矛盾的好方法。
延迟加载的原理就是只有当数据第一次被访问时才会载入到Redis中,没有被访问的数据,只存在于磁盘中,同时载入到Redis中的数据有过期时间设定,方便不再访问的数据可以被Redis定期回收。
Golang接口查询化简延迟加载代码的设计
整个golang的设计思想都是基于接口展开的, 接口查询是Golang的一个很有趣的功能,可以在各种有趣的场景中使用它。
善用Golang的接口查询,会让开发延迟加载本身变得简单轻松,让业务代码、数据来源和缓存的存取框架不再耦合。
常见的接口查询场景如下图所示,其逻辑就是如果数据的结构存在接口A,则走存在接口A的分支,如果数据结构没有接口A,则走不存在接口A的分支。
回到延迟加载问题本身
考虑到Redis中有的数据源自MySQL,有的数据则是临时数据,不存在于MySQL上。
如何设计从Redis中读取时,统一处理临时数据和保存在MySQL的数据,就可以用接口查询来解决。MySQL中保存的数据可以增加一个FetchFromMySQL的接口,而临时数据则不存在这样的接口。
于是,当数据在Redis中不存在时,统一的存储过程会根据struct是否实现了FetchFromMySQL接口,来决定是直接返回miss还是尝试从MySQL中读取并保存到Redis中,从而实现了延迟加载。
整个存取过程可以从下面的途中看出,其中数据甲在MySQL中有保存,数据乙则是临时数据。