Redis

Redis面试题 Redis

1、NoSQL

技术分类

① 解决功能性的问题:JavaJsp、RDBMS、TomcatHTMLLinuxJDBC、SVN

② 解决扩展性的问题:Struts、SpringSpringMVC、Hibernate、MyBatis

③ 解决性能的问题:NoSQLJava线程、Hadoop、Nginx、MQ、ElasticSearch

  • 什么是NoSQL数据库?

    NoSQL(NoSQL = Not Only SQL),意即“不仅仅是SQL”,泛指非关系型的数据库。

    NoSQL不依赖业务逻辑方式存储,而以简单的key-value模式存储。因此大大的增加了数据库的扩展能力。

    • 不遵循SQL标准
    • 不支持ACID
    • 远超于SQL的性能
  • 哪里需要NoSQL适用场景?

    → 对数据高并发的读写

    海量数据的读写

    → 对数据高可扩展性

  • 哪里不适合适用NoSQL?

    → 需要事务支持

    基于sql的结构化查询存储,处理复杂的关系,需要即席查询

    → 用不着sql的和用了sql也不行的情况,请考虑使用NoSQL


  • Redis的好处
    • 几乎覆盖了Memcached的绝大部分功能
    • 数据都在内存中,支持持久化主要用作备份恢复
    • 除了支持简单的key-value模式,还支持多种数据结构的存储,比如list、set、hash、zset
    • 一般是作为缓存数据库辅助持久化的数据库
  • MongoDB的好处
    • 高性能、开源、模式自由(schema free)的文档型数据库
    • 数据都在内存中,如果内存不足,把不常用的数据保存到硬盘中
    • 虽然是key-value模式,但是对value(尤其是json)提供了丰富的查询功能
    • 支持二进制数据及大型对象
    • 可以根据数据的特点代替RDBMS,成为独立的数据库。或者配置RDBMS,存储特定的数据。

2、Redis概述

Ø Redis是一个开源的key-value存储系统

Ø 和Memcached类似,它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)、zset(sorted set -- 有序集合)和hash(哈希类型)。

Ø 这些数据类型都支持push/pop、add/remove取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。

Ø 在此基础上,Redis支持各种不同方式的排序。

Ø 与memcached一样,为了保证效率,数据都是缓存在内存中。

Ø 区别的是Redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件。

Ø 并且在此基础上实现了master-slave(主从)同步。


3、Redis应用场景

  • 配合关系型数据库做高速缓存

    → 高频次,热门访问的数据,降低数据库IO

    分布式架构,做session共享

  • 多样的数据结构存储持久化数据


4、Redis关键技术

Redis是单线程 + 多路IO复用技术

多路复用是指使用一个线程来检查多个文件描述符(Socket)就绪状态,比如调用select和poll函数,传入多个文件描述符,如果有一个文件描述符就绪,则返回,否则阻塞直到超时。得到就绪状态后进行真正的操作可以在同一个线程里执行,也可以启动线程执行(比如使用线程池)

串行 vs 多线程 + 锁(memcached) vs 单线程 + 多路IO复用(Redis)


5、Redis键(key)

keys* 查看当前库所有key(匹配:keys * 1)

exists key 判断某个key是否存在

type key 查看你的key是什么类型

del key 删除指定的key数据

unlink key 根据value选择非阻塞删除

仅将keys从keyspace元数据中删除,真正的删除会在后续异步操作

expire key 10 10秒钟:为给定的key设置过期时间

ttl key 查看还有多少秒过期,-1表示永不过期,-2表示已过期

select 命名切换数据库

dbsize 查看当前数据库的key的数量

flushdb 清空当前库

flushall 通杀全部库


6、常用命名

String

  • set <key><value>添加键值对

    *NX:当数据库中key不存在时,可以将key-value添加数据库

    *XX:当数据库中key存在时,可以将key-value添加数据库,与NX参数互斥

    *EX:key的超时秒数

    *PX:key的超时毫秒数,与EX互斥

  • get <key>查询对应键值

  • append <key><value>将给定的<value>追加到原值的末尾

  • strlen <key>获得值的长度

  • setnx <key><value>只有在key不存在时 设置key的值

  • incr <key>

    将key中储存的数字值增1

    只能对数字值操作,如果为空,新增值为1

  • decr <key>

    将key中储存的数字值减1

    只能对数字值操作,如果为空,新增值为-1

  • incrby / decrby <key><步长>将key中储存的数字值增减。自定义步长。


  • mset <key1><value1><key2><value2>....

    同时设置一个或者多个key-value对

  • mget <key1><key2><key3>.....

    同时获取一个或多个value

  • msetnx .....

    同时设置一个或多个key-value对,当且仅当所有给定的key都不存在

    原子性,有一个失败则都失败


  • getrange <key><起始位置><结束位置>

    获得值的范围,类似java中的substring,前包,后包

  • setrange <key><起始位置><value>

    复写所储存的字符串值,从<起始位置>开始(索引从0开始)


  • setex <key><过期时间><value>

    设置键值的同时,设置过期时间,单位秒

  • getset <key><value>

    以新换旧,设置了新值同时获得旧值


