Redis 全集

redis使用 :

redis是数据库,数据库的思想设计都是类似,像mysql es redis都是相似的。

使用最基础的是 使用它,add del set添加、修改、删除操作。

  add数据的数据类型,像string hash list set zset,

  它是单线程的,删除大量的、占据内存大的key的话,会阻塞,

  影响查询速度。-->因此,设置时,尽量选择string占内存小的、

  而且要避免大量删除(过期时间 雪崩)

 setNx删除:加上过期时间,防止死锁、时间设置均匀一点、带上UUID、requestID 删除策略

                        数据持久化 AOF  内存逐出策略 

集群:分担写的压力、选举机制、哨兵机制、数据主从复制、数据不一致

16个数据库,观察,结构。

数据类型(string hash list set zset)

单线程的,key大 阻塞操作;所以,set时 、del时 要注意;

分布式锁setnx 带上过期时间,requestId, 用redission

过期机制

【使用中的注意点】

学redis就看知乎它的几篇文章

Redis最佳实践:7个维度+43条使用规范,带你彻底玩转Redis | 附最佳实践清单 - 知乎 (zhihu.com)

Redis 在使用中会遇到哪些坑?如何规避? - 知乎 (zhihu.com)

为啥 redis 使用跳表(skiplist)而不是使用 red-black? - 知乎 (zhihu.com)

Redis为什么变慢了?一文讲透如何排查Redis性能问题 | 万字长文 - 知乎 (zhihu.com)

format,png

format,png

  • 部署 Redis 时,如何做好资源规划?
  • 如何降低 Redis 故障发生的频率?

     日常运维 Redis 需要注意什么?

  • Redis 监控重点要关注哪些指标?
  • 我的 Redis 内存为什么增长这么快?
  • 为什么我的 Redis 操作延迟变大了?

1. 对key的操作

key *                    查看库里所有key

dbsize                 库里key的数量

exists key            key是否存在

type key              查看key的类型

del key                删除指定key的数据

ttl key                  查看过期,-1永不过期,-2已过期

2. 常用数据类型

  • 跳跃表是Redis特有的数据结构,就是在链表的基础上,增加多级索引提升查找效率。
  • 跳跃表支持平均 O(logN),最坏 O(N)复杂度的节点查找,还可以通过顺序性操作批量处理节点。

    跳表是一种随机化的数据结构,基于并联的链表,实现简单,插入、删除、查找的复杂度均为 O(logN)。简单说来跳表也是链表的一种,只不过它在链表的基础上增加了跳跃功能,正是这个跳跃的功能,使得在查找元素时,跳表能够提供 O(logN) 的时间复杂度。

    跳表为了避免每次插入或删除带来的额外操作,不要求上下相邻两层链表之间的节点个数有严格的对应关系,而是为每个节点随机出一个层数(level。而且新插入一个节点不会影响其它节点的层数。因此,插入操作只需要修改插入节点前后的指针,而不需要对很多节点都进行调整。

Zset 为何不使用红黑树等平衡树?

1)跳跃表范围查询比平衡树操作简单。 因为平衡树在查询到最小值的时还需要采用中序遍历去查询最大值。 而跳表只需要在找到最小值后,对第一层的链表遍历即可。

2)平衡树的删除和插入需要对子树进行相应的调整,而跳表只需要修改相邻的节点即可。

3)跳表和平衡树的查询操作都是O(logN)的时间复杂度。

4)从整体上来看,跳表算法实现的难度要低于平衡树。

  • String:如果存储数字的话,是用int类型的编码;如果存储非数字,小于等于39字节的字符串,是embstr;大于39个字节,则是raw编码。
  • List:如果列表的元素个数小于512个,列表每个元素的值都小于64字节(默认),使用ziplist编码,否则使用linkedlist编码
  • Hash:哈希类型元素个数小于512个,所有值小于64字节的话,使用ziplist编码,否则使用hashtable编码。
  • Set:如果集合中的元素都是整数且元素个数小于512个,使用intset编码,否则使用hashtable编码。
  • Zset:当有序集合的元素个数小于128个,每个元素的值小于64字节时,使用ziplist编码,否则使用skiplist(跳跃表)编码

有哪些应用场景?
Redis 在互联网产品中使用的场景实在是太多太多,这里分别对 Redis 几种数据类型做了整理:

1)String:个redis字符串value最多是512M  ;

        缓存、限流、分布式锁、计数器、分布式 Session 等。

2)Hash:用户信息、用户主页访问量、组合查询等。

3)List:底层是双向链表。

                简单队列、关注列表时间轴。

4)Set:赞、踩、标签等。

