缓存
缓存
缓存,是一种减少I/O读写和CPU计算,以提升性能的手段。
缓存的使用
主要用在需要频繁读某些不变动,或者变动较少的数据的场景。一般使用场景是:应用中使用缓存,需要读数据时,先去缓存中查询。缓存中没有的话,再查数据库,查到数据后返回结果,并将结果写到缓存中。
缓存分类
本地缓存
指应用中的缓存,缓存与应用在同一个进程内部,请求快,无网络开销。缺点是多个应用间无法共享。
实现方式:
- 使用成员变量,或者局部变量
- 使用静态变量,进程间可共享
一般使用Map实现,考虑多线程安全,则使用ConcurrentHashMap。ID作为key,每次使用id去查缓存。
分布式缓存
缓存是一个独立的应用,多个应用可以共享缓存,可实现持久化。一般使用redis实现
缓存更新问题
如果数据库中的数据有更新,如何更新缓存?
可以采用缓存过期策略,或者定期将数据从数据库中更新到缓存中。
有几种缓存过期策略:
- 先进先出。先缓存的数据,先清理。
- 最少使用。统计缓存的使用情况,最少使用的数据说明不是热点数据,可以清理掉。
- 最近最少使用。最近最少使用的数据,说明数据已经不再是热点数据了,可以清理掉。
满足上述策略的数据会被清理掉,下次访问缓存时再从数据库中读取并更新到缓存中。当然上述策略也是为了释放内存。
另外一种策略是,使用定时任务,在系统负载较低的时候,比如说晚上系统使用人数较少时,从数据库中读取数据,更新到缓存。
如果是应用内的缓存,或者缓存使用并不是很多的情况,并不需要考虑这么复杂的操作。
可以缩小缓存的使用范围,例如缩小到某个方法中,每次请求都清空缓存,再从数据库中读取或者从缓存中读取。这种针对需要在方法中循环处理大量数据,且这批数据中有需要大量读取某个相同值的情况。
例如需要处理一批数据,这批数据中有个字段需要从数据库中获取值,这个字段的值可能相同,不必要处理每条数据时都从数据库中读取,可以每次读取数据后,将值缓存起来,下一条数据先从缓存中读取。
这里定义一个成员变量map作为缓存
private Map<String,String> map;
这个map在什么时候初始化呢?如果在类初始化的时候进行初始化,则存储在map中的数据只有在重启服务后才能清空,重启才能从数据库中获取最新的数据,在此之前读取的数据都是旧。这对数据有变化的情况是不能接受的。
只能在每次处理数据前,初始化map,即是清空map,使得缓存只在这次的请求中生效,这样能保证这次的请求获取的是最新的数据。当然,性能的提升也只是在这次的请求中,下次请求还是先从数据库中读取数据。
架构