Redis集合(Set)

Redis set对外提供的功能与list类似是一个列表的功能,特殊之处在于set是可以自动排重的,当你需要存储一个列表数据,又不希望出现重复数据时,set是一个很好的选择,并且set提供了判断某个成员是否在一个set集合内的重要接口,这个也是list所不能提供的。

Redis的Set是string类型的无序集合。它底层其实是一个value为null的hash表,所以添加,删除,查找复杂度都是O(1)。

  • sadd <key><value1><value2>...

    将一个或多个member元素加入到集合key中,已经存在的member元素将被忽略

  • smembers <key> 取出该集合的所有值

  • sismember <key><value> 判断集合<key>是否为含有该<value>值,有1,没有0

  • scard <key> 返回该集合的元素个数

  • srem <key><value1><value2>.... 删除集合中的某个元素

  • spop <key> 随机从该集合中吐出一个值

  • srandmember <key><n> 随机从该集合中取出n个值。不会从集合中删除

  • smove <source><destination>

    value把集合中一个值从一个集合移动到另一个集合

  • sinter <key1><key2> 返回两个集合的交集元素

  • sunion <key1><key2> 返回两个集合的并集元素

  • sdiff <key1><key2> 返回两个集合的差集元素(key1中的,不包含key2中的)


Redis哈希(Hash)

Redis hash是一个键值对集合

Redis hash是一个string类型的field和value的映射表,hash特别适合用于存储对象

类似Java里面的Map<String,Object>

用户ID为查找的key,存储的value用户对象包含姓名,年龄,生日等信息,如果用普通的key / value结构来存储

主要有以下2种存储方式:

  • hset <key><field><value> 给<key>集合中的<field>键赋值<value>

  • hget <key1><field> 从<key1>集合<field>取出value

  • hmset <key1><field1><value1><field2><value2>.... 批量设置hash的值

  • hexists <key1><field> 查看哈希表key中,给定域field是否存在

  • hkeys <key> 列出该hash集合的所有field

  • hvals <key> 列出该hash集合的所有value

  • hincrby <key><field><increment> 为哈希表key中的域field的值加上增量1 -1

  • **hsetnx <key><field><value> **

    将哈希表key中的域field的值设置为value,当且仅当域field不存在


Redis有序集合Zset(sorted set)

Redis有序集合zset与普通集合set非常相似,是一个没有重复元素的字符串集合

不同之处是有序集合的每个成员都关联了一个评分(score),这个评分(score)被用来按照从最低分到最高分的方式排序集合中的成员。集合的成员是唯一的,但是评分可以是重复的。

因为元素是有序的,所以你也可以很快的根据评分(score)或者次序(position)来获取一个范围的元素

访问有序集合的中间元素也是非常快的,因此你能够使用有序集合作为一个没有重复成员的只能列表

  • zadd <key><score1><value1><score2><value2>....

    将一个或多个member元素及其score值加入到有序集key当中

  • zrange <key><start><stop> [WITHSCORES]

    返回有序集key中,下标在之间的元素

    带WITHSCORES,可以让分数一起和值返回到结果集

  • zrangebyscore key minmax [withscores] [limit offset count]

    返回有序集key中,所有score值介于min 和 max之间(包括等于min或max)的成员。有序集成员按score值递增(从小到大)次序排序

  • zrevrangebyscore key maxmin [withscores] [limit offset count]

    同上,改为从大到小排序

  • zincrby <key><increment><value> 为元素的score加上增量

  • zrem <key><value> 删除该集合下,指定值的元素

  • zcount <key><min><max> 统计该集合下,分数区间内的元素个数

  • zrank <key><value> 返回该值在集合中的排名,从0开始

  • 数据结构

  SortedSet(zset)是Redis提供的一个非常特别的数据结构,一方面它等价于Java的数据
