redis 小结

什么是Redis

Redis是一个高性能非关系型(Nosql)的键值(K-V)数据库,与传统数据库不同,Redis的数据存在内存中,读写非常快,被广泛用于缓存.而且redis的操作是原子性的

Redis优缺点

优点:

​ 1.基于内存操作,存取很快

​ 2.Redis是单线程的,避免线程切换开销及多线程的竞争问题.单线程是指网络请求使用一个线程去处理,也就是一个线程处理所有的网络请求,

​ 3.支持多种数据类型 String Hash List Set Zset等

​ 4.支持持久化 Redis使用RDB和AOF两种持久化机制,持久化功能可以有效的避免数据丢失问题

​ 5.支持事务,Redis的操作都是原子性的,同时Redis还支持对几个操作合并后的原子操作执行

​ 6.支持主从复制 主节点会自动将数据同步到从节点,从而读写分离

缺点

​ 1.对结构化查询的支持比较差

​ 2.数据容量受到物理内存的限制,不适合用作海量数据的高性能读写,Redis更适合在较小数据量的操作

​ 3.Redis较难支持在线扩容,在集群容量达到上限时,在线扩容会变得很复杂

Redis为什么会这么快

基于内存: Redis是使用内存存储,没用磁盘io上的开销

单线程(Redis 6.0之前):使用单线程处理请求,避免了多个线程之间线程切换和锁资源的竞争之间的开销

io多路复用 Redis 采用 IO 多路复用技术。Redis 使用单线程来轮询描述符,将数据库的操作都转换成了事件,不在网络I/O上浪费过多的时间

高效的数据结构: Redis 每种数据类型底层都做了优化,目的就是为了追求更快的速度

关于redis6.0的多线程

https://www.bilibili.com/video/BV1HA41147PD?from=search&seid=5765525821085986393&spm_id_from=333.337.0.0

概括为读取和响应操作多线程,计算单线程

Redis为什么选择单线程

1.避免过多的上下文切换的开销,程序始终在进程的单个线程中,没有多线程切换

2.避免同步机制: 如果Redis使用多线程,需要考虑数据一致性的问题

Redis 6.0的多线程

可以充分利用服务器的cpu资源,单线程模型的主线程只能使用一个cpu

多线程任务可以分摊Redis同步io的读写分离

Redis的场景

缓存热点数据

排行表 Zset

利用原子性的自增,可以实现计数器

简单的消息队列,可以使用Redis自身的发布/订阅模式或者List来实现简单的消息队列,实现异步操作。

分布式锁

Redis的数据类型

1.string 最常用的一种数据类型,String类型的值可以是字符串、数字或者二进制,但值最大不能超过512MB。

2.hash Hash 是一个键值对集合

3.Set 无序去重的集合Set 提供了交集、并集等方法,对于实现共同好友、共同关注等功能特别方便。

4.List 有序可重复的集合,底层是依赖双向链表实现的。

5.SortedSet有序Set。内部维护了一个score的参数来实现。适用于排行榜和带权重的消息队列等场景。

特殊的数据类型

1、Bitmap:位图,可以认为是一个以位为单位数组,数组中的每个单元只能存0或者1,数组的下标在 Bitmap 中叫做偏移量。Bitmap的长度与集合中元素个数无关,而是与基数的上限有关。

2、Hyperloglog。HyperLogLog 是用来做基数统计的算法,其优点是,在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定的、并且是很小的。典型的使用场景是统计独立访客。

3、Geospatial :主要用于存储地理位置信息,并对存储的信息进行操作,适用场景如定位、附近的人等。

连接docker 这里6382是我的端口
docker exec -it 8b24bf236869 redis-cli -p 6382 

默认是不开启的
如需开启,需要修改配置文件redis.conf:io-threads-do-reads no,no改为yes

Keys命令存在的问题

redis是单线程的。keys指令会导致线程阻塞一段时间,直到执行完毕,服务才能恢复。scan采用渐进式遍历的方式来解决keys命令可能带来的阻塞问题,每次scan命令的时间复杂度是O(1),但是要真正实现keys的功能,需要执行多次scan。

