Redis是什么
Redis是一个基于内存的key-value数据库。(内存:性能高,nosql:不实用sql作为查询语言)
特性
- 它支持数据持久化,可以将内存中的数据保存在磁盘中,重启时可再次加载进行使用。
- 有丰富的数据结构(string,list,hash,set,zset),不止5中
- 支持数据备份(主从复制)
单value值可存储的最大限制为1GB
有丰富的api,可设置过期时间
数据类型
String
简单的key-value类型,value值可以是string,也可以是数字。可以用来做计数器(微博数,粉丝数,限制请求数)
incr key如果key不存在,值会被初始化为0,然后在执行incr操作
Hash
适合存储对象,value是一个string类型的filed/value映射表
List
list是一个双向链表,支持反向查找和遍历
Set
set可以进行自动排重,可以判断某个成员是否在集合内,还可以实现交集、并集、差集操作(共同关注,共同粉丝等)
Sorted Set
比set多了个权重参数score,使得集合中的元素能够按照score进行排列(比如礼物排行榜,或者点赞排行榜)
能做什么
缓存
什么是缓存呢?缓存就是在内存中存储的数据备份,当数据未发生变化时,直接从缓存中读取数据,从而避免直连数据库进行查询操作,这样大大降低了数据库的读写次数。并且从内存中读取数据要比从数据库查询速度快。
我们使用redis主要用作数据缓存。(基于redis的特性,选用其作为缓存)
什么数据需要保存在缓存中?(经常被访问的,增删少的数据,比如官网首页数据)
排行榜
适用redis的SortSet数据结构可以很方便
计算器/限速器
利用redis的原子性的自增操作,可以统计类似用户点赞数、用户访问数等。(这类操作频繁,如果通过数据库实现功能,频繁的读写会对数据库带来很大的压力)
限速器的场景是限制某个用户访问某个api的频率,例如抢购,防止用户疯狂点击带来不必要的压力
好友关系
利用集合的一些命令,通过求交集、并集、差集,可以实现获取共同好友、共同爱好等功能
简单消息队列
除了redis自身的发布/订阅模式,也可以利用list来实现一个队列机制。比如将一些通知类消息,邮件通知,短信通知等放入队列中,其他模块进行处理,使应用进行了解耦。
使用list结构作为队列,rpush生产消息,lpop消费消息。到lpop没有消息的时候,就适当的sleep,或者使用blpop,在没有消息的时候,它会阻塞直到消息到来。
有主题订阅模式,可以实现消息队列。但在消费者下线的情况下,生产的消息会丢失。得使用专业的消息队列。
lpush mylist插入队列 lrange mylist 0 -1 查询队列 rpop mylist弹出元素(需要不停调用rpop方法才能知道是否有消息)
brpop/blpop实现阻塞读取
session共享
用户登陆的session是保存在服务器的文件中,如果是集群服务,同一个用户的请求可能落到不同的服务器上,这就会导致用户登陆失效,需频繁登陆,影响体验。使用redis来保存session,无论用户请求哪个服务器都能够获取到对应的session信息。
分布式锁
使用setNx命令来争抢锁,抢到后,给一个失效时间,防止忘记锁释放
Redis Setnx(SET if Not eXists) 命令在指定的 key 不存在时,为 key 设置指定的值。
不能做什么
面试期间,有的面试官询问过,可不可以做数据库。
可以做,但是不建议。
- Redis虽然有持久化的功能,但最终是通过将数据读取到内存上,来进行操作的。内存与硬盘相比,价格高,且空间小,不适用(仅作为数据库保存在内存中纯属资源浪费,成本也会过高)
- 它的持久化方案并不能保证数据落地(持久化方式有rdb,aof,或者混搭,如果宕机,这个时候还未持久化到硬盘上,会有数据丢失)
- 频繁持久化会增大服务器压力,从而降低了redis性能
Redis单线程理解
redis客户端对服务端的每次调用,都经历了:发送命令、执行命令、返回结果,这3个过程。
其中执行命令阶段,由于redis是单线程来处理命令的,所有到达服务端的命令都不会立刻执行,而是先进入一个队列中逐个执行。多个客户端发送的命令执行顺序是不确定的,但可以确定两条命令不会被同时执行,所以不会产生并发问题。这就是redis的单线程基本模型。
redis单线程为什么快?
由于纯内存操作,所以速度非常快
采用单线程的优点,避免了不必要的上下文切换和竞争条件,不用考虑多线程切换以及锁问题。所以没有线程安全问题。
这样会导致无法发挥多核cpu性能,不过可以通过在单机开多个redis实例来提高cpu使用率
I/O多路复用
https://blog.csdn.net/happy_wu/article/details/80052617
多个socket复用同一个I/O
Redis回收策略
当Redis内存已满时,由于内存大小固定,如果继续往里添加数据,就需要淘汰一部分老的数据,释放内存空间来存储新的数据
https://blog.csdn.net/zzl429556205/article/details/103246234
Redis持久化策略
https://blog.csdn.net/zzl429556205/article/details/103246915
原子性
原子性指的是,一个事务是一个不可分割的最小工作单位,要么都成功,要么都失败
Redis的所有单个命令的执行都是原子性的
redis实现事务原理
- 批量操作在发送EXEC命令前被放入队列缓存
- 收到EXEC命令后进入事务执行,事务中任意命令执行失败,其余的命令都不会被执行
- 在事务执行过程中,其他客户端提交的命令请求不会插入到事务执行命令序列中
Java实现对redis操作
Jedis
Jedis是Redis官放推荐的面向java的操作Redis客户端。
RedisTemplate
RedisTemplate是SpringDataRedis中对JedisApi的高度封装。SpringDataRedis相对于Jedis可以方便的更换Redis的java客户端,比Jedis多了自动管理链接池的特性。方便与SpringCache搭配使用。
使用RedisTemplate需要进行序列化,方式有:
- GenericToStringSerializer: 可以将任何对象泛化为字符串并序列化
- Jackson2JsonRedisSerializer: 跟JacksonJsonRedisSerializer实际上是一样的
- JacksonJsonRedisSerializer: 序列化object对象为json字符串
- JdkSerializationRedisSerializer: 序列化java对象(被序列化的对象必须实现Serializable接口,不易读)
- StringRedisSerializer: 简单的字符串序列化
https://blog.csdn.net/hotdust/article/details/51832926
RedisTemplate默认使用JDK序列化(存储复杂类型对象)
StringRedisTemplate使用的是String序列化(存储的是字符串类型数据使用)
JedisPool
JedisPool是一个线程安全的网络连接池,可以创建和管理Jedis实例,降低了开启、关闭连接的开销。
原文:https://www.cnblogs.com/niceyoo/p/10826450.html
序列化
序列化:把对象转换为可传输的子节序列
反序列化:把子节序列还原为对象的过程
为什么需要序列化?
序列化的最终目的是为了对象可以跨平台存储和进行网络传输。
当你使用redis设置key/value时,value对于redis来讲就是一个byte array
Redis失效时间原理
Redis对存储值的过期处理实际上是针对key的处理。
- 消极方法:每次访问key时判断key是否过器
- 积极方法:周期性的从设置了过期时间的key中选择一部分的key进行删除(随机测试20个带有失效时间的key,如果超过25%的key被删除,则重复整个流程)
不设置过期时间,就是永久key
可以通过 expire key time来设置过期时间,setNx(key, time, value)是字符串独有的设置失效时间方式
获取key列表
keys命令,遍历方式复杂度是O(n),由于redis是单线程,keys是以阻塞的方式执行的。阻塞时间会过长(keys abc*)
scan命令,是以非阻塞方式进行key值的查找。(scan是一个基于游标的迭代器,每次调用都要需要上一次返回的游标作为参数)
缓存雪崩
缓存同一时间大面积失效,这个时候来了一波请求,导致数据库压力急增,可能会导致数据库连接异常
解决方案:
给缓存失效时间加上随机值,避免集体失效
缓存击穿
故意请求缓存中不存在的数据,导致数据库压力增加
解决方案:
第一次请求无数据,则设置一个短时间失效的缓存,返回空值。再次访问,将不查询数据库,直接返回空值。
管道机制
可以将多次IO往返的时间缩减为一次(将多个命令一次性发送都redis服务端进行处理)
主从/哨兵/集群
主从复制备份了数据,可以做读写分离
哨兵模式,主要提供了redis高可用,master宕机后,会进行选取某个slave提升为master,继续提供服务
集群,主要为了解决redis内存不足时,怎么去扩展内存。使用cluster进行分片存储。
Redis连接异常
对于连接异常,为保证后续任务继续执行,需要在cache中,执行查库操作
try{
redis操作
}catch(redis连接异常){
//请求mysql拿数据
}
Redis事务
Redis 通过 MULTI、EXEC、WATCH 等命令来实现事务(transaction)功能。事务提供了一种将多个命令请求打包,然后一次性、按顺序地执行多个命令的机制,并且在事务执行期间,服务器不会中断事务而改去执行其他客户端的命令请求,它会将事务中的所有命令都执行完毕,然后才去处理其他客户端的命令请求。
redis同一个事务中如果有一条命令执行失败,其后的命令仍然会被执行,没有回滚
缓存对比
缓存技术 | 数据类型 | 类型 | 持久化 |
redis | 丰富(8种) | 内存数据库,nosql | aof,rdb,混搭 |
memcached | 文本 | 内存数据库,key-value | 不支持 |
话外
sql层面的缓存几乎没有价值(mybatis二级缓存),所以缓存是基于业务。