结构Map<String,Double>,可以给每一个元素value赋予一个权重score,另一方面它又
类似于TreeSet,内部的元素会按照权重score进行排序,可以得到每个元素的名次,还可以
通过score的范围来获取元素的列表。

zset底层使用了两个数据结构
  (1)hash,hash的作用就是关联元素value和权重score,保障元素value的唯一性,可以
通过元素value找到相应的score值。
  (2)跳跃表,跳跃表的目的在于给元素value排序,根据score的范围获取元素列表。

跳跃表(跳表)


7、Redis的发布和订阅

  • 什么是发布和订阅?

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

    Redis客户端可以订阅任意数量的频道。

  • 发布订阅命令行实现

    → 打开一个客户端订阅 channel1

    **subscribe channel1**
    

    → 打开另一个客户端,给channel1发布消息hello

    **publish channel1 hello**
    
    返回的1 是订阅者数量
    

    → 打开第一个客户端可以看到发送的消息

    注:发布的消息没有持久化,如果在订阅的客户端收不到hello,只能收到订阅后发布的消息。


8、Redis新数据类型

  • Bitmaps

    Redis提供了Bitmaps这个“数据类型”可以实现对位的操作:

    (1)Bitmaps本身不是一种数据类型,实际上它就是字符串(key-value),但是它可以对字符串的位进行操作。

    (2)Bitmaps单独提供了一套命令,所以在Redis中使用Bitmaps和使用字符串的方法不太相同。可以把Bitmaps想象成一个以位为单位的数组,数组的每个单元只能存储0和1,数组的下标在Bitmaps中叫做偏移量。

    命令

    1. setbit

      ① **setbit <key><offset><value> ** 设置Bitmaps中某个偏移量的值(0或1)

      *offset:偏移量从0开始

      ② 每个独立用户是否访问过网站存放在Bitmaps中,将访问的用户记做1,没有访问的用户记做0,用偏移量作为用户的id。

      设置键的第offset个位的值(从0算起),假设现在有20个用户,userid=1,6,11,15,19的用户对网站进行了访问,那么当前Bitmaps初始化结果

    2. getbit <key><offset> 获取Bitmaps中某个偏移量的值

      获取键的第offset位的值(从0开始算)

    3. bitcount

      统计字符串被设置为1的bit数。一般情况下,给定的整个字符串都会被进行计数,通过指定额外的start或end参数,可以让计数只在特定的位上进行。start和end参数的设置,都可以使用负数值:比如-1表示最后一个位,而-2表示倒数第二个位,start、end是指bit组的字节的下标数,二者皆包含。

      ① 格式

      bitcount <key>[start end] 统计字符串从start字节到end字节比特值为1的数量。

      ② 实例

      计算2022-11-06这天的独立访问用户数量

  • HypeLogLog

    在工作当中,我们经常会遇到与统计相关的功能需求,比如统计网站PV(PageView页面访问量),可以使用Redis的incr、incrby轻松实现。

    但像UV(UniqueVisitor,独立访客)、独立IP数、搜索记录数等需要去重和计数的问题如何解决?这种求集合中不重复元素个数的问题称为基数问题

    解决基数问题有很多种方案:
    
    (1)数据存储在MySQL表中,使用**distinct count**计算不重复个数
    
    (2)使用Redis提供的hash、set、bitmaps等数据结构来处理
    

    以上的方案结果精确,但随着数据不断增加,导致占用空间越来越大,对于非常大的数据集是不切实际的。

    能否能够降低一定的精度来平衡存储空间?Redis推出了HyperLogLog

    Redis HyperLogLog 是用来做基数统计的算法,HyperLogLog 的优点是,在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定的、并且是很小的。

    在 Redis 里面,每个 HyperLogLog 键只需要花费 12 KB 内存,就可以计算接近 2^64 个不同元素的基数。这和计算基数时,元素越多耗费内存就越多的集合形成鲜明对比。

    但是,因为 HyperLogLog 只会根据输入元素来计算基数,而不会储存输入元素本身,所以 HyperLogLog 不能像集合那样,返回输入的各个元素。

    什么是基数?

    比如数据集 {1, 3, 5, 7, 5, 7, 8}, 那么这个数据集的基数集为 {1, 3, 5 ,7, 8}, 基数(不重复元素)为5。 **基数估计就是在误差可接受的范围内,快速计算基数。**
    
  • Geospatial

    Redis 3.2 中增加了对GEO类型的支持。GEO,Geographic,地理信息的缩写。该类型,就是元素的2维坐标,在地图上就是经纬度。redis基于该类型,提供了经纬度设置,查询,范围查询,距离查询,经纬度Hash等常见操作。


