Redis总结

1.为什么要用redis/为什么用缓存

1)高性能

    第一次访问数据库的数据,非常慢,因为从磁盘上读,将用户访问的数据存在缓存中,这样可以直接从缓存中获取了,操作缓存就是直接操作内存,所以速度相当快。

2)高并发

    直接操作缓存能够成熟的请求远远大于直接访问数据库,所以把数据库中部分数据转移到缓存中。

2.为什么要用redis而不用map/guava做缓存?

    缓存分为本地缓存和分布式缓存。map或guava实现的是本地缓存,主要特点是轻量以及快速,生命周期随着jvm的销毁而结束,并且在多例的情况下,每个实例都需要各自保存一份缓存,不具有一致性。

    使用redis或memcached之类的称为分布式缓存,在多实例的情况下,各实例共用一份缓存数据,缓存具有一致性。缺点是需要保持redis或memcached服务的高可用,整个程序架构上较为复杂。

3.redis的线程模型

    redis内部使用文件事件处理器file event handler,这个文件事件处理器是单线程的,所以redis才叫做单线程模型。采用IO多路复用机制同时监听多个socket,根据socket上的事件来选择对应的事件处理器进行处理。

    文件事件处理器的结构包含4部分:1)多个socket。2)IO多路复用程序。3)文件事件分派器。4)事件处理器

    多个Socket可能会并发产生不同的操作,每个操作对应不同的文件事件,但是IO多路复用程序会监听多个socket,会将socket产生的事件放入队列中排队,事件分派器每次从队列中取出一个事件,把该事件交给对应的事件处理器进行处理。

4.redis和memcached的区别

1)redis支持更丰富的数据类型:不仅支持k/v类型,还提供list,set,zset,hash等数据结构。memcached支持简单的数据类型:String

2)redis支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用,而memcached把数据全部存在内存之中。

3)集群模式:memcached没有原生的集群模式,需要依靠客户端来实现往集群中分片写入数据;但是redis目前是原生支持cluster模式的。

4)Memcached是多线程,非阻塞IO复用的网络模型;Redis使用单线程的多路IO复用模型

5.redis常见数据结构以及使用场景分析

1)String

    常用命令:set, get, decr, incr, mget等

    String数据结构是简单的ey-value类型,value不仅可以是String,也可以是数字。常规key-value缓存应用:常规计数、粉丝数等

2)Hash

    常用命令:hget, hset, hgetall等

    hash是一个string类型的field和value的映射表,适合用于存储对象,后续操作直接修改这个对象中的某个字段的值。比如存取用户信息:

key=JavaUser29
value={
    "id":1,
    "name":"Joah",
    "age":22
}

3)List

    常用命令:lpush, rpush, lpop, rpop, lrange等

    list是链表,应用非常多,比如微博关注列表,粉丝列表等。是一个双向列表,支持反向查找和遍历,不过带来了部分额外的内存开销。另外可以通过lrange命令,从某个元素开始读取多少哥元素,可以基于list实现分页查询,基于redis实现简单的高性能分页,可以做微博那种下拉不断分页的东西,性能高。

4)Set

    常用命令:sadd, spop, smembers, sunion等

    set功能和list类似,差别在于set可以自动去重。当需要存储一个列表数据,有需要去重,这是一个好的选择,并且set提供判断某个成员是否在一个set集合内的接口,这是list不能提供的。

5)Sorted Set

    常用命令:zadd, zrange, zrem, zcard等

    增加了权重参数score,使得集合中的元素可以按score进行有序排列。

6.redis设置过期时间

    redis提供了设置时间过期功能,对值可以设置一个过期时间。对token或登陆信息,尤其是短信验证码很有用的,那redis如何对这批key进行删除的?

    定期删除+惰性删除

   1)定期删除:redis默认是每隔100ms随机抽取一些设置过期时间的key,检查其是否过期,如果过期就删除。这里是随机抽取,因为redis存了几十万个key,每隔100ms就遍历所有设置过期时间的key的话,就会给CPU带来很大的负载!

   2)惰性删除:惰性删除可能会导致很多过期key到了时间并没有被删除掉,还停留在内存里,这就是惰性删除

7.redis内存淘汰机制

1)volatile-lru:从已设置过期时间的数据机(server.db[i].expires)中挑选最近最少使用的数据淘汰

2)volatile-ttl:从已设置过期时间的数据机中挑选将要过期的数据淘汰

3)volatile-random:从已设置过期时间的数据机中任意选择数据淘汰

4)allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的key(最常用的)

5)allkeys-random:从数据集中任意选择数据淘汰

6)no-eviction:禁止驱动数据,当内存不足以容纳新写入数据时,新写入操作就会报错

4.0以后新增两种:

7)volatile-lfu:从已设置过期时间的数据集中挑选最不经常使用的数据淘汰

8)allkeys-lfu:当内存不足以容纳新写入数据时,在键空间中,移除最不经常使用的key

8.redis持久化机制

    redis不同于Memcached的一点是redis支持持久化,一种是快照(RDB),另一种是追加文件(AOF)

9.redis事物

