Redis学习笔记

本文详细介绍了Redis中的数据类型,包括String、List、Set、Hash和Sorted Set,并提供了操作示例。还探讨了Redis的持久化、主从复制、哨兵模式和集群,以及应对缓存穿透、缓存击穿和缓存雪崩的策略。此外,提到了Redis的事务、乐观锁和新的ACL功能。最后,文章提及了Redis的多线程优化和新版本特性。
摘要由CSDN通过智能技术生成

课程地址

NoSQL

not olny SQL,泛指非关系型数据库。直接访问内存,缓解了IO压力

  • 不遵循sql标准
  • 不支持ACID:Atomicity,Consistency,Isolation,Durability

Redis概述与安装

安装目录:/usr/local/bin
在这里插入图片描述
前台启动:redis-server

后台启动:

  1. 将redis.conf复制到/etc/
  2. 修改配置文件,将daemonize设置为yes
  3. 启动时指明配置文件路径:redis-server /etc/redis.conf,默认端口6379

客户端启动:

[root@localhost ~]# redis-cli
127.0.0.1:6379> ping
PONG

默认16个数据库,默认使用0号库。所有库使用统一密码管理

select <dbid>	# 切换数据库
dbsize			# 查看当前数据库的 key 的数量
flushdb			# 清空当前库
flushall		# 通杀全部库

切换数据库:

127.0.0.1:6379> select 1
OK

Redis五大基础数据类型

  • String
  • List
  • Set
  • Hash
  • Zset
keys *		# 查看当前库的所有 key
exists key	# 判断某个 key 是否存在
del key		# 删除指定的 key 数据
unlink key	# 非阻塞删除
expire key 10	# 为 key 设置过期时间
ttl key

字符串

redis中的字符串最大512M

set与get:

127.0.0.1:6379> set k2 v200
OK
127.0.0.1:6379> get k2
"v200"
127.0.0.1:6379> append k2 abdc
(integer) 8
127.0.0.1:6379> get k2
"v200abdc"
127.0.0.1:6379> strlen k2
(integer) 8
#setnx只有当key不存在时设置key值
127.0.0.1:6379> setnx k3 v300
(integer) 1

数字增减:

127.0.0.1:6379> set k4 500
OK
127.0.0.1:6379> incr k4
(integer) 501
127.0.0.1:6379> get k4
"501"
127.0.0.1:6379> decr k4
(integer) 500
127.0.0.1:6379> get k4
"500"
127.0.0.1:6379> incrby k4 20
(integer) 520
127.0.0.1:6379> get k4
"520"
127.0.0.1:6379> decrby k4 40
(integer) 480
127.0.0.1:6379> get k4
"480"

设置多个值

127.0.0.1:6379> mset k1 v1 k2 v2 k3 v3
OK
127.0.0.1:6379> mget k1 k3
1) "v1"
2) "v3"
#原子性,msetnx只要有一个失败,则都失败
127.0.0.1:6379> msetnx k11 v11 k22 v22 k3 v33
(integer) 0
127.0.0.1:6379> keys *
1) "k3"
2) "k1"
3) "k2"

操作value的子串

127.0.0.1:6379> set str hello,world
OK
127.0.0.1:6379> getrange str 6 10
"world"
127.0.0.1:6379> setrange str 0 fuck
(integer) 11
127.0.0.1:6379> get str
"fucko,world"

设置超时时间:

127.0.0.1:6379> setex k4 20 v200
OK
127.0.0.1:6379> ttl k4
(integer) 17
127.0.0.1:6379> ttl k4
(integer) 16
127.0.0.1:6379> ttl k4
(integer) 15
127.0.0.1:6379> get k4
"v200"

取出新值输出旧值

127.0.0.1:6379> getset str fuckyou
"fucko,world"
127.0.0.1:6379> get str
"fuckyou"

列表

操作命令:lpush/rpush; lpop/rpop; rpoplpush; lrange; lindex; llen; lrem;

redis的列表是简单的字符串列表,底层实现为双向链表