5)ZSet:有序集合

        排行榜、好友关系链表。

3.单线程

使用中:set key(大小)、del(阻塞)、读取时 数据不一致

DEL命令耗时久,阻塞主线程,影响查询速度;

管理好 Key的大小

Redis是单线程的,所以一些耗时的操作(删除key、set key)会导致Redis卡顿,比如当Redis数据量特别大的时候,使用keys * 命令列出所有的key

删除key会阻塞redis:

删除一个 key 的耗时,与这个 key 的类型有关。

1. key的类型不是string,    list hash set zset元素太多

2.string占用太多内存

数据不一致:

redis低版本会 导致 主从库读取过期key 数据不一致(4.0以上的才修复bug)

加锁:

过期时间 (不加上的话,有可能造成死锁合理评估时间,避免集中失效;但也不要时间不够)、

带上UUID;加锁解锁 也可以LUA脚本实现,3行。

主从复制 异步: 主机挂了, 还没来得及 把最新的数据 同步给从,就发起 总从切换,导致 分布式锁 或 数据丢失;

主从库 设置的时钟不一致  导致 数据不一致;

主从库 设置的最大内存不同,触发内存逐出策略 导致数据不一致

主机开启数据持久化,丢失数据不敏感的情况,不开AOF,会拖慢redis性能

定期+惰性 删除过期key,

 第一轮 定期轮训 随机删 导致某些过期key还在内存;

exist api方法 触发第二轮 惰性删,删除过期key;

读是共享的,没必要给 读 加锁;只保证  读写、写写并发 时加锁;

  redis是  执行命令时单线程+OS的多路IO复用(IO多线程)技术
 虽然是单线程,但能有多线程的效果,它是怎么实现的?

什么是I/O多路复用?

多路I/O复用技术可以让单个线程高效的处理多个连接请求,而Redis使用用epoll作为I/O多路复用技术的实现。并且,Redis自身的事件处理模型将epoll中的连接、读写、关闭都转换为事件,不在网络I/O上浪费过多的时间。

  • I/O :网络 I/O
  • 多路 :多个网络连接
  • 复用:复用同一个线程。
  • IO多路复用其实就是一种同步IO模型,它实现了一个线程可以监视多个文件句柄;一旦某个文件句柄就绪,就能够通知应用程序进行相应的读写操作;而没有文件句柄就绪时,就会阻塞应用程序,交出cpu。

延申:NIO模型、epoll模型

为什么redis单线程,还那么快?

1.首先,是因为它做的事情简单,绝大多数命令都是 对单个key的操作,而且没有复杂事务

2. redis存在内存里,比 去磁盘快;

3. 操作系统的多路复用  属于一种NIO(非阻塞IO),我写过NIO模型的代码,有三版的优化。

4. 单线程减少上下文切换---》延伸到计组的 cache 

5. 高级数据结构(如 SDS、Hash以及跳表等)。--->按块(缓存行)读

第一个:阻塞的(BIO blocking)。

阻塞导致的弊端:

1.连接:建立服务器连接,此时没有连接的话,会阻塞;

没办法 同一时刻 处理多个连接( 没办法处理 并发)

2.读数据: read(),没有具体的时间发生,就会阻塞(debug打断点,可以看出);

线程其实就一直在闲置 空等-->浪费线程开销,cpu

对并发、资源敏感的程序,不太友好

于是要优化,就引入了NIO

NIO的非阻塞 其实是由OS内部实现的,底层调用了linux内核的系统函数。

accept函数、epoll_create函数

对socketChannel设置非阻塞后,那么即使客户端没发送数据 也不会被阻塞,仍然可以读,只不过读出的结果是null。

目前的优势:

(ps: 非阻塞的IO多路复用 selector选择器的设计思路,特别像 在做国资委项目的高并发的优化。

不要for循环查库遍历10W,

而是先取出所有数据,过滤出有效的,放到一个set集合里。

)

1. 可以一次性接收很多连接了; 不会因为某个线程的阻塞,导致没办法处理 新的客户端连接;

目前的弊端:

1.大量的空循环、空执行  导致浪费了大量的程序执行时间 CPU

举例:假如10w个连接,但只有100个发送数据的,

那每次都需要遍历10w次,而且读了很多的null,浪费了CPU

于是优化,引入selector选择器,它底层就是epoll实现的

思想:其实就是把那100个有数据的放到另一个set集合里,每次只遍历这100个

selector只关注某个具体的时间(如:连接、读等)

把通道channel注册到selector选择器上,

监听

如果事件发生,就执行;没有,就阻塞;