Redis事务的概念

    Redis通过MULTI、EXEC、WATCH等命令来实现事物(transaction)功能。事务提供了一种将多个命令请求打包,然后一次性、按顺序执行多个命令的机制,并且在事务执行期间,服务器不会中断事务而去执行其他客户端的命令请求,它会将事务中的所有命令都执行完毕,然后才去处理其他客户端的命令请求。

    总结:redis事务就是一次性、顺序性、排他性的执行一个队列中的一系列命令

Redis事务没有隔离级别的概念

    批量操作在发送EXEC命令前被放入队列缓存,并不会被实际执行,也就不存在事务内的查询要看到事务里的更新,事务外查询不能看到

Redis不保证原子性

    Redis中,单条命令是原子性执行的,但事务不保证原子性,且没有回滚。事务中任意命令执行失败,其余的命令仍会被执行。

Redis事务的三个阶段

    1)开始事务

    2)命令入库

    3)执行事务

Redis事务相关命令

    watch key1 key2 ...:监视一或多个key,如果在事务执行之前,被监视的key被其他命令改动,则事务被打断

    multi:标记一个事务块的开始

    exec:执行所有事务块的命令(一旦开始执行,之前加的监控锁就会被取消掉)

    discard:取消事务,放弃事务块中的所有命令

    unwatch:取消watch对所有key的监控

Redis事务使用案例

1)正常执行

2)放弃事务

3)若在事务队列中存在命令性错误(类似于java编译性错误),则执行EXEC命令时,所有命令都不会执行

4)若在事务队列中存在语法性错误(类似于java的1/0的运行时异常),则执行EXEC命令时,其他正确命令会被执行,错误命令抛出异常

5)使用watch

案例一:使用watch检测balance,事务期间balance数据未变动,事务执行成功

案例二:使用watch检测balance,在开启事务后(标注1处),在新窗口执行标注2中的操作,更改balance的值,模拟其他客户端在事务执行期间更改watch监控的数据,然后再执行标注1后命令,执行EXEC后,事务未成功执行

一但执行 EXEC 开启事务的执行后,无论事务使用执行成功, WARCH 对变量的监控都将被取消。

故当事务执行失败后,需重新执行WATCH命令对变量进行监控,并开启新的事务进行操作。

 总结:

  watch指令类似于乐观锁,在事务提交时,如果watch监控的多个KEY中任何KEY的值已经被其他客户端更改,则使用EXEC执行事务时,事务队列将不会被执行,同时返回Nullmulti-bulk应答以通知调用者事务执行失败

10.缓存雪崩和缓存穿透问题

什么是缓存雪崩?

    缓存同一时间大面积的失效,后面的请求都会落在数据库上,会造成数据库短时间内承受大量请求而崩掉

雪崩解决的办法

    1)事前:尽量保证整个redis集群的高可用性,发现机器宕机尽快补上,选择何时的内存淘汰策略

    2)事中:本地encache缓存加hystrix限流和降级,避免MySql崩掉

    3)事后:利用redis持久化机制保存的数据尽快恢复缓存

什么是缓存穿透?

    缓存穿透就是大量请求的key根本不存在于缓存中,导致请求直接到了数据库上,根本没有经过缓存这一层。一般Mysql默认的最大连接数是150,但CPU,内存,磁盘,网络等都是指标,3000个并发请求就能搞死大部分数据库。

缓存穿透解决办法

    1)布隆过滤

         对所有可能查询的参数以hash形式存储,在控制层先进行校验,不符合则丢弃。最常见的是用布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被这个bitmap拦截掉,从而避免了对底层存储系统的查询压力。

        Bloom filter。适用范围:实现数据字典,进行数据的判重,或者集合求交集

        基本原理:位数组+K个独立hash函数。将hash函数对应的值的位数组置1,查找时如果发现所有hash函数对应位都是1说明存在,这个过程不保证查找结果时100%。同时也不支持删除一个已经插入的关键字。

    2)缓存空对象,将null变成一个值

        如果一个查询返回的数据为空,把这个空结果进行缓存,过期时间很短,不会超过五分钟。但会有两个问题:

第一,空值做了缓存,意味着缓存层中存了更多的键,需要更多的内存空间 ( 如果是攻击,问题更严重 ),比较有效的方法是针对这类数据设置一个较短的过期时间,让其自动剔除。

 第二,缓存层和存储层的数据会有一段时间窗口的不一致,可能会对业务有一定影响。例如过期时间设置为 5分钟,如果此时存储层添加了这个数据,那此段时间就会出现缓存层和存储层数据的不一致,此时可以利用消息系统或者其他方式清除掉缓存层中的空对象。

11.解决Redis的并发竞争key问题

1)客户端加锁(synchronized)

2)乐观锁(watch命令)

    当执行多键值事务操作时,Redis不仅要求这些键值需要落在同一个Redis实例上,还要求落在同一个slot上

3)redis的setnx实现内置的锁:要设置超时时间,防止抢占到锁的客户端因崩溃造成死锁。

12.如何保证缓存与数据库双写时的数据一致性?

具体讲解

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值