9、Redis_Jedis

  • Jedis所需要的jar包
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>3.2.0</version>
</dependency>
  • 连接Redis注意事项

    禁用Linux的防火墙:Linux(CentOS7)里执行命令

    systemctl stop/disable firewalld.service

    redis.conf中注释掉bind 127.0.0.1然后protected-mode no

  • 创建测试程序

package com.atguigu.jedis;
import redis.clients.jedis.Jedis;

public class Demo1{
    public static void main(String[] args){
        Jedis jedis = new Jedis("192.168.137.3",6379);
        String pong = jedis.ping();
        System.out.println("连接成功:" + pong);
        jedis.close();
    }
}

//测试相关数据类型
//Jedis-API:Key
jedis.set("k1","v1");
jedis.set("k2","v2");
jedis.set("k3","v3");
Set<String> keys = jedis.keys("*");
System.out.println(keys.size());
for(String key:keys){
    System.out.println(key);
}
System.out.println(jedis.exists("k1"));
System.out.println(jedis.ttl("k1"));
System.out.println(jedis.get("k1"));

//Jedis-API:String
jedis.mset("str1","v1","str2","v2","str3","v3");
System.out.println(jedis.mget("str1","str2","str3"));

//Jedis-API:List
List<String> list = jedis.lrange("mylist",0,-1);
for (String element : list) {
    System.out.println(element);
}

//Jedis-API:set
jedis.sadd("orders", "order01");
jedis.sadd("orders", "order02");
jedis.sadd("orders", "order03");
jedis.sadd("orders", "order04");
Set<String> smembers = jedis.smembers("orders");
for (String order : smembers) {
    System.out.println(order);
}
jedis.srem("orders", "order02");

//Jedis-API:hash
jedis.hset("hash1","userName","lisi");
System.out.println(jedis.hget("hash1","userName"));
Map<String,String> map = new HashMap<String,String>();
map.put("telphone","13810169999");
map.put("address","atguigu");
map.put("email","abc@163.com");
jedis.hmset("hash2",map);
List<String> result = jedis.hmget("hash2", "telphone","email");
for (String element : result) {
    System.out.println(element);
}

//Jedis-API:zset
jedis.zadd("zset01", 100d, "z3");
jedis.zadd("zset01", 90d, "l4");
jedis.zadd("zset01", 80d, "w5");
jedis.zadd("zset01", 70d, "z6");
Set<String> zrange = jedis.zrange("zset01", 0, -1);
for (String e : zrange) {
    System.out.println(e);
}

10、Redis与Spring Boot整合

  • 在pom.xml文件中引入redis相关依赖
<!-- redis -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

<!-- spring2.X 集成redis所需 common-pool2 -->
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
    <version>2.6.0</version>
</dependency>
  • appliication.properties配置redis配置
#Redis 服务器地址
spring.redis.host=192.168.XXX.XXX
#Redis 服务器连接端口
spring.redis.port=6379
#Redis 数据库索引(默认为0)
spring.redis.database=0
#连接超时时间(毫秒)
spring.redis.timeout=1800000
#连接池最大连接数(使用负值表示没有限制)
spring.redis.lettuce.pool.max-active=20
#最大阻塞等待时间(负数表示没有限制)
spring.redis.lettuce.pool.max-wait=-1
#连接池中的最大空闲连接
spring.redis.lettuce.pool.max-idle=5
#连接池中的最小空闲连接
spring.redis.lettuce.pool.min-idle=0

11、Redis_事务_锁机制

  • Redis的事务定义

    Redis事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。

