大部分写业务的程序员,在实际开发中使用Redis时,只会对SetValue和GetValue进行操作,对Redis整体缺乏了解。这里总结一下Redis的常见问题,解决大家的知识盲点。一、为什么要使用Redis在项目中使用Redis,主要考虑两个方面:性能和并发。如果只是为了分布式锁等其他功能,还有Zookpeer等其他中间件代替,没必要用Redis。性能:如下图所示,当我们遇到执行时间较长且结果变化不频繁的SQL时,特别适合将运行结果存放在缓存中。这样后续的请求都是从缓存中读取的,从而可以快速响应请求。尤其是秒杀系统,同一时间,几乎所有人都在下单下单。..执行相同的操作---向数据库查询数据。根据交互效果的不同,响应时间没有固定的标准。理想情况下,我们的页面跳转需要瞬间解决,页内操作需要瞬间解决。并发性:如下图所示,在大并发的情况下,所有请求都直接访问数据库,数据库会出现连接异常。这时候就需要用Redis做一个缓冲操作,让请求先访问Redis,而不是直接访问数据库。Redis使用中的常见问题缓存和数据库双写一致性问题缓存雪崩问题缓存击穿问题缓存并发竞争问题2.为什么单线程Redis这么快这个问题是对Redis内部机制的考察。很多人不知道Redis是单线程的工作模型。主要原因有以下三点:纯内存操作单线程操作,使用非阻塞I/O多路复用机制避免频繁的上下文切换白某在A市开了一家快餐店,负责快餐服务城市。由于经济拮据,小白请了一批快递员,小曲发现资金不够,只够买辆车送快递。添加君阳:874811168免费领取结构资料一份。经营方式1顾客每次下单,小白都会让送货员看管,然后让人开车送货。慢慢地,小屈发现了这种商业模式存在以下问题:时间都花在了抢车上,送货员大多无所事事,只好抢车送货。随着订单的增多,快递员也越来越多。小白发现快递店越来越拥挤,也没有办法招到新的快递员。快递员之间的协调需要时间。基于以上不足,小白痛定思痛,提出了第二种商业模式。经营方式2小白只雇用一名送货员。顾客下单后,小白会根据发货地点做标记,依次放在一个地方。让送货员一辆一辆开着车去送货,送货后再回来取下一辆。对比以上两种操作方式,显然第二种方式效率更高。在上面的类比中:每个发货人→每个线程每个订单→每个Socket(I/O流)订单发货位置→不同状态的Socket客户发货请求→客户端明渠的业务方法请求→服务端运行的代码为acar→CPU的核心数有以下结论:第一种运行方式是传统的并发模型,每个I/O流(order)由一个新线程(deliverer)管理。第二种操作模式是I/O多路复用。只有一个线程(一个快递员)通过跟踪每个I/O流的状态(每个快递员已交付到的位置)来管理多个I/O流。下面类比真实的Redis线程模型,如图:Redis-client在运行时,会产生不同事件类型的Sockets。在服务器端,有一个I/O多路复用器将其放入队列中。然后,文件事件分发器依次从队列中取出,转发给不同的事件处理器。三、Redis的数据类型和使用场景一个合格的程序员会用到这五种类型。String是最常见的set/get操作,Value可以是String或number。一般做一些复杂的计数函数的缓存。这里的HashValue存储的是结构化对象,操作其中一个字段比较方便。我在做单点登录的时候,就是用这个数据结构来存储用户信息的,以CookieId为Key,缓存过期时间设置为30分钟,可以很好的模拟一个类似session的效果。List使用了List的数据结构,可以进行简单的消息队列功能。另外可以使用lrange命令实现基于Redis的分页功能,性能好,用户体验好。Set是因为Set堆叠了一组唯一值。这样就可以做全局去重的功能了。我们的系统一般都是集群部署的,使用JVM自带的Set比较麻烦。此外,通过交、并、差等运算,可以计算出共同偏好、所有偏好、独特偏好等函数。SortedSetSortedSet多了一个权重参数Score,集合中的元素可以按照Score进行排列。可以作为排行榜应用,采取TOPN操作。SortedSet可用于延迟任务。4、国内是否使用了Redis的过期策略和内存淘汰机制,由此可见一斑。比如你的Redis只能存5G的数据,你写了10G,那么5G的数据就会被删除。如何删除,你有没有想过这个问题?正解:Redis采用定时删除+惰性删除的策略。为什么不采用定时删除的策略,定时删除,用定时器监控Key,过期自动删除。虽然及时释放了内存,但是却消耗了大量的CPU资源。在大并发请求下,CPU应该花时间处理请求而不是删除key,所以没有采用这种策略。定时删除+惰性删除如何工作定期删除,Redis默认每100ms检查一次,如果有过期的Key就删除。需要注意的是,Redis并不是每100ms检查一次所有的key,而是随机选择它们进行检查。如果只采用定时删除策略,到时候很多key都不会被删除。因此,惰性删除就派上用场了。定时删除+惰性删除就没有其他问题了吗?不会,如果定期删除不会删除Key。而你没有及时请求Key,也就是懒删除没有生效。这样Redis的内存就会越来越高。那么就要采用内存淘汰机制。在redis.conf中有一行配置:#maxmemory-policyvolatile-lru这个配置配备了内存淘汰策略:noeviction:当内存不足以容纳新写入的数据时,新的写入操作会报错.allkeys-lru:当内存不足以容纳新写入的数据时,在key空间中,移除最近最少使用的Key。(推荐,目前项目正在使用这个)(最近使用时间最长的算法)allkeys-random:当内存不足以容纳新写入的数据时,在key空间中,随机移除一个key。(可能没人会用,如果不删除leastusedkey,随机删除)volatile-lru:当内存不足以容纳新写入的数据时,移除key中最近最少使用的key空间带有过期时间键的空间。在这种情况下,Redis一般既作为缓存,又作为持久化存储。(不推荐)volatile-random:当内存不足以容纳新写入的数据时,从key空间中随机移除一个key,并设置过期时间。(仍然不推荐)volatile-ttl:当内存不足以容纳新写入的数据时,在设置了过期时间的key空间中,先移除过期时间较早的Key。(不推荐)5、Redis与数据库双写一致性问题一致性问题又可以分为最终一致性和强一致性。如果数据库和缓存是双写的,难免会出现不一致的情况。前提是如果对数据有很强的一致性要求,就不能放缓存。我们所做的一切,只能保证最终的一致性。另外,我们所做的解决方案只能从根本上降低不一致的概率。因此,不能对一致性要求强的数据进行缓存。首先,采用正确的更新策略,先更新数据库,再删除缓存。其次,因为可能存在删除缓存失败的问题,所以提供一个补偿措施就可以了,比如使用消息队列。6、如何应对缓存穿透和缓存雪崩这两个问题,一般是中小传统软件公司比较难遇到的。如果有百万级流量的大型并发项目,这两个问题就必须深入考虑。缓存穿透是指黑客故意请求缓存中不存在的数据,导致所有请求都发送到数据库,导致数据库连接异常。缓存穿透解决方案:使用互斥锁,当缓存失效时,先获取锁,获取锁后再请求数据库。如果没有拿到锁,它会休眠一段时间,然后重试。采用异步更新策略,无论Key是否获取到值,都会直接返回。在Value值中维护缓存过期时间。如果缓存过期,则异步启动一个线程来读取数据库并更新缓存。需要进行缓存预热(在项目启动前加载缓存)操作。提供一种能够快速判断请求是否有效的拦截机制,例如使用Bloomfilter在内部维护一系列合法有效的key。快速判断请求中携带的Key是否合法有效。如果无效,直接返回。缓存雪崩,即缓存同时大面积失效。这时,又是一波请求。结果请求都发到数据库了,导致数据库连接异常。缓存雪崩解决方案:给缓存过期时间加一个随机值,避免集体失效。使用互斥量,但此解决方案的吞吐量会显着下降。双缓冲。我们有两个缓存,缓存A和缓存B,缓存A的过期时间是20分钟,缓存B的过期时间没有设置。自己做缓存预热操作。然后再细分以下几个小点:从缓存A中读取数据库,有则直接返回;A没有数据,直接从B读取数据,直接返回,异步启动一个更新线程,更新线程同时更新缓存A和缓存B。七、如何解决Redis的并发竞争Keys的问题这个问题大致是有多个子系统同时设置一个Key。这个时候应该注意什么?大家基本都推荐使用Redis的事务机制。但是我不推荐使用Redis的事务机制。因为我们的生产环境基本是Redis集群环境,所以进行了数据分片操作。当你在一个事务中涉及到多个Key操作时,多个Key不一定存放在同一个redis-server上。所以Redis的事务机制很鸡肋。如果key操作不需要顺序,这种情况下,准备一个分布式锁,大家都可以抢到锁,抢到锁后做set操作就可以了,比较简单。如果对这个Key进行操作,需要的序列假设有一个key1,系统A需要将key1设置为valueA,系统B需要将key1设置为valueB,系统C需要将key1设置为valueC。预计key1的值会按照valueA>valueB>valueC的顺序变化。这时候我们在向数据库写入数据的时候,需要保存一个时间戳。假设时间戳如下:SystemAkey1{valueA3:00}SystemBkey1{valueB3:05}SystemCkey1{valueC3:10}那么假设系统B先抢到锁,设置key1到{valueB3:05}。接下来系统A抢到锁,发现自己valueA的时间戳早于缓存中的时间戳,所以不执行set操作,以此类推。其他方法,例如使用队列和将set方法变成串行访问也是可能的。添加君阳:586446657免费领取结构资料一份。8.总结Redis在国内各大公司都有看到,比如我们熟悉的新浪、阿里、腾讯、百度、美团、小米等。学习Redis,这几个方面尤为重要:Redis客户端、Redis高级功能、Redis持久化和开发维护中的常见问题、Redis复制原理和优化策略、Redis分布式解决方案等。