127.0.0.1:6379> lpush k1 v1 v2 v3
(integer) 3
127.0.0.1:6379> lrange k1 0 -1
1) "v3"
2) "v2"
3) "v1"
127.0.0.1:6379> lpop k1
"v3"
127.0.0.1:6379> rpush k2 v1 v2 v3
(integer) 3
127.0.0.1:6379> lrange k2 0 -1
1) "v1"
2) "v2"
3) "v3"
#从k1的右边吐出一个值,插入到k2的左边
127.0.0.1:6379> rpoplpush k1 k2
"v1"
127.0.0.1:6379> lrange k2 0 -1
1) "v1"
2) "v1"
3) "v2"
4) "v3"
127.0.0.1:6379> lindex k2 2
"v2"
127.0.0.1:6379> llen k2
(integer) 4
127.0.0.1:6379> linsert k2 before v2 newv2
(integer) 5
127.0.0.1:6379> lrange k2 0 -1
1) "v1"
2) "v1"
3) "newv2"
4) "v2"
5) "v3"
127.0.0.1:6379> lset k2 2 v22
OK
127.0.0.1:6379> lrange k2 0 -1
1) "v1"
2) "v1"
3) "v22"
4) "v2"
5) "v3"
127.0.0.1:6379> lrem k2 2 v1
(integer) 2
127.0.0.1:6379> lrange k2 0 -1
1) "v22"
2) "v2"
3) "v3"

在这里插入图片描述

集合

与list类似,但是set能自动去重,底层实现为hash表

操作命令:sadd; smembers; sismember; scard; srem; spop; srandmember; smove

127.0.0.1:6379> sadd s1 v1 v2 v3	#声明一个集合s1,并向其中加入3个元素
(integer) 3
127.0.0.1:6379> smembers s1		#查看集合中所有成员
1) "v3"
2) "v2"
3) "v1"
127.0.0.1:6379> sismember s1 v1		#判断s1中是否存在v1
(integer) 1
127.0.0.1:6379> scard s1		#统计s1中的元素个数	
(integer) 3
127.0.0.1:6379> srem s1 v1 v2		#从s1中删除v1和v2
(integer) 2
127.0.0.1:6379> smembers s1
1) "v3"
127.0.0.1:6379> sadd s2 v1 v2 v3 v4
(integer) 4
127.0.0.1:6379> spop s2		#从s2中弹出一个随机元素
"v3"
127.0.0.1:6379> srandmember s2 2	#从s2中随机取出2个元素,但不删除
1) "v1"
2) "v2"
127.0.0.1:6379> srandmember s2 2
1) "v1"
2) "v2"
127.0.0.1:6379> smove s1 s2 v1		#将s1中的v1元素移动到s2中去
(integer) 1

集合的交并差

127.0.0.1:6379> sadd k1 v1 v2 v3
(integer) 3
127.0.0.1:6379> sadd k2 v3 v4 v5
(integer) 3
127.0.0.1:6379> sadd k3 v4 v6 v7
(integer) 3
127.0.0.1:6379> sinter k2 k3		#取交集
1) "v4"
127.0.0.1:6379> sunion k2 k3		#取并集
1) "v5"
2) "v4"
3) "v6"
4) "v3"
5) "v7"
127.0.0.1:6379> sdiff k2 k3		#取差集
1) "v3"
2) "v5"

哈希

key field

操作命令:

hset key field value
hget key field

hmset key field1 value1 field2 value2 ...
hmget key field1 field2 ...

hexists key field

hkeys key
hvals key

hincrby key field increment
hsetnx key field value

在这里插入图片描述

127.0.0.1:6379> hset u01 id 1001	#设置u01的id属性
(integer) 1
127.0.0.1:6379> hset u01 name zhangsan		#设置u01的name属性
(integer) 1
127.0.0.1:6379> hset u01 age 20		#设置u01的age属性
(integer) 1
127.0.0.1:6379> hget u01 name		#获取u01的name属性
"zhangsan"
127.0.0.1:6379> hget u01 age		#获取u01的age属性
"20"
127.0.0.1:6379> hmset u02 id 1001 name lisi age 22		#同时设置多个属性
OK
127.0.0.1:6379> hmget u02 id name age	#同时获取多个属性
1) "1001"
2) "lisi"
3) "22"
127.0.0.1:6379> hexists u02 name		#判断u02是否存在name属性
(integer) 1
127.0.0.1:6379> hexists u02 nam
(integer) 0
127.0.0.1:6379> hkeys u02		#查看u02的所有属性的所有keys
1) "id"
2) "name"
3) "age"
127.0.0.1:6379> hvals u02		#查看u02所有属性的values
1) "1001"
2) "lisi"
3) "22"
127.0.0.1:6379> hincrby u02 age 2		#u02的age属性增加2
(integer) 24
127.0.0.1:6379> hvals u02		#查看u02所有属性的values
1) "1001"
2) "lisi"
3) "24"
127.0.0.1:6379> hsetnx u02 age 40		#当age属性不存在时进行设置
(integer) 0
127.0.0.1:6379> hsetnx u02 country china	#当country属性不存在时进行设置
(integer) 1
127.0.0.1:6379> hvals u02
1) "1001"
2) "lisi"
3) "24"
4) "china"
127.0.0.1:6379> hkeys u02
1) "id"
2) "name"
3) "age"
4) "country"