scan的缺点:在scan的过程中如果有键的变化(增加、删除、修改),遍历过程可能会有以下问题:新增的键可能没有遍历到,遍历出了重复的键等情况,也就是说scan并不能保证完整的遍历出来所有的键。

Redis的数据结构有哪些

Redis的数据结构有简单动态字符串链表字典跳跃表整数集合压缩列表等。

**简单动态字符串:**大家都知道,Redis的底层是用C语言编写,但Redis并没有直接使用C语言传统的字符串表示,而是构建了一种名为简单动态字符串的抽象类型。

链表链表提供了高效的节点重排能力,以及顺序性的节点访问方式,并且可以通过增删节点来灵活地调整链表的长度。链表是列表的底层实现之一

**字典:**字典,又称为符号表(symbol table)、关联数组(associativearray)或映射(map),是一种用于保存键值对(key-value pair)的抽象数据结构。字典在Redis中的应用相当广泛,比如Redis的数据库就是使用字典来作为底层实现的,对数据库的增、删、查、改操作也是构建在对字典的操作之上的。

整数集合: 整数集合(intset)是集合键的底层实现之一,当一个集合只包含整数值元素,并且这个集合的元素数量不多时,Redis就会使用整数集合作为集合键的底层实现。

**压缩列表(ziplist):**压缩列表是Redis为了节约内存而开发的,是由一系列特殊编码的连续内存块组成的顺序型(sequential)数据结构。

跳跃表(skiplist):跳跃表(skiplist)是一种有序数据结构,它通过在每个节点中维持多个指向其他节点的指针,从而达到快速访问节点的目的。跳跃表支持平均O(logN)、最坏O(N)复杂度的节点查找,还可以通过顺序性操作来批量处理节点。跳跃表作是序集合键的底层实现之一

链表、字典等数据结构被广泛地应用在Redis内部不同,Redis只在两个地方用到了跳跃表,比如实现有序集合键

跳表相比于红黑树的优点(redis为什么用跳表不同红黑树):

  • 内存占用更少,自定义参数化决定使用多少内存
  • 查询性能至少不比红黑树
  • 简单更容易实现和维护

最后,说下Redis中的跳跃表和普通的跳跃表有什么区别?

  • Redis中的跳跃表分数(score)允许重复,即跳跃表的key允许重复,如果分数重复,还需要根据数据内容来进字典排序。普通的跳跃表是不支持的。
  • 第1层链表不是一个单向链表,而是一个双向链表。这是为了方便以倒序方式获取一个范围内的元素。
  • 在Redis的跳跃表中可以很方便地计算出每个元素的排名。

过期键的删除策略

惰性删除 : 只有访问这个键时才会检查他是否过期,如果过期则清除,

​ 可以节约cpu资源

​ 大量过期键没有被访问,会一直占用内存

定时删除: 为每个设置过期时间的key都设置一个定时器,到了过期时间就清除

​ 节约内存

​ 占用cpu

定期删除: 每隔一段时间就对键进行检查,删除过期的键

​ 惰性和定时的折中方案

Redis的内存淘汰机制

Redis是基于内存的,所以容量肯定是有限的,有效的内存淘汰机制对Redis是非常重要的。

当存入的数据超过Redis最大允许内存后,会触发Redis的内存淘汰策略。在Redis4.0前一共有6种淘汰策略。

  • volatile-lru:当Redis内存不足时,会在设置了过期时间的键中使用LRU算法移除那些最少使用的键。(注:在面试中,手写LRU算法也是个高频题,使用双向链表哈希表作为数据结构)
  • volatile-ttl:从设置了过期时间的键中移除将要过期的
  • volatile-random:从设置了过期时间的键中随机淘汰一些
  • allkeys-lru:当内存空间不足时,根据LRU算法移除一些键
  • allkeys-random:当内存空间不足时,随机移除某些键
  • noeviction:当内存空间不足时,新的写入操作会报错

前三个是在设置了过期时间的键的空间进行移除,后三个是在全局的空间进行移除

在Redis4.0后可以增加两个

  • volatile-lfu:从设置过期时间的键中移除一些最不经常使用的键(LFU算法:Least Frequently Used))
  • allkeys-lfu:当内存不足时,从所有的键中移除一些最不经常使用的键