Redis事务的主要作用就是串联多个命令防止别的命令插队。

  • 悲观锁

    悲观锁(Pessimistic Lock),顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。

  • 乐观锁

    乐观锁(Optimistic Lock),顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断以下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量。Redis就是利用这种CAS(Compare And Swap)机制实现事务的。

  • Redis事务三特性

    → 单独的隔离操作

    事务中的所有命令都会**序列化、按顺序**地执行。事务在执行的过程中,**不会被**其他客户端发送来的命令请求所**打断**。
    

    → 没有隔离级别的概念

    队列中的命令没有**提交之前都不会实际被执行**,因为事务提交前任何指令都不会被实际执行。
    

    → 不保证原子性

    事务中如果有一条命令执行失败,其后的命令仍然会被执行,没有回滚。
    
  • LUA脚本在Redis中的优势

    将复杂的或者多步的redis操作,写为一个脚本,一次提交给redis执行,减少反复连接redis的次数。提升性能。

    LUA脚本是类似redis事务,有一定的原子性,不会被其他命令插队,可以完成一些redis事务性的操作。

    但是注意redis的lua脚本功能,只有在redis2.6以上的版本才可以使用。

    利用lua脚本淘汰用户,解决超卖问题。

    redis2.6版本以后,通过lua脚本解决争抢问题,实际上是redis利用其单线程的特性,用任务队列的方式解决多任务并发问题。

  • Redis_事务_秒杀案例

    第一版:简单版。达不到并发的效果

    第二版:加事务—乐观锁(解决超卖),但是出现遗留库存和连接超时

    第三版:连接池解决超时问题

    第四版:解决库存依赖问题,LUA脚本


12、Redis持久化之RDB

Redis提供了2个不同形式的持久化方式
→ RDB(Redis DataBase)
→ AOF(Append Of File)

  • 什么是RDB?

    在指定的时间间隔内将内存中的数据集快照写入磁盘,也就是行话讲的Snapshot快照它恢复时是将快照文件直接读到内存里。

  • 备份是如何执行的?

    Redis会单独创建fork)一个子进程来进行持久化,会先将数据写入到一个临时文件中,待持久化过程都结束了,再用这个临时文件替换上次持久化好的文件。整个过程中,主进程是不进行任何IO操作的,这就确保了极高的性能,如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,那么RDB方式要比AOF方式更加的高效。RDB的缺点是最后一次持久化后的数据可能丢失。

  • dump.rdb文件

    在redis.conf中配置文件名称,默认为dump.rdb

    触发RDB快照:保持策略

    命令save VS bgsave
    save:save时只管保存,其他不管,全部阻塞。手动保存。不建议。
    bgsave:Redis会在后台异步进行快照操作,快照同时还可以响应客户端请求。

flushall命令
执行flushall命令,也会产生dump.rdb文件,但是里面是空的,无意义

  • Save

    格式:save秒钟 写操作次数

    RDB是整个内存的压缩过的Snapshot,RDB的数据结构,可以配置复合的快照触发条件,默认是1分钟内改了1万次,或5分钟内改了10次,或15分钟内改了1次。

  • stop-writes-on-bgsave-error

    当Redis无法写入磁盘的话,直接关掉Redis的写操作。推荐yes。

  • rdbcompression压缩文件

    对于存储到磁盘中的快照,可以设置是否进行压缩存储。如果是的话,redis会采用LZF算法进行压缩。

    如果你不想消耗CPU来进行压缩的话,可以设置为关闭此功能。推荐yes。

  • rdbchecksum检查完整性

    在存储快照后,还可以让redis使用CRC64算法来进行数据校验,但是这样做会增加大约10%的性能消耗,如果希望获取到最大的性能提升,可以关闭此功能。推荐yes

  • rdb的备份

    先通过config get dir 查询rdb文件的目录

    将*.rdb的文件拷贝到别的地方

    rdb的恢复

    1. 关闭Redis
    2. 先把备份的文件拷贝到工作目录下cp dump2.rdb dump.rdb
    3. 启动Redis,备份数据会直接加载
  • 优势

    • 适合大规模的数据恢复
    • 对数据完整性和一致性要求不高更适合使用
    • 节省磁盘空间
    • 恢复速度快

  • 劣势

    • Fork的时候,内存中的数据被克隆了一份,大致2倍的膨胀性需要考虑
    • 虽然Redis在fork的时候使用了写时拷贝技术,但是如果数据庞大时还是比较消耗性能
    • 备份周期在一定间隔时间做一次备份,所以如果Redis意外down掉的话,就会丢失最后一次快照后的所有修改
  • 如何停止

    动态停止RDB:redis-cli config set save ""#save后给空值,表示禁用保存策略

  • 总结

    RDB是一个非常紧凑的文件。
    RDB在保存RDB文件时父进程唯一需要做的就是fork(单独创建)出一个子进程来做,父进程不需要再做其他IO操作,所以RDB持久化方式可以最大化Redis的性能
    与AOF相比,再恢复大的数据集的时候,RDB方式会更快一些。

    数据丢失风险大
    RDB需要经常fork子进程来保存数据集到硬盘上,当数据集比较大的时候,fork的过程是非常耗时的,可能会导致Redis在一些毫秒级不能响应客户端请求。