有序集合

操作命令:

zadd key score1 value1 score2 value2 ...

zrange key min max [withscores]
zrangebyscore key min max [withscores]

zincrby key increment member

zrem key member

zcount key min max
zrank key member

在这里插入图片描述

127.0.0.1:6379> zadd zs1 100 java 200 c++ 300 python 400 lua	#向zset中加入元素
(integer) 4
127.0.0.1:6379> zrange zs1 0 -1		#显示所有元素
1) "java"
2) "c++"
3) "python"
4) "lua"
127.0.0.1:6379> zrange zs1 0 -1 withscores		#连同分数显示所有元素
1) "java"
2) "100"
3) "c++"
4) "200"
5) "python"
6) "300"
7) "lua"
8) "400"
127.0.0.1:6379> zrangebyscore zs1 300 400		#查询score在300到400之间的元素
1) "python"
2) "lua"
127.0.0.1:6379> zrangebyscore zs1 300 400 withscores	#查询score在300到400之间的元素,连同分数显示
1) "python"
2) "300"
3) "lua"
4) "400"
127.0.0.1:6379> zrevrangebyscore zs1 400 200 withscores		#反向排列,查询score在400到200之间的元素
1) "lua"
2) "400"
3) "python"
4) "300"
5) "c++"
6) "200"
127.0.0.1:6379> zincrby zs1 50 c++		#为zs1的c++ field的value增加50
"250"
127.0.0.1:6379> zrem zs1 lua			#移除zs1的lua field
(integer) 1
127.0.0.1:6379> zrange zs1 0 -1			#查询zs1的所有元素
1) "java"
2) "c++"
3) "python"
127.0.0.1:6379> zrange zs1 0 -1 withscores
1) "java"
2) "100"
3) "c++"
4) "250"
5) "python"
6) "300"
127.0.0.1:6379> zcount zs1 200 300		#计数zs1中score在200到300之间的元素个数
(integer) 2
127.0.0.1:6379> zrank zs1 python		#查询python的位置(rank)
(integer) 2

配置文件

使能远程连接:

  1. 将NETWORK中的限制本地连接的bind 127.0.0.1 -::1注释掉
  2. 再将protected-mode yes修改为no

字段说明:

  • tcp-backlog:tcp连接队列长度

发布和订阅

一种消息通信方式:发布者(pub)发送消息,订阅者(sub)接收消息

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

在这里插入图片描述

subscribe channel1		#订阅者订阅频道

publish channel1 hello,world	#发布者向频道中发布消息

Redis三大新数据类型

bitmap

操作命令:

setbit key offset value
getbit key offset
bitcount key
bitop operation destkey key [key...]
127.0.0.1:6379> setbit bm1 1 1		#set bm1 1 bit
(integer) 0
127.0.0.1:6379> setbit bm1 6 1
(integer) 0
127.0.0.1:6379> setbit bm1 11 1
(integer) 0
127.0.0.1:6379> setbit bm1 15 1
(integer) 0
127.0.0.1:6379> setbit bm1 19 1
(integer) 0
127.0.0.1:6379> getbit bm1 19
(integer) 1
127.0.0.1:6379> getbit bm1 13
(integer) 0
127.0.0.1:6379> bitcount bm1		#统计bm1中1的个数
(integer) 5
127.0.0.1:6379> setbit bm2 1 1
(integer) 0
127.0.0.1:6379> setbit bm2 5 1
(integer) 0
127.0.0.1:6379> setbit bm2 15 1
(integer) 0
127.0.0.1:6379> bitcount bm2
(integer) 3
127.0.0.1:6379> bitop and bm2_1 bm1 bm2		#求bm1和bm2的与运算,结果保存在bm2_1
(integer) 3
127.0.0.1:6379> bitcount bm2_1
(integer) 2
127.0.0.1:6379> getbit bm2_1 1
(integer) 1
127.0.0.1:6379> getbit bm2_1 3
(integer) 0
127.0.0.1:6379> getbit bm2_1 5
(integer) 0
127.0.0.1:6379> getbit bm2_1 15
(integer) 1