这两个也是一个是在设置了过期时间的键的空间,一个是在全局空间。

Redis持久化

redis是基于内存的,为了防止一些意外导致的数据丢失,需要将数据持久化到磁盘上

Redis常见的持久化机制有RDB 和 AOF

RDB (默认)

RDB是redis默认的持久化方式,按照一定的间隔时间将内存的数据以快照的形式保存到硬盘,恢复时是将快照读取到内存中,RDB持久化实际操作过程是fork一个子进程,先将数据集写入临时文件,写入成功后,再替换之前的文件,用二进制压缩存储

优点

适合对大规模的数据恢复,比AOF的启动效率高

只有一个文件 dump.rdb,方便持久化

性能最大化,在开始持久化时,它唯一需要做的只是fork出子进程,之后再由子进程完成这些持久化的工作,这样就可以极大的避免服务进程执行IO操作了。

缺点

数据安全性低,在一定间隔时间内做一次备份,如果Redis突然宕机,会丢失最后一次快照的修改

由于RDB是通过fork子进程来协助完成数据持久化工作的,因此当数据集较大时,可能会导致整个服务器停止服务几百毫秒,甚至是1秒钟。

AOF

AOF持久化以日志的形式记录服务器所处理的每一个写、删除操作,查询操作不会记录,以文本的方式记录,可以打开文件看到详细的操作记录

优点:

  • 具备更高的安全性,Redis提供了3种同步策略,分别是每秒同步、每修改同步和不同步。相比RDB突然宕机丢失的数据会更少,每秒同步会丢失一秒种的数据,每修改同步会不会丢失数据。
  • 由于该机制对日志文件的写入操作采用的是append模式,因此在写入过程中即使出现宕机现象,也不会破坏日志文件中已经存在的内容。
  • AOF包含一个格式清晰、易于理解的日志文件用于记录所有的修改操作,可以通过该文件完成数据的重建。

缺点:

  • 对于相同数量的数据集而言,AOF文件通常要大于RDB文件。RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快。
  • 根据AOF选择同步策略的不同,效率也不同,但AOF在运行效率上往往会慢于RDB。

Redis的分布式

方案一 在 lua 脚本里面 set ex px nx + value(唯一性)

锁过期释放了,业务还没执行完

方案二 Redisson框架

watch dog 机制

方案三 : redlock

如果线程一在Redis的master节点上拿到了锁,但是加锁的key还没同步到slave节点。恰好这时,master节点发生故障,一个slave节点就会升级为master节点。线程二就可以获取同个key的锁啦,但线程一也已经拿到锁了,锁的安全性就没了。

RedLock算法

假设有5个完全独立的Redis服务器,多节点Redis实现的RedLock算法如下

  • 获取当前时间戳
  • 客户端尝试在5个实例中按顺序获取锁,在所有实例中使用相同的键名和随机值。当在每个实例中设置锁时,需要将锁的获取时间设置为比锁过期短很多。例如,如果锁自动释放时间为10秒,则锁的获取时间在5-50毫秒。这是为了不要过长时间等待已经关闭的Redis实例,如果一个Redis实例不可用,我们应该尽快尝试获取下一个Redis实例的锁。
  • 客户端通过从当前时间中减去步骤1中获得的时间戳,计算出获取锁所需的时间。当且仅当客户端能够在大多数实例(至少3个)中获得锁,并且花费在获取锁的总时间小于锁的有效性时间时,该锁被认为已经获得。
  • 如果获得了锁,锁真正的有效时间为锁初始设置的有效时间(过期时间)减去第三步的时间,例如,锁初始有限时间为5s,获取锁花了0.5s,则锁真正的有效时间为4.5s(忽略了时钟漂移,时间漂移指指两个电脑间时间流速基本相同的情况下,两个电脑(或两个进程间)时间的差值)
  • 如果客户端由于某些原因无法获得锁(要么无法锁定N/2+1个Redis实例,要么有锁的有效时间为负数),客户端将尝试解锁所有Redis实例(即使是它认为无法锁定的Redis实例)。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值