互联网应用通常都需要应付大并发量,为了提高QPS,通常会使用中央缓存(例如memcache)和本地缓存的方式。请求先经过本地缓存,如果不命中,则请求穿透到中央缓存,如果还是不命中,则会直接查询数据库,并把查询到的数据刷新到中央缓存中。如果采用这种方式的话,必须要解决一个问题,如何刷新本地缓存的数据。
当时的解决方案如下:
服务器a、b和c中的应用application-a基于oscache构建了本地缓存,为了能刷新服务器中这些应用中的oscache,可以在application-a这个应用中提供一个刷新oscache的http接口,叫refresh(),每次服务器d 中的后台应用修改了数据后,先刷新中央缓存,然后直接获取服务器a、b、c的ip地址,然后for循环,挨个的调用refresh()接口。
这种做法有很多缺点:
1. 必须维护application-a应用的所有ip地址,如果新增了一个机器部署了application-a,则必须在后台管理系统中对应的新增一个ip地址,以便数据更新后,可以通知到新应用中的oscache缓存数据;
2. 使用for循环,是串行的通知,没有做到并行的通知,如果服务器多的话,有一定的延迟;
3. 假设更新其中一台服务器的缓存数据失败了,没有重试机制。
另一种做法是,是MQ的订阅机制,生产者把消息写到一个队列中,然后通知订阅该队列的所有消费者。
设置本地缓存数据的失效时间,例如10分钟,时间一到,则数据自动失效,让请求穿透过去,应用再次将新数据加载到本地缓存中,再次设置10分钟的失效时间。
这种做法非常的简单简洁,但是注意考虑业务场景。
当请求过来的时候,根据key先从中央缓存中获取对应这个key的一个时间戳,然后再根据这个key去本地缓存中获取时间戳,比较两个时间戳,如果本地缓存未失效,则从本地缓存获取。
这种做法也有个缺点,就是无论如何都必须先去中央缓存读取一次。多了一次开销,不过时间戳的大小也非常小,影响不是很大。