HyperLogLog

操作命令:

pfadd key [element[element...]]
pfcount key [key...]
pfmerge destkey sourcekey [sourcekey...]

基数问题:求集合中不重复元素个数

在这里插入图片描述

127.0.0.1:6379> pfadd hll1 "java"	#向hll1中添加元素
(integer) 1
127.0.0.1:6379> pfadd hll1 "cpp" "php" "python"		#向hll1中添加元素
(integer) 1
127.0.0.1:6379> pfcount hll1		#计数hll1中的元素个数
(integer) 4
127.0.0.1:6379> pfadd hll2 "a" "b" "b" "c"
(integer) 1
127.0.0.1:6379> pfcount hll2
(integer) 3
127.0.0.1:6379> pfmerge hll2_1 hll1 hll2	#合并hll1和hll2到hll2_1
OK
127.0.0.1:6379> pfcount hll2_1
(integer) 7

geospatial

处理地理信息:经纬度,距离,圆形覆盖范围等

操作命令:

geoadd key longitude latitude member [longitude latitude member...]
geopos key member [member...]
geodist key member1 member2 [m | km]
georadius key longitude latitude radius [m | km]
127.0.0.1:6379> geoadd china:city 121.37 31.23 shanghai		#添加经纬度和城市
(integer) 1
127.0.0.1:6379> geoadd china:city 106.23 29.43 chongqing 114.93 22.76 shenzhen 116.23 39.52 beijing
(integer) 3
127.0.0.1:6379> geopos china:city shanghai		#查看城市的经纬度
1) 1) "121.36999815702438354"
   2) "31.22999903975783553"
127.0.0.1:6379> geodist china:city shanghai chongqing km	#计算两个城市之间的距离,以km为单位
"1466.0135"
127.0.0.1:6379> georadius china:city 110 30 1000 km		#求以(110,30)为中心,方圆1000km的城市
1) "chongqing"
2) "shenzhen"

Jedis

Java连接redis

package com.atguigu.jedis;

import redis.clients.jedis.Jedis;

public class jedisDemo1 {
    public static void main(String[] args) {
        Jedis jds = new Jedis("192.168.37.144", 6379);
        String res = jds.ping();
        System.out.println(res);
    }
}

测试字符串相关操作:

    //测试字符串操作
    public static void demo1() {
        Jedis jds = new Jedis("192.168.37.144", 6379);
        jds.set("name", "lucy");
        String n1 = jds.get("name");
        System.out.println(n1);

        Set<String> keys = jds.keys("*");
        for (String k : keys) {
            System.out.println(k);
        }

        jds.mset("k1", "v1", "k2", "v2", "k3", "v3");
        List<String> lmg = jds.mget("k1", "k3");
        for (String l : lmg) {
            System.out.println(l);
        }
    }

测试list相关操作:

    public static void demo2() {
        Jedis jds = new Jedis("192.168.37.144", 6379);
        jds.flushAll();
        jds.lpush("l1", "v1", "v2", "v3");
        List<String> l1 = jds.lrange("l1", 0, -1);
        for(String s : l1){
            System.out.println(s);
        }
    }

测试set的相关操作:

    public static void demo3() {
        Jedis jds = new Jedis("192.168.37.144", 6379);
        jds.flushAll();
        jds.sadd("s1", "v1", "v2", "v3", "v1");
        Set<String> s1 = jds.smembers("s1");
        System.out.println(s1);
    }

测试hash的相关操作:

    public static void demo4() {
        Jedis jds = new Jedis("192.168.37.144", 6379);
        jds.flushAll();
        jds.hset("h1", "age", "20");
        jds.hset("h1", "name", "daniel");
        String hs = jds.hget("h1", "name");
        System.out.println(hs);
    }

