• Redis学习笔记整理_01


    传送门: redis中文网地址

    注:redis注重的是速度,所以在做技术选型的时候因为一些其他原因而牺牲掉redis的速度,那还不如不用

    1. redis 是单进程的
    2. 系统中可以有多个 redis 实例,每个实例中可以有多个库,库中存的都是键值对。
    3. key 可以设置过期时间,但过期时间不会随着访问而延长,如果有效期内发生,会剔除过期时间

    一、数据类型

    1. key是个对象,包含 type 类型、字节长度和 encoding
      type 用来快速判断接下来的命令 vlaue 的数据类型是否可行,而不是真的用数据参与计算然后再报异常
      encoding 用来拿字节做计算
      在这里插入图片描述

    1. String类型

    字符串set、get、append、setrange、getrange、strlen、msetnx(原子性操作)
    String 、 byte数值incr、incrby、decr、decrby、incrbyfloat
    bitmapsetbit 、bitcount、bitpos、bitop

    1)字符串

    --设置 key value
    set k1 hello;	get k1;   --hello
    
    -- key没有的时候生效,可用在分布式,多个线程操作一个,只会有一个操作成功,其余返回nil
    set k1 ooxx nx; -- nil		
    get k1;   -- hello	
    
    -- 只能更新,没值不能设	
    set k2 hello xx;  -- nil
    get k2;  -- nil
    
    -- 设置多个值
    mset k3 a k4 b;	 -- OK
    get k3;  -- a
    get k4; -- b
    
    -- 获取多个
    mget k3 k4; -- 'a' 'b'
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    -- 追加 append
    get k1;  -- hello
    append k1 " world";  -- (integer)11
    get k1; -- hello world
    
    -- 截取 getrange
    getrange k1 6 10;  -- world
    
    -- 正反向索引 
    getrange k1 6 -1;  -- world
    getrange k1 0 -1;  -- hello world
    
    -- setrange  覆盖
    setrange k1 6 zjh  -- (integer)9
    get k1;  -- hello zjh
    
    -- 获取字符串长度  strlen
    strlen k1;  -- (integer) 9
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    -- getset : 替换 value 并返回旧值,减少io通信
    set k1 hello; -- ok
    getset k1 zjh; -- hello
    get k1; -- zjh
    
    -- msetnx 设置多个不存在的键值 原子性操作
    msetnx k1 a k2 b; -- 1
    mget k1 k2; -- a b
    msetnx k2 c k3 b; -- 0
    mget k1 k2 k3; -- a b  
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    --通过 key 查看 value 类型 (key 中有描述 value 类型的 type)
    type k1;   -- string
    
    • 1
    • 2

    --- 注意
    get k1;  -- hello zjh
    set k1 99;  --  ok
    set k2 hello; -- ok
    type k1; -- string
    type k2; -- string
    object encoding k2; -- embstr
    object encoding k1; -- int
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    2)数值

    可用于抢购、秒杀、详情页、点赞、评论数等,规避并发下,对数据库的事务操作,完全由redis内存操作代替

    --- incrby
    get k1; -- 99
    incr k1;  -- (integer)100
    get k1; -- 100
    incrby k1 22;  -- (integer)122
    get k1; -- 122
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    -- decrby
    get k1; -- 122
    decr k1; -- 121
    decrby k1 22; -- 99
    
    • 1
    • 2
    • 3
    • 4
    -- incrbyfloat
    get k1; -- 99
    incrbyfloat k1 0.5 -- 99.5
    
    • 1
    • 2
    • 3
    注:类型转换

    二进制安全:只取字节流

    set k2 9; -- ok
    object encoding k2; -- int
    strlen k2; -- (integer)1
    append k2 999; -- (integer)4
    get k2; -- 9999
    object encoding k2 -- raw
    incr k2; -- (integer)10000
    object encoding k2 -- int
    strlen k2 -- (integer)5
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    3)bitmap

    -- setbit
    setbit k1 1 1; --0
    strlen k1;  -- 1
    get k1; -- @
    setbit k1 7 1; -- 0
    strlen k1;  -- 1
    get k1;  -- A
    setbit k1 9 1;  -- 0
    strlen k1;  -- 2
    get k1; -- A@
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    在这里插入图片描述

    -- bitpos 二进制字节索引  返回全量的二进制位  
    
    bitpos k1 1 0 0; -- (integer)1     0 0 是字节索引
    bitpos k1 1 1 1; -- (integer)9
    bitpos k1 1 0 1; -- (integer)1     在k1当中,二进制 1 在 0~1 字节中的二进制位置
    
    • 1
    • 2
    • 3
    • 4
    • 5
    -- bitcount 
    bitcount k1 0 1;  -- (integer)3    k1 里 0~1 字节中间 二进制 1 的数量
    
    • 1
    • 2
    -- bitop  二进制字节操作,与或非
    setbit k1 1 1; -- (integer)0
    setbit k1 7 1; -- (integer)0
    get k1; -- A    0100 0001
    setbit k2 1 1;
    setbit k2 6 1;
    get k2;  -- b  0100 0010
    bitop and andkey k1 k2  --  按位与(全一为一)
    get andkey;  -- @  0100 0000
    
    bitop or orkey k1 k2  -- 按位或(有一则一)
    get orkey  -- C 0100 0011
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    解决需求
    1. 有用户系统,统计用户登录天数,且窗口随机
    ---- 人员做为 key 365天对应到二进制位,-2  -1 表示16个二进制位里 1 的个数,
    setbit sean 1 1
    setbit sean 7 1
    setbit sean 364 1
    strlen sean
    bitcouny sean -2 -1
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    01020304
    人员01010101010101…
    人员020101011111…
    1. 活跃用户统计,窗口随机。比如说 1号 - 3号 连续登录要去重
    ---- 将人员对应二进制位,日期做为 key
    setbit 20190101 1 1
    setbit 20190102 1 1
    setbit 20190102 7 1
    bitop or destkey 20190101 20190102
    bitcount destkey 0 -1
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    u1u2u3
    2019010101000110…
    2019010211010101…

    2.list

    --	lpush    rpush
    lpush k1 a b c d e f  -- (integer)6    fedcba
    rpush k2 a b c d e f  -- (integer)6    abcdef
    lpop k1  -- f
    lpop k1  -- e
    lpop k1  -- d
    rpop k2  -- f
    rpop k1  -- a
    lrange k1 0 -1 -- c b
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    -- lindex 根据下标取出
    lrange k1 0 -1  -- f e d c b a
    lindex k1 2  -- d
    lindex k1 -1  -- a
    
    --  lset 根据下标替换
    lset k1 3 xxxx  --  ok
    lrange k1 0 -1  --  f e d xxxx b a
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    -- lrem  linsert
    lpush k3 1 a 2 b 3 a 4 c 5 a 6 d  -- (integer)12
    lrange k3 0 -1  --  d 6 a 5 c 4 a 3 b 2 a 1
    lrem k3 2 a --  (integer)2      移除 k3 里的 2 个 a,正数从左往右删除
    lrange k3 0 -1  --  d 6 5 c 4 3 b 2 a 1
    linsert k3 after 6 a  --  (integer)11   在 6 的后面插入 a
    lrange k3 0 -1  --  d 6 a 5 c 4 3 b 2 a 1
    linsert k3 before 3 a  --  (integer)12   在 3 的前面插入 a
    lrange k3 0 -1  --  d 6 a 5 c 4 a 3 b 2 a 1
    
    lrem k3 -2 a  --  (integer)2     负数从右往左删除
    lrange k3 0 -1   --   d 6 a 5 c 4 3 b 2 1
    
    llen k3  --  10    统计元素个数
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    三个客户端连接同一端口同一进程的redis
    在这里插入图片描述

    -- trim  删除两边
    lpush k4 a b c d e d f d fs sdf sdf sdf sd f
    lrange k4 0 -1  -- f sd sdf sdf sdf fs d f d e d c b a
    ltrim k4 2 -2  --  ok
    lrange k4 2 -2  -- sdf sdf sdf fs d f d e d c b
    
    • 1
    • 2
    • 3
    • 4
    • 5

    3.hash

    --  引
    set sean::name 'zjh'  -- ok
    get sean::name  -- zjh
    set sean::age 18  --  ok
    get sean::age  --  18
    keys sean*  --  sean::name  sean::age
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    --  hash
    hset sean name zjh  -- (integer)1
    hmset sean age 18 address sy  --  ok
    hget sean name  --  zjh
    hmget sean name age  --  zjh 18
    hkeys sean  --  name age address
    hvals sean  --  zjh 18 sy
    hgetall sean  --  name zjh age 18 address sy
    
    hincrbyfloat sean age 0.5  --  18.5
    hget sean age  --  18.5
    hincrbyfloat sean age -1  --  17.5
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    4.set

    1. smembers 很耗费网络的吞吐量
    2. sinter 交集 , sinterstore 将交集存在给定的 key 里
      sunion 并集 , sunionstore 同理
    3. srandmember key count count 为正数,取出一个去重的结果集(不能超过已有集)
      如果是负数,取出一个带重复的结果集,一定满足你要的数量。
      0 不返回
    --  smembers
    sadd k1 tom sean peter ooxx tom xxoo  --  (integer) 5
    smembers k1  --  sean tom ooxx peter xxoo   tom被去重
    
    srem k1 ooxx xxoo  --  (integer) 2
    smembers k1  --  tom peter sean
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    --  sinter  sinterstore
    sadd k2 1 2 3 4 5
    sadd k3 4 5 6 7 8
    sinter k2 k3  --  4 5    交集
    sinterstore dest k2 k3  --  (integer) 2    将交集存到 dest 里,节省IO
    smembers dest  --  4 5 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    --- 差集  想取数据的放左面,右面放参考
    sdiff k2 k3  --  1 2 3
    sdiff k3 k2  --  6 7 8
    
    • 1
    • 2
    • 3
    --  srandmember  随机
    sadd k1 tom ooxx xxoo xoxo oxox xoox oxxo
    srandmember k1 3  --  tom xoox oxox  --  不重复随机结果
    srandmember k1 -3  --  oxox ooxx oxox    包含重复结果
    srandmember k1 -10  --  tom oxox oxox xoxo xxoo ooxx tom xoox xxoo tom
    
    -- spop  随机取出不放回  取出一个
    spop k1  --  oxox
    spop k1  --  xxoo
    spop k1  --  xoxo
    .
    .
    .
    spop k1  --  tom
    spop k1  --  nil
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    5.sorted set

    物理内存,排序方式左小右大,且不随命令发生变化

    
    zadd k1 8 apple 2 banana 3 orange  --  (integer)3
    zrange k1 0 -1  --  banana orange apple
    zrange k1 0 -1 withscores  --  banana 2 orange 3 apple 8
    
    -- 按分值取
    zrangebyscore k1 3 8  --  orange apple
    
    -- 价格由低到高取出前两名
    zrange k1 0 1  --  banana orange
    
    -- 价格由高到低取出前两名
    zrevrange k1 0 1  --  apple orange
    
    -- 根据元素取出分值
    zscore k1 apple  --  8
    
    -- 取出排名
    zrank k1 apple  --  2
    
    
    --  数值计算
    zrange k1 0 -1 withscores  --  banana 2 orange 3 apple 8
    zincrby k1 2.5 banana  --  4.5
    zrange k1 0 -1 withscores  --  orange 3 banana 4.5 apple 8
    
    • 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
    zunionstore目标key参与并集的key的数量key的名称权重聚合
    zunionstoreunkey12k1 k2weights 1 0.5默认为相加
    zadd k1 80 tom 60 sean 70 baby  --  (integer)3
    zadd k2 60 tom 100 sean 40 yiming  --  (integer)3
    
    --  取 k1 k2 并集,其余参数默认
    zunionstore unkey 2 k1 k2  --  (integer) 4
    zrange unkey 0 -1 withscores  --  yiming 40 baby 70 tom 140 sean 160      默认情况下相同项相加
    
    zunionstore unkey1 2 k1 k2 weights 1 0.5  --  (integer) 4       权重:  1 分值不变    0.5 分值除以2
    zrange unkey1 0 -1 withscores  --  yiming 20 baby 70 sean 110 tom 110
    
    -- 并集取最大值
    zunionstore unkey2 2 k1 k2 aggregate max  --  (integer) 4
    zrange unkey2 0 -1 withscores  --  yiming 40 baby 70 tom 80 sean 100
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    二、发布订阅

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

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

    下图展示了频道 channel1 , 以及订阅这个频道的三个客户端 —— client2 、 client5 和 client1 之间的关系:
    在这里插入图片描述

    当有新消息通过 PUBLISH 命令发送给频道 channel1 时, 这个消息就会被发送给订阅它的三个客户端:
    在这里插入图片描述

    --  创建订阅频道
    redis 127.0.0.1:6379> SUBSCRIBE redisChat
     
    Reading messages... (press Ctrl-C to quit)
    1) "subscribe"
    2) "redisChat"
    3) (integer) 1
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    --  向订阅频道发送消息
    redis 127.0.0.1:6379> PUBLISH redisChat "Redis is a great caching technique"
     
    (integer) 1
     
    redis 127.0.0.1:6379> PUBLISH redisChat "Learn redis by w3cschool.cc"
     
    (integer) 1
     
    # 订阅者的客户端会显示如下消息
    1) "message"
    2) "redisChat"
    3) "Redis is a great caching technique"
    1) "message"
    2) "redisChat"
    3) "Learn redis by w3cschool.cc"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    三、事务

    redis 为速度服务,所以并没有真正的事务,它可以将命令绑在一起一股脑的执行,如果中间有命令失败,就进行撤销,但不会百分百成功

    MULTI 标记一个事务块的开始。
    EXEC 执行所有事务块内的命令。

    如下图,redis 是单线程,若客户端01 的 exec 命令先到达,就先执行客户端01 的命令块,若客户端02 的 exec 命令先到达,就先执行客户端 02 的命令块

    在这里插入图片描述
    但如果客户端 2 的指令先执行,会将 k1 删除,客户端 1 执行的时候会报错。redis 提供了 WATCH 命令,可以对 key 进行监控。但这个监控是 CAS 的,如果 k1 的 value 被修改,那么 WATCH 后的命令块不会执行。

    四、布隆过滤器

    将检索要素映射到二进制位,用到的二进制位变为 1。搜索的时候如果对应的二进制位有返回 0 的,那么就可以明确没有相应的搜索项。这么做可以增加防穿透的概率,但不能100%

    1. 确定自己有什么
    2. 有的向bitmap中标记
    3. 请求的可能被勿标记
    4. 会减少大量的放行:穿透
    5. 成本低

    五、持久化

    当数据量很大,并发很高的时候,想要将数据存到磁盘必然会有一个持续时间,在这段时间中数据肯定会有修改,假如想要备份 24 点整的数据,不阻塞的话很难保证不会有脏数据

    以下为单机

    1. 基本知识

    了解 redis 持久化之前,需要先掌握几个知识:

    1. RDB AOF
    2. 管道
    3. 父子进程
    4. fork()

    缓存:数据可以丢,急速
    数据库:数据绝对不能丢 速度+持久性 掉电易失

    存储层:

    1. 快照 / 副本 (备份数据)
    2. 日志 (数据丢失后可根据日志找回)

    RDB : 快照
    AOF : 日志

    Linux系统:管道

    1. 衔接,前一个命令的输出做为后一个命令的输入
    2. 管道会触发创建【子进程】
    echo $$ | more
    echo $BASHPID | more
    $$ 高于 |
    
    • 1
    • 2
    • 3

    父子进程:

    1. Linux中,export的环境变量,子进程的修改不会破坏父进程;父进程的修改也不会破坏子进程

    子进程修改变量,不会影响父进程

    #!/bin/bash          -- 写一个脚本,定义被谁运行,
    echo $$          -- 获取当前进程
    echo $num    -- 定义变量 num
    num=999
    echo num:$num    -- 取值验证是否修改成功
    
    sleep 20
    
    echo $num    -- 验证有没有被父进程修改
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    [root@bash ~]# ./test.sh &    -- 让脚本在后台运行
    [1] 114155					  --  子进程运行结果
    [root@bash ~]# 114155         --  子进程运行结果
    1							  --  子进程运行结果
    num:99                        --  子进程运行结果
    
    [root@bash ~]# echo $$        --  当前为父进程
    114017
    [root@bash ~]# echo $num	  --  获取 num   1
    1
    [root@bash ~]# 999   		  --  20秒结束,子进程变量为 99
    
    [1]+ Done                  ./test.sh
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    父进程修改变量,不会影响子进程

    echo $$  #当前为父进程
    114017
    
    echo $num  #获取num
    1
    
    ./test.sh &    #运行脚本
    [1] 114158  #子进程运行结果
    114158    #子进程运行结果
    1         #子进程运行结果
    num:999  #子进程运行结果
    
    echo $num  #获取父进程 num
    1
    
    num=888  # 修改父进程 num 变量
    echo $num  # 查看父进程 num
    888
    
    999    #等待20秒,子进程为999
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    fork() 快速创建一个子进程
    利用父子进程的特点进行数据备份, 创建子进程时不发生复制,这使得创建子进程的速度变快,数据是写时备份(copy on write),也就是进程中数据修改时再进行备份,先创建变量,然后修改指针。

    redis 持久化数据,利用 fork()命令 copy on write 一个子进程,父进程数据的修改不会影响子进程,以此保证时点正确

    2. RDB

    特性:

    1. 时点性
    2. save (阻塞存储,比如关机维护)
    3. bgsave (fork创建子进程)
    4. 配置文件中给出bgsave规则,save这个表示

    注意文件名和存储位置,在配置文件中是分开的
    文件名 :dbfilename dump.rdb
    存储路径:dir /var/lib/redis/6379

    弊端:
    不支持拉链,仅有一个 dump.rdb

    3. AOF

    1. redis的写操作记录到文件中
    2. 丢失数据少
    3. redis 中,RDB 和 AOF 可以同时开启,如果开启了 AOF ,只会用 AOF 恢复数据redis 4.0 以后,AOF 中包含 RDB 全量,增加记录新的写操作

    缺点:体谅无限变大,恢复慢


    对缺点进行优化:

    1. 4.0以前,重写,删除抵消的命令,合并重复的命令,最终也是一个纯指令的日志文件
    2. 4.0以后,重写,将老的数据 RDB 到 AOF 文件中,将增量的以指令的方式 append 到 AOF,这样 AOF 是一个混合体,利用了 RDB 的快,利用了日志的全量

    但是,redis 是内存数据库,写操作会触发IO

    appendonly yes    # 打开异步RDB到磁盘,会造成数据丢失
    
    appendfilename*appendonly.aof*    # 文件名称
    
    appendfsync always    # 每一次io 都调用一次 flush,数据可靠
    appendfsync everysec    # 每秒,丢的少,丢的最多的时候就是空间差一点点满,1秒时间到了,从这一秒开始往后进行磁盘写入,不过这种情况概率小
    appendfsync no    # io以后不进行 flush 操作,内核数据满了以后往磁盘写,但会残留
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    验证时需要调整的配置文件项:

    ps -fe | grep redis    # 验证是否关闭所有 redis
    
    vi /etc/redis/6379.conf    # 修改配置文件
    # daemonize no    不在后台运行,在前台阻塞运行
    # #logfile /var/log/redis_6379.log    关闭日志文件,输出到屏幕
    # appendonly yes    开启追加
    # aof-use-rdb-preamble no    关闭混合
    
    redis-server /etc/redis/6379.conf    # 通过配置文件启动 redis
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
  • 相关阅读:
    利用编码特长,我赚取了每月1000美元的额外收入
    excel怎么保存为模板,excel一张图怎么设置同一的格式,excel怎么保存为图表样式
    C# 文本分类之朴素贝叶斯分类
    域内批量解析chrome浏览器
    二进制编码及浮点数表示
    Windows RDS远程会话服务
    webpack-bundle-analyzer 插件配置
    js实现tab栏切换
    MediaSoup简介
    【Redis知识点总结】(二)——Redis高性能IO模型剖析
  • 原文地址:https://blog.csdn.net/weixin_42547014/article/details/127371151