oracle数据库缓存机制-oracle库授权库权限
关于作者
王子辰
物流研发部架构师,GIS技术部负责人,2012年加入京东,有多年一线团队推广准备经验,负责部分物流研发部架构,专注于低延时系统设计和海量数据处理。 曾负责青龙配送订单团队,主导重构架构设计和主要研发工作,在短时间内将服务性能提升数十倍。 还设计开发了地址配送网点分类模型,实现了配送订单精准配送至路段,降本增效,大大提高了自动配送订单的准确性。 目前负责物流GIS部门,先后主持国标转京标、物流可视化等项目。
自古兵家狡猾。 在《口攻》中,“因此,一是攻敌,二是攻敌,二是攻军,再是攻城。攻城的方法是最后的手段。” 可见,攻城计有很多种,而爬墙攻城是最不明智的做法。 军队将被耗损,金钱和食物将丢失,人民将遭受损失。 所以,我们有很多迂回的策略、谋略、外交、军事手段等等,每一种都比攻城更省钱、更轻巧,缓存设计也是如此。
>>>>为什么要设计缓存?
其实高并发的解决方案并不是互联网独创的。 计算机的祖先早就为类似的场景做出了解决方案。 比如《计算机组成原理》中提到的cpu缓存概念,就是一种容量比内存小但速度却快很多的高速缓存。 甚至达到数千万次。
传统的CPU直接通过fsb连接内存的方式,显然会因为等待内存访问而导致CPU的吞吐量下降,内存会成为性能瓶颈。 同时,由于访问内存时热点数据比较集中,因此需要在CPU和内存之间建立一层临时存储作为缓存。
随着系统复杂度的增加,缓存与内存之间的速度进一步扩大。 由于技术难度和成本,存在较大的二级和三级缓存。 按照读取顺序,绝大多数请求落在一级缓存上,其次是二级缓存。
核心1
核心2
L1d
(一级数据缓存)
L1
(一级指令缓存)
L1d
(一级数据缓存)
L1
(一级指令缓存)
L2
L2
L3
L3
因此,在应用于SOA甚至微服务场景时,内存相当于一个持久化的数据库,用于存储业务数据,其吞吐量必然远小于缓存。 对于java程序来说,本地的jvm缓存比集中的redis缓存要好。
关系数据库易于操作,易于维护,访问数据灵活。 但是,随着数据量的增加,检索和更新的效率会越来越低。 因此,在高并发、低延迟要求的复杂场景下,需要减轻数据库的负担,减轻其压力。
>>>>减轻数据库负担
>>>>缓存分布式,做多级缓存
1.读请求时写缓存
写入缓存时,是逐级写入,先写入本地缓存,再写入集中缓存。 具体的缓存方法有很多,但是需要注意几个原则:
不要复制粘贴,避免重复代码
不要和业务耦合的太紧,不利于后期维护
在开发初期,即将上线前,为了排查问题,往往会为缓存设置开关,但是开关设置过多会同时增加系统的复杂度,需要一个统一的配置管理系统结合起来。
综上所述,高耦合带来的痛苦,弥补起来代价很大,所以可以参考Spring缓存来实现,实现起来也比较简单。 使用的时候,一个注解就可以搞定。
2、写缓存失败怎么办? 我应该先写入缓存还是数据库?
既然是缓存设计,策略上肯定是保证最终一致性的,所以我们只需要使用异步消息来弥补即可。
在大多数缓存应用场景中,读写比相差很大,读远大于写。 这种场景只需要以数据库为中心,先写入数据库,再写入缓存即可。
最后补充一点,当数据库出现异常时,不要简单地捕获RuntimeException,而是将特定关注的异常抛出,然后进行针对性的异常处理。
3.关于其他性能方面
缓存的设计是为了尽可能少的占用,昂贵的内存资源和太大的维护都驱使我们这样设计。 因此,需要尽量减少缓存不必要的数据,有些同学为了图省事,将整个对象序列化存储起来。 另外,序列化和反序列化也是很耗性能的。
>>>>vs各种缓存同步方案
缓存同步方案有很多种,从一致性、数据库访问压力、实时性等方面考虑。 一般来说,有以下几种方式:
1.延迟加载
上段说到,读的时候顺便加载一下。 为了更新缓存数据,缓存需要过期。
优点:简单明了
缺点:
懒加载太简单了,没有自动加载、异步刷新等机制,为了弥补它的不足,请参考下面两种方法。
2. 补充
缓存时可以将过期时间等信息写入一个异步队列,后台设置一个线程池定时扫描这个队列,在快要过期的时候主动重新加载缓存,让数据一直保留在缓存中。 如果没有缓存,就不用去数据库查询了。 常见的处理方式是使用binlog将消息处理成消息进行增量处理。
3、定时加载
这就需要一个异步线程池,周期性的把数据库数据刷到一个集中缓存中,比如redis。
>>>>防止缓存穿透
缓存穿透就是查询的key根本不存在,所以无法查询缓存,查询数据库。 如果这样的key恰好有大量的并发请求,就会给数据库造成不必要的压力。 如何解决?
将所有现有键存储在另一个存储的 Set 集合中。 查询时,可以先检查key是否存在。
简单的说就是给不可查询的key加上一个标识空值的值,这样数据库就查询不到了。 比如场景是查询某个省、市、市街道对应的移动营业厅。 如果某条街道没有移动营业厅,关键规则不变,可将该值设置为无意义的字符,如“0”。 当然,这个方案必须保证缓存集群的高可用。
这些key可能不会永远存在,所以需要根据业务场景设置过期时间。
>>>>热点缓存及缓存淘汰策略
在某些场景下,热点缓存只需要保留一部分,不需要缓存全量缓存,比如热门商品信息、购买某类商品的热门商圈信息等.
一般来说,缓存过期的策略有以下三种:
1. FIFO(先进先出)
先进先出,淘汰最早进来的缓存数据,一个标准的队列。
以队列为基本数据结构,新数据从队首入队,队尾淘汰。
2. LRU(最近最少使用)
最近最少使用,驱逐最近未使用的缓存数据。 如果数据最近被访问过,则不会被逐出。
与FIFO不同的是,它需要做一个链表的基本模型。 读写时间复杂度为O(1),新数据写入头部,链表充满数据从尾部淘汰;
最近访问的数据移到头部,实现算法有很多,比如hashmap+双向链表等;
问题在于,如果最近偶尔频繁访问某些键而不是异常访问,则会污染数据。
3. LFU(最不常用)
消除最近最少使用的数据。 注意与LRU的区别在于LRU的淘汰规则是基于访问时间的。
LFU中的每个数据块都有一个引用计数,数据块按照引用计数排序,如果数据块碰巧引用计数相同,则按照时间排序;
因为新增的数据访问数为1,所以插在队尾;
队列中的数据被新访问后,引用计数增加,队列重新排序;
当需要淘汰数据时,删除排序列表的最后一个数据块;
一个明显的问题是,如果数据在短时间内被频繁访问多次,比如访问异常或者环路不受控制,然后又长时间不用,数据就会被误保留,因为它的高频度并没有被淘汰。 尤其是新数据,由于它的初始计数为1oracle数据库缓存机制,即使正常使用oracle数据库缓存机制,也会因为不如旧数据而被淘汰。 所以维基百科上说纯LFU算法不是经常单独使用而是和其他策略结合使用。
>>>>缓存使用中的一些常见问题
Q:那么我应该选择使用本地缓存(local cache)还是集中缓存(Cache cluster)呢?
A:首先看数据量和更新缓存的开销。 如果整体缓存的数据量不大,变化不频繁,建议使用本地缓存。
Q:如何批量更新一批缓存数据?
A:顺序从数据库读取,然后批量写入缓存,批量更新,设置版本过期键或者主动删除。
Q:不知道有哪些key,如何定时删除?
A:以redis为例,keys * 太耗性能,不推荐。 可以指定一个集合,把所有的key都存放在这个集合中,然后删除整个集合,这样就可以彻底清理干净了。
Q:一个key包含一个大集合,redis无法实现内存空间的统一分片?
A: 1. 可以简单的设置key过期,这样会允许cache miss; 2.为key设置版本,比如当前时间两天后,然后在读取缓存Caching的时候根据时间判断是否需要重新加载,作为版本过期的策略。