Redis事务

事务的基本操作

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

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

三个命令:multi, exec, discard

从输入 multi 命令开始,输入的命令都会依次进入命令队列中,但不会执行,直到输入 exec 后,Redis 会将之前的命令队列中的命令依次执行

组队的过程中可以通过 discard 来放弃组队

127.0.0.1:6379> multi	#开始向队列中压入命令
OK
127.0.0.1:6379(TX)> set k1 v1
QUEUED
127.0.0.1:6379(TX)> sadd s1 v2 v3 v4 v4
QUEUED
127.0.0.1:6379(TX)> lpush l1 v5 v6 v7
QUEUED
127.0.0.1:6379(TX)> exec	#执行队列中的命令
1) OK
2) (integer) 3
3) (integer) 3
127.0.0.1:6379> keys *
1) "l1"
2) "s1"
3) "k1"
127.0.0.1:6379> get k1
"v1"
127.0.0.1:6379> smembers s1
1) "v2"
2) "v4"
3) "v3"
127.0.0.1:6379> lrange l1 0 -1
1) "v7"
2) "v6"
3) "v5"

使用discard退出:

127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> hset u01 name zhangsan
QUEUED
127.0.0.1:6379(TX)> hset u01 age 22
QUEUED
127.0.0.1:6379(TX)> discard
OK
127.0.0.1:6379> keys *
1) "l1"
2) "s1"
3) "k1"

错误处理:

如果在组队过程中某一条命令执行失败,则执行时所有命令都会失败

127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set str1 v1
QUEUED
127.0.0.1:6379(TX)> set str2 v2
QUEUED
127.0.0.1:6379(TX)> set str3
(error) ERR wrong number of arguments for 'set' command
127.0.0.1:6379(TX)> exec
(error) EXECABORT Transaction discarded because of previous errors.

如果在执行过程中某一条命令执行失败,则只会影响到这一条命令的结果,不会像mysql一样回滚:

在这里插入图片描述

127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set str1 v1
QUEUED
127.0.0.1:6379(TX)> set str2 v2
QUEUED
127.0.0.1:6379(TX)> incr str2
QUEUED
127.0.0.1:6379(TX)> exec		#只有第三条命令执行失败
1) OK
2) OK
3) (error) ERR value is not an integer or out of range

乐观锁与悲观锁

悲观锁:

在这里插入图片描述
乐观锁:

在这里插入图片描述
watch对执行过程进行监视

在这里插入图片描述

当一个事务对i进行操作后,会修改版本号导致另一个事务的执行失败:

127.0.0.1:6379> watch i
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> decrby i 50		//先执行
QUEUED
127.0.0.1:6379(TX)> exec
1) (integer) 50
127.0.0.1:6379>

另一个事务:

127.0.0.1:6379> set i 100
OK
127.0.0.1:6379> watch i
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> incrby i 10
QUEUED
127.0.0.1:6379(TX)> exec	//后执行,由于i已经被改动,导致执行失败
(nil)
127.0.0.1:6379> get i
"50"

unwatch取消对执行过程的监视

redis事务3个特性:

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

没有隔离级别的概念:队列中的命令没有提交之前都不会实际被执行,因为事务提交前任何指令都不会被实际执行

不保证原子性:事务中如果有一条命令执行失败,其后的命令仍然会被执行,没有回滚


持久化

redis有两种持久化方式:

  • RDB:Redis DataBase
  • AOF:Append Only File

RDB

在指定的时间间隔内,将内存中的数据集快照写入磁盘

配置参数:save 20 3,在20秒以内有3个数据变化则进行持久化操作。最后一次持久化可能会造成数据丢失

127.0.0.1:6379> config get dir	#获取配置文件中的dir值
1) "dir"
2) "/root"
127.0.0.1:6379> save	#手动保存,会阻塞
OK
127.0.0.1:6379> bgsave		#在后台异步保存
Background saving started

如果需要恢复数据,只需将备份文件 (dump.rdb) 移动到 redis 安装目录并启动服务即可

AOF

在这里插入图片描述
在配置文件中开启AOF:appendonly yes