13、Redis持久化之AOF(Append Only File)

  • 什么是AOF?

    日志的形式来记录每个写操作(增量保存),将Redis执行过的所有写指令记录下来(读操作不记录),只许追加文件但不可以改写文件,redis启动之初会读取该文件重新构建数据,换言之,redis重启的话就根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作。

  • AOF持久化流程

    1. 客户端的请求写命令会被append追加到AOF缓冲区
    2. AOF缓冲区根据AOF持久化策略 [always,everysec,no] 操作sync同步到磁盘的AOF文件中
    3. AOF文件大小超过重写策略或手动重写时,会对AOF文件rewrite重写,压缩AOF文件容量
    4. Redis服务重启时,会重新load加载AOF文件中的写操作达到数据恢复的目的

  • AOF注意事项

AOF默认不开启
可以在redis.conf中配置文件名称,默认为appendonly.aof
AOF文件的保存路径,同RDB的路径一致。

AOF和RDB同时开启,redis听谁的?
AOF和RDB同时开启,系统默认取AOF的数据(数据不存在丢失)

AOF启动/修复/恢复
-》 AOF的备份机制和性能虽然和RDB不同,但是备份和恢复的操作同RDB一样,都是拷贝备份
    文件,需要恢复时再拷贝到Redis工作目录下,启动系统即加载。
-》 正常恢复
    -》 修改默认的appendonly no,改为yes
    -》 将有数据的aof文件复制一份保存到对应目录(查看目录:config get dir)
    -》 恢复:重启redis然后重新加载
-》 异常恢复
    -》 修改默认的appendonly no,改为yes
    -》 如遇到AOF文件损坏,通过/usr/local/bin/redis-check-aof-fix
        appendonly.aof进行恢复
    -》 备份被写坏的AOF文件
    -》 恢复:重启redis然后重新加载
    
AOF同步频率设置
-> appendfsync always
始终同步,每次Redis的写入都会立刻记入日志;性能较差但数据完整性比较好
-> appendfsync everysec
每秒同步,每秒记入日志一次,如果宕机,本秒的数据可能丢失
-> appendfsync no
redis不主动进行同步,把同步时机交给操作系统

Rewirte压缩
(详情见Redis6课件)
  • 优势

    → 备份机制更稳健,丢失数据概率更低

    可读的日志文件,通过操作AOF文件,可以处理误操作

  • 劣势

    → 比起RDB占用更多的磁盘空间

    → 恢复备份速度要慢

    → 每次读写都同步的话,有一定的性能压力

    → 存在个别Bug,造成恢复不能

  • 总结

    AOF文件是一个只进行追加的日志文件。
    Redis可以在AOF文件体积变得过大时,自动地在后台对AOF进行重写
    AOF文件有序地保存了对数据库执行的所有写入操作,这些写入操作以Redis协议的格式保存,因此AOF文件的内容非常容易被人读懂,对文件进行分析也很轻松。

    对于相同的数据集来说,AOF文件的体积通常要大于RDB文件的体积。
    根据所使用的fsync策略,AOF的速度可能会慢于RDB。

总结RDB与AOF
官方推荐两个都启用。
如果对数据不敏感,可以选单独用RDB。
不建议单独用AOF,因为可能会出现Bug。
如果只是做纯内存缓存,可以都不用。

同时开启两种持久化方式
在这种情况下,当redis重启的时候会优先载入AOF来恢复原始的数据,因为在通常情况下AOF文件
保存的数据集要比RDB文件保存的数据集要完整。

RDB的数据不实时,同时使用两者时服务器重启也只会找AOF文件,那要不要只是用AOF呢?
建议不要,因为RDB更适合用于备份数据库(AOF在不断变化不好备份),快速重启,而且不会有
AOF可能潜在的bug,留着作为一个万一的手段。

14、Redis_主从复制

  • 什么是主从复制?

    主机数据更新后根据配置和策略,自动同步到备机的master / slaver机制,Master以写为主,Slave以读为主

  • 能干嘛?

    读写分离,性能扩展

    容灾快速恢复

  • 怎么使用主从复制?

    1. 拷贝多个redis.conf文件include(写绝对路径)
    2. 开启daemonize yes
    3. Pid文件名字pidfile
    4. 指定端口port
    5. Log文件名字
    6. dump.rdb名字dbfilename
    7. Appendonly关掉或者换名字
  • 常用3招