native方法,是用c语言实现;linux源码使用c和汇编实现的。

#man操作手册

select epoll的区别

select 非阻塞,但没有过滤,

epoll是最好的,这是一个优化的过程

netty也是对NIO封装,并且进行了优化。

byteBuffer这个类不好用,flip方法很容易有bug

常见的缓存使用方式:

读请求来了,先查缓存,缓存有值命中,直接返回;

缓存没命中,就去查数据库,把数据库的值更新到缓存,再返回。

缓存穿透:查询不存在的数据    每次请求都要到数据库去查询,给数据库带来压力。

通俗点说,读请求访问时,缓存和数据库都没有某个值,这样就会导致每次对这个值的查询请求都会穿透到数据库,这就是缓存穿透。

布隆过滤器:判断该key存在

分布式锁:

缓存失效时,不是立即去加载db数据,而是先使用某些带成功返回的原子操作命令,如(Redis的setnx)去操作,成功的时候,再去加载db数据库数据和设置缓存。否则就去重试获取缓存。

缓存雪奔:大量数据同时过期,引起数据库压力过大甚至down机。

  • 缓存雪奔一般是由于大量数据同时过期造成的,均匀设置过期时间解决,即让过期时间相对离散一点。如采用一个较大固定值+一个较小的随机值,5小时+0到1800秒酱紫。
  • Redis 故障宕机也可能引起缓存雪奔。这就需要构造Redis高可用集群。


 

一、在Redis中,假如我们设置了100w个key,这些key设置了只能存活2个小时,那么在2个小时后,redis是如何来删除过期的key?

答案:定期删除 +  惰性删除。

那什么是定期删除?什么的惰性删除?靠这两种策略就可以删除掉redis中过期的key吗?

定期删除:

周期性轮询    随机抽取 策略

Redis会把  所有设置了过期时间的key  放进一个字典里,然后每隔100ms 从字典中随机一些key检查过期时间  并删除 已过期的key。

redis 默认每秒10次  过期扫描  :随机抽取 一些key(注意:是一些,不是 所有),检查是否过期,过期则删除。

如果每隔100ms,就把redis的所有key(假设有1000w的key)都检查一遍,会给CPU很大的负载,redis就卡死了。

因此,弊端: 如果只采用定期删除策略,会导致很多过期的key到时间了  但还没被删除。占用了大量内存资源。

为了解决这个弊端,redis增加了惰性删除策略

惰性删除:对于那些过期的key,靠定期删除策略没有被删除掉,还保留在内存中,

这时候如果系统去主动查询这个key,redis判断已经过期了,才会把这个过期的key删除掉。

内存不够 逐出算法

当新数据进入redis时,如果内存不足怎么办? 

Redis使用内存存储数据,在执行每一个命令前,

会调用freeMemoryIfNeeded()检测内存是否充足。

如果内存不满足新加入数据的最低存储要求,

redis要临时删除一些数据为当前指令清理存储空间。清理数据的策略称为逐出算法。 
注意:逐出数据的过程不是100%能够清理出足够的可使用的内存空间,如果不成功则反复执行。当对所有数据尝试完毕后,如果不能达到内存清理的要求,将出现错误信息。

从redis.conf配置文件里 看到了redis的淘汰机制 。

最近 最少使用的;

使用次数最少的;

 Redis 内存淘汰策略

  • volatile-lru:当内存不足以容纳新写入数据时,从设置了过期时间的key中使用LRU(最近最少使用)算法进行淘汰;
  • allkeys-lru:当内存不足以容纳新写入数据时,从所有key中使用LRU(最近最少使用)算法进行淘汰。
  • volatile-lfu:4.0版本新增,当内存不足以容纳新写入数据时,在过期的key中,使用LFU算法进行删除key。
  • allkeys-lfu:4.0版本新增,当内存不足以容纳新写入数据时,从所有key中使用LFU算法进行淘汰;
  • volatile-random:当内存不足以容纳新写入数据时,从设置了过期时间的key中,随机淘汰数据;。
  • allkeys-random:当内存不足以容纳新写入数据时,从所有key中随机淘汰数据。
  • volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的key中,根据过期时间进行淘汰,越早过期的优先被淘汰;
  • noeviction:默认策略,当内存不足以容纳新写入数据时,新写入操作会报错。

持久化

redis数据放到内存里,但为了防止宕机时数据丢失 快速恢复,选择把数据 持久化备份到磁盘里;

RDB (启动快,但是丢5分钟数据)和 AOF(存 追加的命令,丢一秒钟数据,就是启动慢)

持久化各有利弊,

RDB 文件 是经过压缩的二进制文件,是 某个时间点的完整数据,

