2023年6月20日发(作者:)

⼯作三年的java程序员⾯试题⼀、概述本⽂主要来分析JMM内存模型,英⽂名JAVA Memory Model,它是与计算机硬件有关的⼀个概念。为了保证共享内存的正确性(可见性、有序性、原⼦性),内存模型定义了共享内存系统中多线程程序读写操作⾏为的规范。正⽂在实际的⼯作项⽬中, 缓存成为⾼并发、⾼性能架构的关键组件 ,那么Redis为什么可以作为缓存使⽤呢?⾸先可以作为缓存的两个主要特征:在分层系统中处于内存/CPU具有访问性能良好,缓存数据饱和,有良好的数据淘汰机制由于Redis 天然就具有这两个特征,Redis基于内存操作的,且其具有完善的数据淘汰机制,⼗分适合作为缓存组件。其中,基于内存操作,容量可以为32-96GB,且操作时间平均为100ns,操作效率⾼。⽽且数据淘汰机制众多,在Redis 4.0 后就有8种了促使Redis作为缓存可以适⽤很多场景。那Redis缓存为什么需要数据淘汰机制呢?有哪8种数据淘汰机制呢?数据淘汰机制Redis缓存基于内存实现的,则其缓存其容量是有限的,当出现缓存被写满的情况,那么这时Redis该如何处理呢?Redis对于缓存被写满的情况,Redis就需要缓存数据淘汰机制,通过⼀定淘汰规则将⼀些数据刷选出来删除,让缓存服务可再使⽤。那么Redis使⽤哪些淘汰策略进⾏刷选删除数据?在Redis 4.0 之后,Redis 缓存淘汰策略6+2种,包括分成三⼤类:不淘汰数据noeviction ,不进⾏数据淘汰,当缓存被写满后,Redis不提供服务直接返回错误。在设置过期时间的键值对中,volatile-random ,在设置过期时间的键值对中随机删除volatile-ttl ,在设置过期时间的键值对,基于过期时间的先后进⾏删除,越早过期的越先被删除。volatile-lru , 基于LRU(Least Recently Used) 算法筛选设置了过期时间的键值对, 最近最少使⽤的原则来筛选数据volatile-lfu ,使⽤ LFU( Least Frequently Used ) 算法选择设置了过期时间的键值对, 使⽤频率最少的键值对,来筛选数据。在所有的键值对中,allkeys-random, 从所有键值对中随机选择并删除数据allkeys-lru, 使⽤ LRU 算法在所有数据中进⾏筛选allkeys-lfu, 使⽤ LFU 算法在所有数据中进⾏筛选Note: LRU( 最近最少使⽤,Least Recently Used)算法, LRU维护⼀个双向链表 ,链表的头和尾分别表⽰ MRU 端和 LRU 端,分别代表最近最常使⽤的数据和最近最不常⽤的数据。LRU 算法在实际实现时,需要⽤链表管理所有的缓存数据,这会带来额外的空间开销。⽽且,当有数据被访问时,需要在链表上把该数据移动到 MRU 端,如果有⼤量数据被访问,就会带来很多链表移动操作,会很耗时,进⽽会降低 Redis 缓存性能。其中,LRU和LFU 基于Redis的对象结构redisObject的lru和refcount属性实现的:typedef struct redisObject { unsigned type:4; unsigned encoding:4; // 对象最后⼀次被访问的时间 unsigned lru:LRU_BITS; /* LRU time (relative to global lru_clock) or * LFU data (least significant 8 bits frequency // 引⽤计数 * and most significant 16 bits access time). */ int refcount; void *ptr;} robj;Redis的LRU会使⽤redisObject的lru记录最近⼀次被访问的时间,随机选取参数maxmemory-samples 配置的数量作为候选集合,在其中选择 lru 属性值最⼩的数据淘汰出去。在实际项⽬中,那么该如何选择数据淘汰机制呢?优先选择 allkeys-lru算法,将最近最常访问的数据留在缓存中,提升应⽤的访问性能。有顶置数据使⽤ volatile-lru算法 ,顶置数据不设置缓存过期时间,其他数据设置过期时间,基于LRU 规则进⾏筛选 。在理解了Redis缓存淘汰机制后,来看看Redis作为缓存其有多少种模式呢?Redis缓存模式Redis缓存模式基于是否接收写请求,可以分成只读缓存和读写缓存:只读缓存:只处理读操作,所有的更新操作都在数据库中,这样数据不会有丢失的风险。Cache Aside模式读写缓存,读写操作都在缓存中执⾏,出现宕机故障,会导致数据丢失。缓存回写数据到数据库有分成两种同步和异步:同步:访问性能偏低,其更加侧重于保证数据可靠性Read-Throug模式Write-Through模式异步:有数据丢失风险,其侧重于提供低延迟访问Write-Behind模式Cache Aside模式查询数据先从缓存读取数据,如果缓存中不存在,则再到数据库中读取数据,获取到数据之后更新到缓存Cache中,但更新数据操作,会先去更新数据库种的数据,然后将缓存种的数据失效。⽽且Cache Aside模式会存在并发风险:执⾏读操作未命中缓存,然后查询数据库中取数据,数据已经查询到还没放⼊缓存,同时⼀个更新写操作让缓存失效,然后读操作再把查询到数据加载缓存,导致缓存的脏数据。Read/Write-Throug模式查询数据和更新数据都直接访问缓存服务,缓存服务同步⽅式地将数据更新到数据库。出现脏数据的概率较低,但是就强依赖缓存,对缓存服务的稳定性有较⼤要求,但同步更新会导致其性能不好。Write Behind模式查询数据和更新数据都直接访问缓存服务,但缓存服务使⽤异步⽅式地将数据更新到数据库(通过异步任务) 速度快,效率会⾮常⾼,但是数据的⼀致性⽐较差,还可能会有数据的丢失情况,实现逻辑也较为复杂。在实际项⽬开发中根据实际的业务场景需求来进⾏选择缓存模式。那了解上述后,我们的应⽤中为什么需要使⽤到redis缓存呢?在应⽤使⽤Redis缓存可以提⾼系统性能和并发,主要体现在⾼性能:基于内存查询,KV结构,简单逻辑运算⾼并发: Mysql 每秒只能⽀持2000左右的请求,Redis轻松每秒1W以上。让80%以上查询⾛缓存,20%以下查询⾛数据库,能让系统吞吐量有很⼤的提⾼虽然使⽤Redis缓存可以⼤⼤提升系统的性能,但是使⽤了缓存,会出现⼀些问题,⽐如,缓存与数据库双向不⼀致、缓存雪崩等,对于出现的这些问题该怎么解决呢?使⽤缓存常见的问题使⽤了缓存,会出现⼀些问题,主要体现在:缓存与数据库双写不⼀致缓存雪崩: Redis 缓存⽆法处理⼤量的应⽤请求,转移到数据库层导致数据库层的压⼒激增;缓存穿透:访问数据不存在在Redis缓存中和数据库中,导致⼤量访问穿透缓存直接转移到数据库导致数据库层的压⼒激增;缓存击穿:缓存⽆法处理⾼频热点数据,导致直接⾼频访问数据库导致数据库层的压⼒激增;缓存与数据库数据不⼀致只读缓存(Cache Aside模式)对于只读缓存(Cache Aside模式), 读操作都发⽣在缓存中,数据不⼀致只会发⽣在删改操作上(新增操作不会,因为新增只会在数据库处理),当发⽣删改操作时,缓存将数据中标志为⽆效和更新数据库 。因此在更新数据库和删除缓存值的过程中,⽆论这两个操作的执⾏顺序谁先谁后,只要有⼀个操作失败了就会出现数据不⼀致的情况。难道这样就够了吗?不,远远不够!提前多熟悉阿⾥往年的⾯试题肯定是对⾯试有很⼤的帮助的,但是作为技术性职业,⼿⾥有实打实的技术才是你⾯对⾯试官最有⽤的利器,这是从内在散发出来的⾃信。备战阿⾥时我花的最多的时间就是在学习技术上,占了我所有学习计划中的百分之70,这是⼀些我学习期间觉得还是很不错的⼀些学习笔记,以及学习视频都是的!我为什么要写这篇⽂章呢,其实我觉得学习是不能停下脚步的,在⽹络上和⼤家⼀起分享,⼀起讨论,不单单可以遇到更多⼀样的⼈,还可以扩⼤⾃⼰的眼界,学习到更多的技术,我还会在csdn、博客、掘⾦等⽹站上分享技术,这也是⼀种学习的⽅法。今天就分享到这⾥了,谢谢⼤家的关注,以后会分享更多的⼲货给⼤家!享更多的⼲货给⼤家![外链图⽚转存中…(img-vL9mgo8A-07)][外链图⽚转存中…(img-6Hg0BRsm-09)][外链图⽚转存中…(img-U69l5t88-10)]

2023年6月20日发(作者:)

⼯作三年的java程序员⾯试题⼀、概述本⽂主要来分析JMM内存模型,英⽂名JAVA Memory Model,它是与计算机硬件有关的⼀个概念。为了保证共享内存的正确性(可见性、有序性、原⼦性),内存模型定义了共享内存系统中多线程程序读写操作⾏为的规范。正⽂在实际的⼯作项⽬中, 缓存成为⾼并发、⾼性能架构的关键组件 ,那么Redis为什么可以作为缓存使⽤呢?⾸先可以作为缓存的两个主要特征:在分层系统中处于内存/CPU具有访问性能良好,缓存数据饱和,有良好的数据淘汰机制由于Redis 天然就具有这两个特征,Redis基于内存操作的,且其具有完善的数据淘汰机制,⼗分适合作为缓存组件。其中,基于内存操作,容量可以为32-96GB,且操作时间平均为100ns,操作效率⾼。⽽且数据淘汰机制众多,在Redis 4.0 后就有8种了促使Redis作为缓存可以适⽤很多场景。那Redis缓存为什么需要数据淘汰机制呢?有哪8种数据淘汰机制呢?数据淘汰机制Redis缓存基于内存实现的,则其缓存其容量是有限的,当出现缓存被写满的情况,那么这时Redis该如何处理呢?Redis对于缓存被写满的情况,Redis就需要缓存数据淘汰机制,通过⼀定淘汰规则将⼀些数据刷选出来删除,让缓存服务可再使⽤。那么Redis使⽤哪些淘汰策略进⾏刷选删除数据?在Redis 4.0 之后,Redis 缓存淘汰策略6+2种,包括分成三⼤类:不淘汰数据noeviction ,不进⾏数据淘汰,当缓存被写满后,Redis不提供服务直接返回错误。在设置过期时间的键值对中,volatile-random ,在设置过期时间的键值对中随机删除volatile-ttl ,在设置过期时间的键值对,基于过期时间的先后进⾏删除,越早过期的越先被删除。volatile-lru , 基于LRU(Least Recently Used) 算法筛选设置了过期时间的键值对, 最近最少使⽤的原则来筛选数据volatile-lfu ,使⽤ LFU( Least Frequently Used ) 算法选择设置了过期时间的键值对, 使⽤频率最少的键值对,来筛选数据。在所有的键值对中,allkeys-random, 从所有键值对中随机选择并删除数据allkeys-lru, 使⽤ LRU 算法在所有数据中进⾏筛选allkeys-lfu, 使⽤ LFU 算法在所有数据中进⾏筛选Note: LRU( 最近最少使⽤,Least Recently Used)算法, LRU维护⼀个双向链表 ,链表的头和尾分别表⽰ MRU 端和 LRU 端,分别代表最近最常使⽤的数据和最近最不常⽤的数据。LRU 算法在实际实现时,需要⽤链表管理所有的缓存数据,这会带来额外的空间开销。⽽且,当有数据被访问时,需要在链表上把该数据移动到 MRU 端,如果有⼤量数据被访问,就会带来很多链表移动操作,会很耗时,进⽽会降低 Redis 缓存性能。其中,LRU和LFU 基于Redis的对象结构redisObject的lru和refcount属性实现的:typedef struct redisObject { unsigned type:4; unsigned encoding:4; // 对象最后⼀次被访问的时间 unsigned lru:LRU_BITS; /* LRU time (relative to global lru_clock) or * LFU data (least significant 8 bits frequency // 引⽤计数 * and most significant 16 bits access time). */ int refcount; void *ptr;} robj;Redis的LRU会使⽤redisObject的lru记录最近⼀次被访问的时间,随机选取参数maxmemory-samples 配置的数量作为候选集合,在其中选择 lru 属性值最⼩的数据淘汰出去。在实际项⽬中,那么该如何选择数据淘汰机制呢?优先选择 allkeys-lru算法,将最近最常访问的数据留在缓存中,提升应⽤的访问性能。有顶置数据使⽤ volatile-lru算法 ,顶置数据不设置缓存过期时间,其他数据设置过期时间,基于LRU 规则进⾏筛选 。在理解了Redis缓存淘汰机制后,来看看Redis作为缓存其有多少种模式呢?Redis缓存模式Redis缓存模式基于是否接收写请求,可以分成只读缓存和读写缓存:只读缓存:只处理读操作,所有的更新操作都在数据库中,这样数据不会有丢失的风险。Cache Aside模式读写缓存,读写操作都在缓存中执⾏,出现宕机故障,会导致数据丢失。缓存回写数据到数据库有分成两种同步和异步:同步:访问性能偏低,其更加侧重于保证数据可靠性Read-Throug模式Write-Through模式异步:有数据丢失风险,其侧重于提供低延迟访问Write-Behind模式Cache Aside模式查询数据先从缓存读取数据,如果缓存中不存在,则再到数据库中读取数据,获取到数据之后更新到缓存Cache中,但更新数据操作,会先去更新数据库种的数据,然后将缓存种的数据失效。⽽且Cache Aside模式会存在并发风险:执⾏读操作未命中缓存,然后查询数据库中取数据,数据已经查询到还没放⼊缓存,同时⼀个更新写操作让缓存失效,然后读操作再把查询到数据加载缓存,导致缓存的脏数据。Read/Write-Throug模式查询数据和更新数据都直接访问缓存服务,缓存服务同步⽅式地将数据更新到数据库。出现脏数据的概率较低,但是就强依赖缓存,对缓存服务的稳定性有较⼤要求,但同步更新会导致其性能不好。Write Behind模式查询数据和更新数据都直接访问缓存服务,但缓存服务使⽤异步⽅式地将数据更新到数据库(通过异步任务) 速度快,效率会⾮常⾼,但是数据的⼀致性⽐较差,还可能会有数据的丢失情况,实现逻辑也较为复杂。在实际项⽬开发中根据实际的业务场景需求来进⾏选择缓存模式。那了解上述后,我们的应⽤中为什么需要使⽤到redis缓存呢?在应⽤使⽤Redis缓存可以提⾼系统性能和并发,主要体现在⾼性能:基于内存查询,KV结构,简单逻辑运算⾼并发: Mysql 每秒只能⽀持2000左右的请求,Redis轻松每秒1W以上。让80%以上查询⾛缓存,20%以下查询⾛数据库,能让系统吞吐量有很⼤的提⾼虽然使⽤Redis缓存可以⼤⼤提升系统的性能,但是使⽤了缓存,会出现⼀些问题,⽐如,缓存与数据库双向不⼀致、缓存雪崩等,对于出现的这些问题该怎么解决呢?使⽤缓存常见的问题使⽤了缓存,会出现⼀些问题,主要体现在:缓存与数据库双写不⼀致缓存雪崩: Redis 缓存⽆法处理⼤量的应⽤请求,转移到数据库层导致数据库层的压⼒激增;缓存穿透:访问数据不存在在Redis缓存中和数据库中,导致⼤量访问穿透缓存直接转移到数据库导致数据库层的压⼒激增;缓存击穿:缓存⽆法处理⾼频热点数据,导致直接⾼频访问数据库导致数据库层的压⼒激增;缓存与数据库数据不⼀致只读缓存(Cache Aside模式)对于只读缓存(Cache Aside模式), 读操作都发⽣在缓存中,数据不⼀致只会发⽣在删改操作上(新增操作不会,因为新增只会在数据库处理),当发⽣删改操作时,缓存将数据中标志为⽆效和更新数据库 。因此在更新数据库和删除缓存值的过程中,⽆论这两个操作的执⾏顺序谁先谁后,只要有⼀个操作失败了就会出现数据不⼀致的情况。难道这样就够了吗?不,远远不够!提前多熟悉阿⾥往年的⾯试题肯定是对⾯试有很⼤的帮助的,但是作为技术性职业,⼿⾥有实打实的技术才是你⾯对⾯试官最有⽤的利器,这是从内在散发出来的⾃信。备战阿⾥时我花的最多的时间就是在学习技术上,占了我所有学习计划中的百分之70,这是⼀些我学习期间觉得还是很不错的⼀些学习笔记,以及学习视频都是的!我为什么要写这篇⽂章呢,其实我觉得学习是不能停下脚步的,在⽹络上和⼤家⼀起分享,⼀起讨论,不单单可以遇到更多⼀样的⼈,还可以扩⼤⾃⼰的眼界,学习到更多的技术,我还会在csdn、博客、掘⾦等⽹站上分享技术,这也是⼀种学习的⽅法。今天就分享到这⾥了,谢谢⼤家的关注,以后会分享更多的⼲货给⼤家!享更多的⼲货给⼤家![外链图⽚转存中…(img-vL9mgo8A-07)][外链图⽚转存中…(img-6Hg0BRsm-09)][外链图⽚转存中…(img-U69l5t88-10)]