薪火相传
-》 上一个Slave可以是下一个Slave的Master,Slave同样可以接收其他Slaves的连接和同
步请求,那么该Slave作为了链条中的下一个master,可以有效减轻master的写压力,去中心
化降低风险。
-》 用slaveof <ip><port>
-》 中途变更转向:会清除之前的数据,重新建立拷贝最新的
-》 风险是一旦某个slave宕机,后面的slave都没法备份
主机挂了,从机还是从机,无法写数据了
反客为主
-》 当一个master宕机后,后面的slave可以立刻升为master,
其后面的slave不用做任何修改
-》 用slaveof no one将从机变为主机

复制原理
-》 Slave启动成功连接到master后会发送一个sync命令
-》 Master接到命令启动后台的存盘进程,同时收集所有接收到用于修改数据集命令,在后台
进程执行完毕之后,master将传送整个数据文件到slave,以完成一次完全同步
-》 全量复制:而slave服务在接收到数据库文件数据后,将其存盘并加载到内存中
-》 增量复制:Master继续将新的所有收集到的修改命令依次传给slave,完成同步
-》 但是只要是重新连接master,一次完全同步(全量复制)将被自动执行
  • 哨兵模式(sentinel)

    反客为主的自动版,能够后台监控主机是否故障,如果故障了根据投票数自动将从库转换为主库

    配置流程

1. 自定义的/myredis 目录下新建sentinel.conf文件,名字绝不能错
2. 配置哨兵,填写内容
   sentinel monitor mymaster 127.0.0.1 6379 1
   其中mymaster为监控对象起的服务器名称,1为至少有多少个哨兵同意迁移的数量
3. 启动哨兵
   /usr/local/bin
   执行redis-sentinel /myredis/sentinel.conf

**当主机挂掉,从机选举中产生新的主机**
  • 主从复制存在的问题(!)

    由于所有的写操作都是先在Master上操作,然后同步更新到Slave上,所以从Master同步到Slave机器有一定的延迟,当系统很繁忙的时候,延迟问题会更加严重,Slave机器数量的增加也会使这个问题更加严重。

  • 故障恢复


15、集群

详情见Redis6课件

D:\Atshangguigu\Java框架\Redis6\笔记


16、Redis应用问题解决

  • 缓存穿透

问题描述
key对应的数据在数据源并不存在,每次针对此key的请求从缓存获取不到,请求都会压到数据源
,从而可能压垮数据源。比如用一个不存在的用户id获取用户信息,不论缓存还是数据库都没有,
若黑客利用此漏洞进行攻击可能压垮数据库。

解决方案
一个一定不存在缓存及查询不到的数据,由于缓存是不命中时被动写的,并且出于容错考虑,如果
从存储层查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到存储层去查询,失
去了缓存的意义。
1、对空值缓存:如果一个查询返回的数据为空(不管数据是否不存在),我们仍然把这个空结果
  (null)进行缓存,设置空结果的过期时间会很短,最长不超过五分钟。
2、设置可访问的名单(白名单):使用bitmaps类型定义一个可以访问的名单,名单id作为
   bitmaps的偏移量,每次访问和bitmap里面的id进行比较,如果访问id不在bitmaps里面
   ,进行拦截,不允许访问。
3、采用布隆过滤器:(布隆过滤器(Bloom Filter)是1970年由布隆提出的。它实际上是一个
   很长的二进制向量(位图)和一系列随机映射函数(哈希函数)。布隆过滤器可以用于检索一个
   元素是否在一个集合中。它的有点是空间效率和查询时间都远远超过一般的算法,缺点是有一
   定的误识别率和删除困难)。
   将所有可能存在的数据哈希到一个足够大的bitmaps中,一个一定不存在的数据会被这个
   bitmaps拦截掉,从而避免了对底层存储系统的查询压力。
4、进行实时监控:当发现Redis的命中率开始极速降低,需要排查访问对象和访问的数据,
   和运维人员配合,可以设置黑名单限制服务。
  • 缓存击穿

**问题描述**
key对应的数据存在,但在redis中过期,此时若有大量并发请求过来,这些请求发现缓存过期
一般都会从后端DB加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把后端DB压垮。

**解决方案**
key可能会在某些时间点被超高并发地访问,是一种非常“热点”的数据。这个时候,需要考虑
一个问题:缓存被**“击穿”**的问题。

**解决问题**
**1、预先设置热门数据:**在redis高峰访问之前,把一些热门数据提前存入到redis里面,加
   大这些热门数据key的时长