恢复大数据集时的速度快

由fork创建 子线程,把数据写入到一个临时文件(需要内存是原来的2倍) 完成持久化;

缺点:Redis 出现宕机,最多可能丢失5分钟数据

 AOF 是像mysql的binlog日志一样  写入命令,

有3种策略:

1.always:每次redis数据发生变化时,把命令追加到aof文件,产生大量的磁盘IO,虽然影响性能,但数据最安全;

2.每秒: 只在每秒 写命令追加到aof文件;宕机时 丢失一秒的数据;

缺点:文件 保存的是 所有的命令,它恢复时 时需要重新执行指令,所以启动速度很慢

混合:

redis重启时,为了启动快,先加载 RDB的内容;

然后有时间了,就再重放增量AOF日志

关键词串联:

分布式锁使用:

1.为了效率,只在一台机器上执行

  用分布式锁,带上过期时间(防止死锁,设置不同的时间,防止统一过期雪崩、单线程:删除耗时,影响查询速度),带上进程idUUID(防止释放别人的锁)

2.已知 定期删除+惰性删除,所以 代码里用exist方法(触发 惰性删除),从内存中删除key

1.   项目是分布式集群,某个发邮件的功能代码,想只需要在一台机器上执行即可(为了效率,不重复 执行),

所以set值的时候 用分布式锁setNX,带上过期时间。

(不带过期时间的话,锁一直存在,防止死锁 导致别的进程无法使用(别的进程对这个key set值时 就无法set值了)。)

(可以调用api判断key是否存在: 定期删除策略 即使没有及时删除这个key,这时调用是否存在方法  就会激发 惰性删除策略   把它给从内存里清除掉)

2. 设置不同的缓存时间,避免大量缓存同一时间失效。

    设置key时,尽量用string 并且 占内存小一点

关键词串联;

集群:

多主实例:红锁,为解决主从切换时,分布式锁丢失(主从切换时,主从尚未同步复制成功,(主从的锁还不一致时 就切换了,从变成主后 没有锁))

DBA: 哨兵模式、一主多从的集群、数据淘汰机制、读写分离、主从同步、主开启数据持久化到磁盘

为了主从数据一致性:

考虑:统一  主从的机器时钟、最大内存、版本、磁盘内存、

【故障】

主从复制 丢数据-----》MQ(消息队列)本质是为了 重试。当删除redis失败后,重试。


 


 

20200729172516880.png

1.解决 功能性问题 而产生的:               java jsp  tomcat html linux jdbc  svn
2.解决扩展性问题 (而产生的框架):      struts springmvc mybatisplus
3.解决性能问题 而产生的:                     nosql(redis)  java线程  hadoop  nginx  mq  es(搜索)

nosql缓存,减少io操作,提高时间效率。
不遵循sql标准
不支持ACID1nn
查询性能高

(3)支持事务,操作都是原子性,所谓的原子性就是对数据的更改要么全部执行,要么全部不执行

存到内存、支持持久化备份恢复、k-v模式存储 list set hash zset

redis默认有16个数据库,一般都是用的0,密码都是相同的 


 

3. 配置文件

4. 发布和订阅

redis发布订阅(pub/sub)是一种通信模式,发送者(pub)发送消息,订阅者(sub)接收消息。

5.事务与锁机制

redi事务的主要作用就是 串联多个命令 防止别的命令插队。按顺序执行。

悲观锁 乐观锁

6.备份

        AOF 数据完整性好

读写分离:

一主多从:

从应用里 写数据到 主服务器里,主服务器把内容复制到 几个从服务器里,

以后应用想°数据的话,就去 从服务器里读。

哨兵模式:

哨兵监听主服务器是否挂了,挂了的话  去从服务器里选举出一个作为新的主服务器。

配置哨兵、启动哨兵

7.redis集群

容量不够,redis如何扩容?---通过集群,解决容量不够的问题

并发写操作,redis如何分摊?--用集群,解决 写的压力;

8.基于redis实现分布式锁(共享锁)

分布式锁:在这段代码上加个分布式锁,部署的3台机器上,只要让其中一个机器执行就可以了。

给集群的某台机器上锁了,那其他机器不知道,

是为了让所有机器都能共享,知道这个资源被上锁了。

SETNX 加锁 (最好 带上过期时间);

删除del key之后,才释放了。

只能等你释放锁之后,别人才能使用;否则就等待;

高并发意味着大流量,需要运用技术手段 抵抗流量的冲击

高性能:性能反映了用户体验,响应时间分别是100毫秒和1秒,给用户的感受是完全不同的。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值