Redis :是一种非关系型数据库,以键值对的形式进行存储(key-value),主要用来做数据缓存,Redis运行在内存中,数据也保存在内存中,目的在于让数据离浏览器更进,
Redis的它的读写速度非常快,结构简单易拓展,高性能,灵活的数据类型。
String是Redis最基础的数据结构类型,它是二进制安全的,可以存储图片或者序列化的对象,值最大存储为512M。
简单使用举例: set key value
、get key
等
内部编码有3种:int(8字节长整型)/embstr(小于等于39字节字符串)/raw(大于39个字节字符串)
应用场景
在Redis中,哈希类型是指v(值)本身又是一个键值对(k-v)结构.
简单使用举例:hset key field value
、hget key field
内部编码:ziplist(压缩列表)
、hashtable(哈希表)
应用场景:缓存用户信息等。
列表(list)类型是用来存储多个有序的字符串
内部编码:ziplist(压缩列表)、linkedlist(链表)
应用场景参考以下:
集合(set)类型也是用来保存多个的字符串元素,但是不允许重复元素。
底层内部编码:ziplist(压缩列表)
、skiplist(跳跃表)
应用场景:
已排序的字符串集合,同时元素不能重复。
底层内部编码:ziplist(压缩列表)
、skiplist(跳跃表)
应用场景:
性能极高(读写速度快)(基于内存实现)
丰富的数据类型(list、set、Map等)
原子性 (所有的操作都是原子性的)
丰富的特性 (支持publish,通知等)
合理的线程模型(单线程模型:避免上下文切换;IO多路复用)
虚拟内存机制
我们所查询的数据在数据库中没有,查询后也不在缓存中存,每次还是回去访问数据库。
特点:数据库没有,缓存也没有
解决:
本来数据库没有,将查询出来的空值放到缓存中。key = null
对查询的参数进行验证
数据库有数据,只是某个热点key在某个时间点上过期了,此时有大量查询请求到达(查询是不加锁),查询缓存没有,一起都向数据库发送请求,导致数据库崩溃。
解决:
可以设置热点的key过期时间要把握好 永不过期
查询缓存后,访问数据库时,可以加锁 (使用互斥锁方案)
大量的热点key过期或者redis服务器故障,导致大量的请求到达数据库。
解决方案:
随机设置有效时间,避免同时生效
把不同的热点key放在不同的redis服务器上(集群)
设置较长的过期时间
在java中设置定时任务,去检测key是否过期
什么是热Key呢?
在Redis中,我们把访问频率高的key,称为热点key。
如果某一热点key的请求到服务器主机时,由于请求量特别大,可能会导致主机资源不足,甚至宕机,从而影响正常的服务。
热点Key是怎么产生的呢?主要原因有两个:
key的过期策略
为key设置过期时间,那么时间到了后,Redis如何处理过期的key
立即删除。到期立即执行回调函数,立即释放内存,对redis性能有影响。
惰性删除。到期不会立即删除,到下次使用该键的时候,根据状态(设置时是会记录的),来决定是删除还是继续使用,占用内存)
定期删除。每隔一段时间对所有到期的键进行删除(类似于java中垃圾回收线程)
Redis中同时使用了惰性过期和定期过期两种过期策略。
Redis是基于内存的非关系型K-V数据库,既然它是基于内存的,如果Redis服务器挂了,数据就会丢失。为了避免数据丢失了,Redis提供了持久化,即把数据保存到磁盘。
Redis提供了RDB和AOF两种持久化机制。
RDB,就是把内存数据以快照的形式保存到磁盘上。
配置触发持久化的机制:
save m n save 多少秒内 多少键
flushall 命令
退出
RDB 的优点
RDB缺点
AOF:
以日志的形式,将写命令存储到文件 set name jim
还原时,将命令逐个执行还原数据
默认是不开启的 appendonly no
同步机制
appendfsync always 每次set记录一次
appendfsyns everysec 每秒记录一次
AOF的优点
AOF的缺点
单点部署一旦宕机,就不可用了。为了实现高可用,通常的做法是,将数据库复制多个副本以部署在不同的服务器上,其中一台挂了也可以继续提供服务。Redis 实现高可用有三种部署模式:主从模式,哨兵模式,集群模式。
主从---> 主机和从机(集群架构)
主机负责写数据,将数据同步到从机,一般的读数据从从机查询,
实现读写分离:写命令由主机执行,而读命令由从机执行。
如果只有一台redis服务,万一服务宕机,所有的请求都会到达mysql,导致mysql宕机,可以搭建多台redis服务器,如果其中一台出现故障,其他服务可以正常使用。
有一个单独线程,对集群中的多态服务进行监听,给每个服务发请求,如果没有响应,表明出现故障。
比如,主机出现宕机,会在从机随机选取一台当做主机。当原来主机恢复后,又可以当做主机继续使用。
分布式锁,是控制分布式系统不同进程共同访问共享资源的一种锁的实现。秒杀下单、抢红包等等业务场景,都需要用到分布式锁,我们项目中经常使用Redis作为分布式锁。
Redis分布式锁的几种实现方法:
分布式锁可能存在锁过期释放,业务没执行完的问题。有些小伙伴认为,稍微把锁过期时间设置长一些就可以啦。其实我们设想一下,是否可以给获得锁的线程,开启一个定时守护线程,每隔一段时间检查锁是否还存在,存在则对锁的过期时间延长,防止锁过期提前释放。
跳跃表
什么是延时双删呢?
因为延时双删可能会存在第二步的删除缓存失败,导致的数据不一致问题。
优化:删除失败就多删除几次呀,保证删除缓存成功就可以了,所以可以引入删除缓存重试机制
删除缓存重试流程
重试删除缓存机制还可以吧,就是会造成好多业务代码入侵。其实,还可以这样优化:通过数据库的binlog来异步淘汰key。
以mysql为例吧
redis是单线程模式还是多线程模式。
不同版本的区别:
6.x之前是真正意义上的单线程,处理客户端的连接和执行操作的命令,都是由一个线程完成的。
6.x之后引入多线程,处理客户端请求是由专门的线程处理,执行命令还是单线程。
为什么是单线程模式,速度还非常快?
数据存储在内存中,读取速度快,cpu不是性能瓶颈
结构简单,key-value 底层是哈希结构,查询操作速度是O(1)
采用IO多路复用,非阻塞IO模型,提高连接访问效率
单线程执行命令,不存在线程切换,节省开销,而且是线程安全的。
redis在执行单条命令时,是原子性(单线程的一次只能有一个线程执行命令),有时候,一次操作需要执行多条命令,如何保证多条命令整体执行。
可以通过redis事务执行:
开启事务:multi
添加命令:... 命令添加进来不会立即执行,添加到一个多列。
执行exec命令时,才会将对列中的多条命令依次执行,执行一个事务多条命令时,其他客户端会被隔离,不会交替执行,但是事务不保证多条命令执行的原子性(假如执行3条命令,其中一条语法错误,那么会将其他两条正确的命令继续执行)
Redis为了解决哈希冲突,采用了链式哈希。链式哈希是指同一个哈希桶中,多个元素用一个链表来保存,它们之间依次用指针连接。
为了保持高效,Redis 会对哈希表做rehash操作,也就是增加哈希桶,减少冲突。为了rehash更高效,Redis还默认使用了两个全局哈希表,一个用于当前使用,称为主哈希表,一个用于扩容,称为备用哈希表。
可以的,Redis提供两个指令生成RDB,分别是save和bgsave。
RESP,英文全称是Redis Serialization Protocol,它是专门为redis设计的一套序列化协议。这个协议其实在redis的1.2版本时就已经出现了,但是到了redis2.0才最终成为redis通讯协议的标准。
RESP主要有实现简单、解析速度快、可读性好等优点。
应对缓存穿透问题,我们可以使用布隆过滤器。布隆过滤器是什么呢?
布隆过滤器是一种占用空间很小的数据结构,它是由一个很长的二进制向量和一组Hash映射函数组成,它用于检索一个元素是否在一个集合中,空间效率和查询时间都比一般的算法要好的多,缺点是有一定的误识别率和删除困难。
布隆过滤器原理:
假设我们有个集合A,A中有n个元素。利用k个哈希散列函数,将A中的每个元素映射到一个长度为a位的数组B中的不同位置上,这些位置上的二进制数均设置为1。如果待检查的元素,经过这k个哈希散列函数的映射后,发现其k个位置上的二进制数全部为1,这个严肃很可能属于集合A,反之,一定不属于集合A。
例:假设集合A有3个元素,分别为{d1,d2,d3}。有1个哈希函数,为Hash1。现在将A的每个元素映射到长度为16位数组B。
我们现在把d1映射过来,假设Hash1(d1)= 2,我们就把数组B中,下标为2的格子改成1,如下:
我们现在把d2也映射过来,假设Hash1(d2)= 5,我们把数组B中,下标为5的格子也改成1,如下:
接着我们把d3也映射过来,假设Hash1(d3)也等于 2,它也是把下标为2的格子标1:
因此,我们要确认一个元素dn是否在集合A里,我们只要算出Hash1(dn)得到的索引下标,只要是0,那就表示这个元素不在集合A,如果索引下标是1呢?那该元素可能是A中的某一个元素。因为你看,d1和d3得到的下标值,都可能是1,还可能是其他别的数映射的,布隆过滤器是存在这个缺点的:会存在hash碰撞导致的假阳性,判断存在误差。
如何减少这种误差呢?
这样即使存在误差,我们可以发现,布隆过滤器并没有存放完整的数据,它只是运用一系列哈希映射函数计算出位置,然后填充二进制向量。如果数量很大的话,布隆过滤器通过极少的错误率,换取了存储空间的极大节省,还是挺划算的。