Redis
-
基于内存运行,性能高效
-
支持分布式,理论上可以无限扩展
-
key-value存储系统
-
开源的使用ANSI C语言编写 , 遵守BSD协议 , 支持网络 , 可基于内存亦可持久化的日志型 , Key-value数据库
-
特点:
- C/S通讯模型
- 单进程单线程模型
- 丰富的数据类型
- 操作具有原子性
- 持久化
- 高并发读写
- 支持lua脚本
-
应用场景:
- 缓存系统('热点'数据:高频读,低频写)
- 计数器
- 消息队列系统
- 排行榜
- 社交网络
- 实时系统
Redis的数据类型:
String类型 ,哈希类型,列表类型,集合类型和顺序集合类型
String类型:
常用命令:
- GET/MGET
- SET/SETEX/MSET/MSETNX
- INCR/DECR
- GETSET
- DEL
哈希类型:
由field和关联的value组成的map。其中field和value都是字符串类型的。
常用命令:
- HGET/HMGET/HGETALL
- HSET/HMSET/HSETNX
- HEXISTS/HLEM
- HKEYS/HDEL
- HVALS
列表类型:
是一个插入顺序的字符串元素集合,基于双链表实现
常用命令:
- LPUSH/LPUSHX/LPOP/RPUSH/RPUSHX/RPOP/LINSERT/LSET
- LINDEX/LRANGE
- LLEN/LTRIM
集合类型:
Set类型是一种无顺序集合,它和List类型最大的区别是:集合中的元素没有顺序,且元素是唯一的。
Set类型的底层是通过哈希表实现的,其操作命令为:
- SADD/SPOP/SMOVE/SCARD
- SINTER/SDIFF/SDIFFSTORE/SUNION
应用场景:
- 在某些场景中,通过交集,并集和差集运算,通过Set类型可以非常方便地查找共同好友,共同关注和共同偏好等社交关系。
顺序集合类型:
ZSet是一种有序集合类型,每个元素都会关联一个double类型的分数权值,通过这个权值来为集合中的成员进行从大到小的排序。与Set类型一样,其底层也是通过哈希表实现的。
常用命令:
- ZADD/ZPOP/ZMOVE/ZCARD/ZCOUNT
- ZINTER/ZDIFF/ZDIFFSTORE/ZUNION
redis数据结构如下:
1.压缩列表是列表键和哈希键的底层实现之一。当一个列表键只包含少量列表项,并且每个列表项要么就是小整数,要么就是长度比较短的字符串,Redis就会使用压缩列表来做列表键的底层实现。
2.整数集合是集合键的底层实现之一,当一个集合只包含整数值元素,并且这个集合的元素不多时,Redis就会使用整数集合作为集合键的底层实现
简单动态字符串SDS
基于C语言中传统字符串的缺陷,Redis自己构建了一种名为简单动态字符串的抽象类型,简称SDS其结构如下:
struct sdshdr{
//buf数组已经使用字节数量,既SDS字符串长度
int len;
//记录buf数组中未使用字节的数量
int free;
//字节数组,用于博爱村二进制数据
char buf[];
}
SDS几乎贯穿了Redis的所有数据结构,应用十分广泛。
SDS的特点:
Redis巧妙的使用了SDS避免了C字符串的缺陷。在SDS中,buf数组的长度不一定就是字符串的字符数量加一,buf数组里面可以包含未使用的字节,而这些未使用的字节由free属性记录。
与此同时,SDS采用了空间预分配的策略,避免C字符串每一次修改时都需要进行内存重分配的耗时操作,将内存重分配从原来的每次修改N次就分配Nc---》降低到了修改N次最多分配N次。
Redis特性:
1.事务
- 命令序列化,按顺序执行
- 原子性:
- 要么同时成功,要么同时失败
- 三阶段:开始事务-》命令入队-》执行事务
- 命令:
- MULTI/EXEC/disCARD
2.发布订阅
-
Pub/sub是一种消息通讯模式
-
Pub发送消息,Sub接受消息
-
‘fire and forgot’ 发送即遗忘
-
命令:
- Publish/Subscribe/Psubscribe/UnSub
-
类似MQ中的广播通知
3.Stream
- Redis 5.0新增
- 等待消费
- 消费组(组内竞争)
- 消费历史数据
- FIFO
Redis常见问题:
缓存击穿:
概念:在Redis获取某一key时,由于Key不存在,而必须向DB发起一次请求的行为,称为‘Redis击穿’
同一时间访问大量不存在的key,就会造成缓存击穿问题。
原因:
- 第一次访问
- 恶意访问不存在的key
- key过期
规避方案:
- 服务器启动时,提前写入
- 规范Key的命名,通过中间件拦截
- 对某些高频率访问的key,设置合理的过期时间TTL或永不过期
缓存雪崩:
概念:Redis缓存层由于某种原因宕机后,所有的请求会涌向存储层,短时间内的高并发请求可能会导致存储层挂机,称之为“Redis雪崩”。
规避方案:
缺点:
1.缓存和数据库双写一致性问题
一致性问题很常见,因为加入缓存之后,请求时先从Redis中查询,如果redis存在数据就不会走数据库了,如果不能保证缓存和数据库的一致性就会导致请求获取到的数据不是最新的数据:
解决方案:
2.缓存的并发竞争问题
并发竞争,指的是同时有多个子系统去set同一个key值。
解决方案:
- 最简单的方式就是做一个分布式锁,大家去抢锁,抢到锁就做set操作即可
3.缓存雪崩问题
缓存雪崩:既缓存同一时间大面积的失效,这时候又来了一波请求,结果请求都怼到数据库上,从而导致数据库连接异常。
解决方案:
4.缓存击穿问题
缓存穿透:即黑客故意去请求缓存中不存在的数据,导致所有的请求都怼到数据库上,从而数据库连接异常。
解决方案:
- 利用互斥锁,缓存失效的时候,先去获得所,得到锁了,再去请求数据库。没得到锁,则休眠一段时间重试。
- 采用异步更新策略,无论key是否取到值,都直接返回,value值中维护一个缓存失效时间,缓存如果过期,异步起一个线程去读数据库,更新缓存。
面试题:
- 基于内存:Redis是使用内存存储,没有磁盘IO上的开销。数据存在内存中,读写速度快。
- 单线程实现(Redis6.0以前):Redis使用单个线程处理请求,避免了多个线程之间线程切换和锁资源争用的开销。
- IO多路复用模型:Redis采用IO多路复用技术。Redis使用单线程来轮询描述符,将数据库的操作都转换成了事件,不在网络I/O上浪费过多的时间
- 高效的数据结构:Redis每种数据类型都做了优化,目的就是为了追求更快的速度。
2.Redis为何选择单线程呢?
- 单线程实现可以避免过多的上下文切换开销。程序始终运行在进程中单个线程内,没有多线程切换的场景。
- 避免同步机制的开销。如果Redis选择使用多线程模型,需要考虑数据同步的问题,则必然会引入某些数据库过程中带来更多的开销,增加程序复杂度的同时还会降低性能。
- 实现简单,方便维护。如果Redis使用多线程模式,那么所有的底层数据结构的设计都必须考虑线程安全的问题,那么Redis的实现将会变得更加复杂。
3.Redis 的应用场景有哪些?
- 缓存热点数据,缓解数据库的压力
- 利用Redis原子性的自增操作,可以实现计数器的功能,比如用户点赞数、用户访问数等。
- 作为简单的消息队列,实现异步操作
- 限速器,可用于限制某个用户访问某个接口的频率,比如秒杀场景用于防止用户快速带年纪带来的不必要的压力
- 好友关系,利用集合的一些命令,比如交集、并集、差集等,实现共同好友、共同爱好值类的功能。
4.Redis怎么实现消息队列
- 有三种方式可以实现
5.讲讲Redis主从复制的原理?
6.了解过期键的删除策略吗?
- 被动删除。在访问key时,如果发现key已经过期,那么会将key删除。
- 主动删除。定时清理key,每次清理会依次遍历所有DB,从db随机取出20个key,如果过期就删除,如果其中有5个key过期,那么就继续对这个db进行清理,否则开始清理下一个db。
- 内存不够时清理。Redis有最大内存的限制,通过maxmemory参数可以设置最大内存,当使用的内存超过了设置的最大内存,就要进行内存释放,在进行内存释放的时候,会按照配置的淘汰策略清理内存。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。