**2、实时调整:**现场监控哪些数据热门,实时调整key的过期时长
**3、使用锁:**
  -》 就是在缓存失效的时候(判断拿出来的值为空),不是立即去load db
  -》 先使用缓存工具的某些带成功操作返回值的操作(比如Redis的SETNX)去set一个mutex key
  -》 当操作返回成功时,再进行load db的操作,并回设缓存,最后删除mutex key
  -》 当操作返回失败,证明有线程在load db,当前线程睡眠一段时间再重试整个get缓存的方法
  -》 **见下图**

  • 缓存雪崩

问题描述
key对应的数据存在,但在redis中过期,此时若有大量并发请求过来,这些请求发现缓存过期
一般都会从后端DB加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把后端DB压垮。
缓存雪崩与缓存击穿的区别在于这里针对很多key缓存,前者则是某一个key正常访问

解决方案
缓存失效时的雪崩效应对底层系统的冲击非常可怕!

解决问题
1、构建多级缓存架构:nginx缓存 + redis缓存 + 其他缓存(ehcache等)
2、使用锁或队列:
   用加锁或者队列的方式来保证不会有大量的线程对数据库一次性进行读写,从而避免失效
   时大量的并大请求落到底层存储系统上。不适用高并发情况
3、设置过期标志更新缓存:
   记录缓存数据是否过期(设置提前量),如果过期会触发通知另外的线程在后台去更新实际
   key的缓存
4、将缓存失效时间分散开:
   比如我们可以在原有的失效时间基础上增加一个随机值,比如1-5分钟随机,这样每一个缓存
   的过期时间的重复率就会降低,就很难引发集体失效的事件

  • 使用redis实现分布式锁
set sku:1:info "OK" NX PX 10000
EX second: 设置键的过期时间为second秒。
  SET key value EX second 效果等同于 SETEX key second value。
PX millisecond: 设置键的过期时间为millisecond毫秒。
  SET key value PX millisecond 效果等同于 PSETEX key millisecond value。
NX: 只在键不存在时,才对键进行设置操作。
  SET key value NX 效果等同于 SETNX key value。
XX: 只在键已经存在时,才对键进行设置操作
![](https://img2022.cnblogs.com/blog/2701464/202201/2701464-20220121153937028-502259651.png)

1. 多个客户端同时获取锁(setnx)
2. 获取成功,执行业务逻辑{从db获取数据,放入缓存},执行完成释放锁(del)
3. 其他客户端等待重试

17、Redis6.0新功能

  • ACL

    Redis ACL是Access Control List(访问控制列表)的缩写,该功能允许根据可以执行的命令和可以访问的键来限制某些连接。

在Redis5版本之前,Redis安全规则只有密码控制还有通过rename来调整高危命令比如flushdb,KEYS*,shutdown等。Redis6则提供ACL的功能对用户进行更细粒度的权限控制:
(1)接入权限:用户名和密码
(2)可以执行的命令
(3)可以操作的KEY

1、使用 acl list 命令展现用户权限列表
2、使用 acl cat命令
   1)查看添加权限指令类别
   2)加参数类型名可以查看类型下具体命令
3、使用 acl whoami 命令查看当前用户
4、使用 acl setuser 命令创建和编辑用户ACL
  • 其他持续关注
Redis6新功能还有:
1、RESP3新的Redis通信协议:优化服务端与客户端之间通信
2、Client side caching客户端缓存:基于RESP3协议实现的客户端缓存功能。为了进一步
  提升缓存的性能,将客户端经常访问的数据cache到客户端。减少TCP网络交互
3、Proxy集群代理模式:Proxy功能,让Cluster拥有像单实例一样的接入方式,降低大家
  使用cluster的门槛。不过需要注意的是代理不改变Cluster的功能限制,不支持的命令还是
  不会支持,比如跨slot的多key操作
4、Modules API
  Redis6中模块API开发进展非常大,因为Redis Labs为了开发复杂的功能,从一开始就用
  上了Redis模块。Redis可以变成一个框架,利用Modules来构建不同系统,而不需要从头
  开始写然后还有BSD许可。Redis一开始就是一个向编写各种系统开放的平台。

Redis为什么要分16个数据库

.conf文件中的bind属性

Redis问题——Error: 磁盘在使用中,或被另一个进程锁定。

DENIED Redis is running in protected mode because protected mode is enabled......

posted @ 2022-01-21 15:40  ArosyCat  阅读(49)  评论(0编辑  收藏  举报