当AOF和RDB都开启,Redis默认取appendonly.aof中的数据。

和rdb类似,如果需要恢复数据,只需将备份文件 (appendonly.aof) 移动到 redis 安装目录并启动服务即可

在这里插入图片描述
持久化策略:

  • appendfsync always:每一个操作完立刻持久化
  • appendfsync everysec:每秒持久化一次,有数据丢失的风险
  • appendfsync no:持久化时机交给操作系统

redis重写压缩操作:

在这里插入图片描述

AOF持久化流程:

在这里插入图片描述


主从复制

一主多从

主机的数据更新后,根据配置和策略,自动备份到从机的master/slave机制。master以写为主,slave以读为主

在这里插入图片描述

  1. 读写分离,降低了主服务器的压力
  2. 容灾快速恢复

创建主从复制:

  1. 创建测试文件夹:/root/redis_master_slave
  2. /etc/redis.conf拷贝测试文件夹,并修改其中的appendonly no
  3. 创建三个新的redis配置文件redis6379.conf~redis6381.conf,并在其中include /root/redis_master_slave/redis.conf
  4. 修改每一个配置文件如下:
include /root/redis_master_slave/redis.conf
pidfile /var/run/redis_6379.pid
port 6379
dbfilename dump6379.rdb
  1. 启动三个redis服务器进程:
[root@localhost redis_master_slave]# redis-server redis6379.conf
[root@localhost redis_master_slave]# redis-server redis6380.conf
[root@localhost redis_master_slave]# redis-server redis6381.conf
[root@localhost redis_master_slave]# ps -aux | grep redis
root       4051  0.7  0.2 163128  8224 ?        Ssl  14:37   0:00 redis-server *:6379
root       4068  1.1  0.2 163128  8220 ?        Ssl  14:37   0:00 redis-server *:6380
root       4085  1.0  0.2 163128  8272 ?        Ssl  14:38   0:00 redis-server *:6381
root       4099  0.0  0.0 112812   980 pts/0    R+   14:38   0:00 grep --color=auto redis
  1. 连接三台服务器:
redis-cli -p 6379
redis-cli -p 6380
redis-cli -p 6381

通过info replication查询三个进程都是主机:

127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:0
master_failover_state:no-failover
master_replid:2a02286395e6555038e154ee81e7081e1d4b3090
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:0
second_repl_offset:-1
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
  1. 创建主从关系:
127.0.0.1:6380> slaveof 127.0.0.1 6379
OK
127.0.0.1:6381> slaveof 127.0.0.1 6379
OK

在主机中写,在从机中也能读取到。但是不能在从机中进行写操作:

#主机写
127.0.0.1:6379> keys *
(empty array)
127.0.0.1:6379> set k1 v1
OK
#从机读
127.0.0.1:6381> keys *
1) "k1"
127.0.0.1:6381> get k1
"v1"
#从机写,失败
127.0.0.1:6381> set k2 v2
(error) READONLY You can't write against a read only replica.

当某一台slave挂掉后重新连接并加入到从服务器后,他会将master中的数据从头开始复制过来
当一台master挂掉后重启,它还会成为master

实现原理:

  1. 当slave连接上master之后,slave会向master发送同步消息请求
  2. 当master收到slave的同步请求后,会把主服务器中的数据持久化到rdb文件中,发送给从服务器,从服务器再将rdb文件读取内存
  3. 每次master进行写操作之后,会将数据备份到从服务器中

薪火相传

在这里插入图片描述

127.0.0.1:6380> slaveof 127.0.0.1 6379

127.0.0.1:6381> slaveof 127.0.0.1 6380
master
slave1
salve2

反客为主

在上图中,当master挂掉,slave1可以晋升为master。使用命令

slaveof no one

变为下图的模式:

master
salve2

哨兵模式

反客为主的自动版。在工作目录创建文件sentinel.conf写入如下内容:

//sentinel.conf
sentinel monitor mymaster 127.0.0.1 6379 1

前台启动哨兵:redis-sentinel sentinel.conf

当主机挂掉,从机上位成为master。一旦主机再次开启,它会成为slave

在这里插入图片描述

优先级:replica-priority 100,值越小优先级越高


集群

(无中心化)集群解决了什么问题:增大容量,提高并发

在这里插入图片描述
任何一台服务器都可以作为集群的入口

