• Redis学习笔记


    https://redis.io/

    课程地址

    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
    
    • 1
    • 2
    • 3

    在这里插入图片描述
    切换数据库:

    127.0.0.1:6379> select 1
    OK
    
    • 1
    • 2

    Redis五大基础数据类型

    • String
    • List
    • Set
    • Hash
    • Zset

    在这里插入图片描述

    字符串

    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    数字增减:

    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"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    i=0,两个线程分别执行100次i++,结果会是多少?

    答案:2~200

    设置多个值

    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"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    操作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"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    设置超时时间:

    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"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    取出新值输出旧值

    127.0.0.1:6379> getset str fuckyou
    "fucko,world"
    127.0.0.1:6379> get str
    "fuckyou"
    
    • 1
    • 2
    • 3
    • 4

    列表

    操作命令: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"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48

    在这里插入图片描述

    集合

    与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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    集合的交并差

    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"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    哈希

    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    在这里插入图片描述

    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"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48

    有序集合

    操作命令:

    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    在这里插入图片描述

    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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    配置文件

    使能远程连接:

    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	#发布者向频道中发布消息
    
    • 1
    • 2
    • 3

    Redis三大新数据类型

    bitmap

    操作命令:

    setbit key offset value
    getbit key offset
    bitcount key
    bitop operation destkey key [key...]
    
    • 1
    • 2
    • 3
    • 4
    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    HyperLogLog

    操作命令:

    pfadd key [element[element...]]
    pfcount key [key...]
    pfmerge destkey sourcekey [sourcekey...]
    
    • 1
    • 2
    • 3

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

    在这里插入图片描述

    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    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]
    
    • 1
    • 2
    • 3
    • 4
    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"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    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);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    测试字符串相关操作:

        //测试字符串操作
        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);
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    测试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);
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    测试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);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    测试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);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    Redis事务

    事务的基本操作

    在这里插入图片描述
    三个命令:multi, exec, 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"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    使用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"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    错误处理:

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

    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.
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    如果在执行过程中某一条命令执行失败,则只会影响到这一条命令的结果,不会像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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    乐观锁与悲观锁

    悲观锁:

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

    在这里插入图片描述
    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>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    另一个事务:

    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"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    如果需要恢复数据,只需将备份文件 (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
    • 2
    • 3
    • 4
    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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    1. 连接三台服务器:
    redis-cli -p 6379
    redis-cli -p 6380
    redis-cli -p 6381
    
    • 1
    • 2
    • 3

    通过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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    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
    
    • 1
    • 2
    • 3
    • 4

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

    #主机写
    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.
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    当某一台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
    
    • 1
    • 2
    • 3
    master
    slave1
    salve2

    反客为主

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

    slaveof no one
    
    • 1

    变为下图的模式:

    master
    salve2

    哨兵模式

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

    //sentinel.conf
    sentinel monitor mymaster 127.0.0.1 6379 1
    
    • 1
    • 2

    前台启动哨兵: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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    启动服务器:

    #! /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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    启动集群:

    $ 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表示我们希望为集群中的每个主节点创建一个从节点,最简单的方式
    
    • 1
    • 2

    连接到集群:

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

    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    向集群中插入一个节点:

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

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

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

    通过分组来解决:

    # 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
    
    • 1
    • 2
    • 3
    • 4

    查询key所在的插槽:

    192.168.163.132:6380> cluster keyslot name
    (integer) 5798
    192.168.163.132:6380> cluster keyslot age
    (integer) 741
    
    
    • 1
    • 2
    • 3
    • 4
    • 5

    计算插槽中有几个keys:

    192.168.163.132:6380> cluster countkeysinslot 5474
    (integer) 3
    
    • 1
    • 2

    获取slots中的keys:

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

    一旦主机挂掉,经过一段时间后从机自动成为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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    而一旦主机重启,他会成为原本从机的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)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    为了保证上锁和设置过期时间的原子性,可以让二者同时进行: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)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    使用UUID防止误删除:

    UUID表示不同的操作

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

    set lock uuid nx ex 10
    
    • 1

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

    在这里插入图片描述


    新功能介绍

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

    高危命令:

    flushdb
    keys *
    shutdown
    
    • 1
    • 2
    • 3

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

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

    操作命令:

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

    IO多线程:

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

    io-threads 4
    io-threads-do-reads yes
    
    • 1
    • 2
  • 相关阅读:
    最新版VMWare安装Linux的详细过程
    2000-2018年286个地级市PM2.5数据
    java与es8实战之五:SpringBoot应用中操作es8(带安全检查:https、账号密码、API Key)
    从零基础到车载测试工程师,薪资11K,肯拼搏的人,总会有所收获
    《游戏系统设计十五》游戏房间服的设计
    AST反混淆实战|某国外混淆框架一小段混淆js还原分析
    支付宝服务商第三方代发布小程序
    小程序页面导航和页面事件
    YoloV8改进策略:NWD小目标检测新范式,助力YoloV5、V8在小目标上暴力涨点
    Feign(黑马程序员)
  • 原文地址:https://blog.csdn.net/DanielSYC/article/details/124505267