在这里插入图片描述

一个集群中至少要有3个主节点,为了实现集群,编写6个配置文件:

$ ls
cp.sh  redis6379.conf  redis6380.conf  redis6381.conf  redis6389.conf  redis6390.conf  redis6391.conf  redis.conf  sentinel.conf

$ cat redis6379.conf 
include /root/redis_master_slave/redis.conf
pidfile "/var/run/redis_6379.pid"
port 6379
dbfilename "dump6379.rdb"

cluster-enabled yes
cluster-config-file nodes-6379.conf
cluster-node-timeout 15000

启动服务器:

#! /bin/bash
redis-server redis6379.conf
redis-server redis6380.conf
redis-server redis6381.conf

redis-server redis6389.conf
redis-server redis6390.conf
redis-server redis6391.conf

启动集群:

$ redis-cli --cluster create --cluster-replicas 1 192.168.163.132:6379 192.168.163.132:6380 192.168.163.132:6381 192.168.163.132:6389 192.168.163.132:6390 192.168.163.132:6391
#1表示我们希望为集群中的每个主节点创建一个从节点,最简单的方式

连接到集群:

reids-cli -c -p 6379	//任何节点都可以
127.0.0.1:6379> cluster nodes	//查看节点信息

IP分配原则(HA原则):尽量保证每个主数据库运行在不同的IP,同时保证从库和主库运行在不同的IP

slot的概念:

在这里插入图片描述
作用:将数据哈希分散到不同的节点

Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
Adding replica 192.168.163.132:6390 to 192.168.163.132:6379
Adding replica 192.168.163.132:6391 to 192.168.163.132:6380
Adding replica 192.168.163.132:6389 to 192.168.163.132:6381

向集群中插入一个节点:

127.0.0.1:6379> set k1 v1
-> Redirected to slot [12706] located at 192.168.163.132:6381
OK

同时插入多个值会造成问题:

192.168.163.132:6381> mset k1 v1 k2 v2 k3 v3
(error) CROSSSLOT Keys in request don't hash to the same slot

通过分组来解决:

# key{group} value
192.168.163.132:6381> mset name{user} lucy age{user} 20 address{user} hefei
-> Redirected to slot [5474] located at 192.168.163.132:6380
OK

查询key所在的插槽:

192.168.163.132:6380> cluster keyslot name
(integer) 5798
192.168.163.132:6380> cluster keyslot age
(integer) 741

计算插槽中有几个keys:

192.168.163.132:6380> cluster countkeysinslot 5474
(integer) 3

获取slots中的keys:

192.168.163.132:6380> cluster getkeysinslot 5474 3
1) "address{user}"
2) "age{user}"
3) "name{user}"

一旦主机挂掉,经过一段时间后从机自动成为master

127.0.0.1:6380> cluster nodes
b6699c1f2f9db9f9e434bc8aa37b03572a7e798a 192.168.163.132:6381@16381 master - 0 1662726272069 3 connected 10923-16383
4a4fe2cfad9a93240ddccc8b91963cefc6e77679 192.168.163.132:6391@16391 master - 0 1662726273121 7 connected 0-5460
e27bd104fe6c37de925e096d2e96a72b5a9b823b 192.168.163.132:6390@16390 slave b6699c1f2f9db9f9e434bc8aa37b03572a7e798a 0 1662726275289 3 connected
7a7fcc91754f5eb6988e63e6464a008b971885db 192.168.163.132:6389@16389 slave f38819d648f5a3335db6faf6b322a53957aed349 0 1662726274208 2 connected
f38819d648f5a3335db6faf6b322a53957aed349 192.168.163.132:6380@16380 myself,master - 0 1662726274000 2 connected 5461-10922
22f7843c7e1200c0b9be9ef0f91b522a1a1a0049 192.168.163.132:6379@16379 master,fail - 1662726249440 1662726246000 1 disconnected

而一旦主机重启,他会成为原本从机的slave

如果主从都挂掉,redis能否继续提供服务?

在这里插入图片描述


应用问题解决

缓存穿透

在这里插入图片描述

原因:

  1. redis无法同步数据
  2. 出现了很多非正常的恶意url访问,请求不存在的资源

解决方案:

  1. 对空值做缓存:如果一个查询结果为空,则对这个null值做缓存,但是设置过期时间很短
  2. 设置可访问名单(白名单):使用bitmaps定义可访问名单,使用名单id做bitmaps的索引
  3. 使用布隆过滤器
  4. 进行实时监控:当发现redis的命中率开始急剧降低,排查访问对象,设置黑名单

缓存击穿

原因:redis中某个key过期了,但是在某一时间大量访问这个key,从而压垮了后端DB

在这里插入图片描述
解决方案:

在这里插入图片描述


缓存雪崩

在这里插入图片描述
现象:数据库压力变大

原因:在极少的时间段内,出现了大量key过期的问题,导致后端DB压力瞬间增大

解决方案:

在这里插入图片描述
在这里插入图片描述


分布式锁

在这里插入图片描述

主流实现方案:

  1. 基于数据库实现分布式锁
  2. 基于redis
  3. 基于zookeeper

setnx k1 10就相当于一把锁,k1的值只能被设置一次。将锁设置过期时间,防止其一直不被释放:

192.168.163.132:6380> setnx l1 10		//setnx上锁
(integer) 1
192.168.163.132:6380> expire l1 20		//设置过期时间
(integer) 1
192.168.163.132:6380> ttl l1
(integer) 18
192.168.163.132:6380> setnx l1 20		//尝试修改锁,失败
(integer) 0
192.168.163.132:6380> get l1
"10"
192.168.163.132:6380> ttl l1			//已过期
(integer) -2
192.168.163.132:6380> setnx l1 20		//锁过期后可以修改
(integer) 1
192.168.163.132:6380> get l1
"20"
192.168.163.132:6380> del l1			//删除锁
(integer) 1
192.168.163.132:6380> get l1
(nil)

为了保证上锁和设置过期时间的原子性,可以让二者同时进行:set l2 10 nx ex 12

192.168.163.132:6380> set l1 10 nx ex 30	//同时设置锁和过期时间
OK
192.168.163.132:6380> ttl l1
(integer) 28
192.168.163.132:6380> get l1
"10"
192.168.163.132:6380> setnx l1 20			//尝试修改锁
(integer) 0
192.168.163.132:6380> get l1				//修改失败
"10"
192.168.163.132:6380> ttl l1
(integer) 14
192.168.163.132:6380> del l1
(integer) 1
192.168.163.132:6380> get l1
(nil)

使用UUID防止误删除:

UUID表示不同的操作

释放锁的时候,要判断当前的UUID和被释放锁的UUID是否一样

set lock uuid nx ex 10

使用lua脚本保证删除的原子性:

在这里插入图片描述


新功能介绍

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

高危命令:

flushdb
keys *
shutdown

ACL功能对用户进行更细粒度的权限控制:

  1. 接入权限:用户名和密码
  2. 可以执行的命令
  3. 可以操作的key

操作命令:

acl list	//查看acl中有哪些用户
acl setuser username		//添加用户
acl whoami			//查看当前用户

IO多线程:

在这里插入图片描述
如何开启:修改配置文件的如下属性

io-threads 4
io-threads-do-reads yes
尚硅谷是一个教育机构,他们提供了一份关于Redis学习笔记。根据提供的引用内容,我们可以了解到他们提到了一些关于Redis配置和使用的内容。 首先,在引用中提到了通过执行命令"vi /redis-6.2.6/redis.conf"来编辑Redis配置文件。这个命令可以让你进入只读模式来查询"daemonize"配置项的位置。 在引用中提到了Redis会根据键值计算出应该送往的插槽,并且如果不是该客户端对应服务器的插槽,Redis会报错并告知应该前往的Redis实例的地址和端口。 在引用中提到了通过修改Redis的配置文件来指定Redis的日志文件位置。可以使用命令"sudo vim /etc/redis.conf"来编辑Redis的配置文件,并且在文件中指定日志文件的位置。 通过这些引用内容,我们可以得出结论,尚硅谷的Redis学习笔记涵盖了关于Redis的配置和使用的内容,并提供了一些相关的命令和操作示例。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [Redis学习笔记--尚硅谷](https://blog.csdn.net/HHCS231/article/details/123637379)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [Redis学习笔记——尚硅谷](https://blog.csdn.net/qq_48092631/article/details/129662119)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值