Redis

windows版本

linux版本操作参考


NoSQL

NoSQL是解决性能问题的一种技术,Redis是一种典型的NoSQL数据库

image-20220223122352249




Redis

Redis的操作基本都是基于内存的,CPU资源根本就不是Redis的性能瓶颈,所以Redis不需要通过多线程技术来提升CPU利用率。

redis是设计为为缓存使用的

redis所有的操作都是原子性的。采用单线程处理所有业务,命令是一个一个执行的,因此无需考虑并发带来的数据影响

基础操作

开启 redis 数据库

image-20220223123433600

在安装目录下使用 redis-server.exe redis.windows.conf 命令打开


help命令

help command 获得指定命令的帮助文档

help 获得一个列表的帮助文档

help @group 获得群组的帮助文档


set命令

set key value

插入一个数据,以 key 为键,以 value 为值

image-20220223124451651


get命令

get key

以 key 获得一个数据

image-20220223124527201


del命令

del key

删除一个数据

image-20220223125929261


quit、exit命令

quit / exit

退出客户端

image-20220223124743380


数据类型

string

redis 自身是一个Map,其中所有的数据都是采用 key : value 的形式存储的

数据类型指的是存储的数据,key部分永远都是字符串

如果字符串以整数的形式展示,则可以作为数字使用,但还是个字符串

image-20220223125557894


  • mset命令

    一次添加多个数据,添加多个数据建议使用mset,但有极大的数据,建议分割后再发送,因为执行极大量的数据会造成线程阻塞

    image-20220223130220363

    image-20220223130445381


  • mget命令

    一次获取多个数据,获取多个数据建议使用mget,但有极大的数据,建议分割后再发送,因为执行极大量的数据会造成线程阻塞

    image-20220223130344753


  • strlen命令

    获取某个数据的字符长度

    image-20220223130645478


  • append命令

    往一个数据的后面追加字符串

    image-20220223130817294

    image-20220223130751676


string类型数据的扩展操作

业务场景:大型企业中,分表是基本操作,使用多张表存储相同类型数据,但是对于主键id必须要不能重复

设置数值数据增加指定范围的值

  • incr命令

    让指定的数据自增一

image-20220223184032269


  • incrby命令

    让指定的数据增加指定的值

    image-20220224100602737


  • incrbyfloat命令

    让指定的数据增加指定的值(小数值)

    image-20220224100743968


设置数值数据减小指定范围的值

  • decr命令

让指定的数据自减一

image-20220223184119282


  • decrby命令

    让指定的数据减小指定的值(小数值)

    image-20220224100917546


业务场景:海选投票,只能通过微信投票,每个微信号每4小时只能投1票

  • setex命令

    添加一个值,并限制其过期的时间

    setex key seconds value

image-20220224105534015

  • psetex命令

    setex命令的毫秒版本


业务场景:展示某用户的粉丝数量,博客数量

格式一:

image-20220224111049219

格式二:

image-20220224111258626




hash

  • 新的存储结构:对一系列存储的数据进行编组,方便管理,典型应用存储对象信息
  • 需要的存储结构:一个存储空间保存多个键值对数据
  • hash类型:底层使用哈希表结构实现数据存储

image-20220224151853159


  • 添加 / 修改数据


    hset key field value

    1
    2
    3
    hset user name zhangsan
    hset user age 38
    hset user id 1

    批量添加 / 修改数据

    hmset key field1 value1 field2 value2…

    1
    hmset user name zhangsan age 38 id 1

  • 获取数据


    hget key field

    1
    hget user name // ==> "zhangsan"

    hgetall key

    1
    2
    3
    4
    5
    6
    7
    hgetall user  // ==> 单数为field
    1) "name"
    2) "zhangsan"
    3) "age"
    4) "38"
    5) "id"
    6) "1"

    批量获取数据

    hmget key field1 field2…

    1
    hmget user name age

  • 删除数据


    hdel key field

    1
    hdel user name age id

  • 获取哈希表中字段的数量


    hlen key

    1
    hlen user  // ==> 3

  • 获取哈希表中是否存在指定的字段


    hexists key field

    1
    hexists user name // ==> 1 (1表示true,0表示false)

hash类型数据的扩展操作

  • 获取数据


    获取哈希表中所有的键

    hkeys key

    1
    2
    3
    4
    hkeys user // ==>
    1) "id"
    2) "name"
    3) "age"

    获取哈希表中有的值

    hvals key

    1
    2
    3
    4
    hvals user // ==>
    1) "1"
    2) "jjw"
    3) "21"

  • 增加 / 减小数据


    设置指定字段的数值数据增加指定范围的值

    hincrby key field incremnet

    1
    hincrby user age 2 // ==> 23

    hincrbyfloat key field incremnet

    1
    hincrby user age 2。5 // ==> 23.5

注:

hash类型下的value只能存储字符串,不允许存储其他数据类型,不存在嵌套现象

hash类型十分贴近对象的数据存储形式,并且可以灵活添加删除对象属性。但hash设计初衷不是为了存储大量对象而设计的,不可滥用


  • 如果当前这个key中有值就不做任何操作,如果没有就添加数据

    hsetnx key field value

    1
    hsetnx user name jjw



list

  • 数据存储需求:存储多个数据,并对数据进入存储空间的顺序进行区分
  • 需要的存储结构:一个存储空间保存多个数据,且通过数据可以体现进入顺序
  • list类型:保存多个数据,底层使用双向链表存储结构实现

  • 添加 / 修改数据

    左添加 / 修改数据

    lpush key value1 value2…

    1
    lpush list huawei xiaomi apple

    右添加 / 修改数据

    rpush key value1 value2…

    1
    rpush list huawei

  • 获取数据

    按范围获取数据

    lrange key start stop

    1
    lrange list 0 2 // ==> "apple" "xiaomi" "huawei"

    按索引获取数据

    lindex key index

    1
    lindex list 1 // ==> "xiaomi"

    获取list类型的长度

    llen key

    1
    llen list

  • 获取并移除数据

    左弹出数据

    lpop key

    1
    lpop list // ==> "apple"

    右弹出数据

    rpop key

    1
    rpop list // ==> "huawei"

list类型数据扩展操作

  • 规定时间内获取并移除数据

    左弹出(b代表阻塞,可以从多个list中获取)

    blpop key1 key2 timeout

    1
    blpop list 30 // 表示如果没有可以等待30秒,30秒之后没有就算了

    右弹出

    brpop key1 key2 timeout


  • 移除指定数据


    从左向右删除

    lrem key count(移除多少个) value

    1
    2
    rpush list a b c c d e
    lrem list 1 c // ==> "a" "b" "c" "d" "e"

注:

  • list中保存的数据都是string类型的,数据总容量是有限的最多2^32-1个元素
  • list具有索引的概念,但是操作数据时通常以队列的形式进行入队出队操作,或栈形式
  • 负数为全部数据操作结束的索引



set

  • 新的存储需求:存储大量的数据,在查询方面提供更高的效率
  • 需要的存储结构:能够保存大量的数据,高效的内部存储机制,便于查询
  • set类型:与hash存储结构完全相同,仅存储键,不存储值,并且值是不允许重复的

image-20220226100410766


  • 添加数据

    sadd key member1 member2…

    1
    sadd users jjw1 jjw2

  • 获取全部数据


    smembers key

    1
    smembers users // ==> "jjw1" "jjw2"

  • 判断集合中是否包含指定数据


    sismember key member

    1
    sismember users jjw1

set类型数据的扩展操作

  • 随机获取集合中指定数量的数据


    srandmember key [count]

    1
    2
    sadd news 1 2 3 4 5
    sranmember news 2 // ==>随机出现 "2" "4"

  • 随机获取集合中的某个数据 并将该数据移出集合

    spop key

    1
    2
    sadd news 1 2 3 4 5
    spop news // ==> "3" 剩下: "1" "2" "4" "5"

  • 求两个集合的交、并、差集


    sinter key1 key2

    1
    2
    3
    sadd set1 1 2 3 4 5
    sadd set2 4 5 6 7 8
    sinter set1 set2 // ==> "4" "5"

    sunion key1 key2

    1
    2
    3
    sadd set1 1 2 3 4 5
    sadd set2 4 5 6 7 8
    sunion set1 set2 // ==> "1" "2" "3" "4" "5" "6" "7" "8"

    sdiff key1 key2

    1
    2
    3
    4
    sadd set1 1 2 3 4 5
    sadd set2 4 5 6 7 8
    sdiff set1 set2 // ==> set1-set2 ==> "1" "2" "3"
    sdiff set2 set1 // ==> set2-set1 ==> "6" "7" "8"

  • 求两个集合的交、并、差集并存储到指定集合中


    sinterstore destination key1 key2

    1
    2
    sinterstore set3 set1 set2
    smembers set3 // ==> "4" "5"

    sunionstore destination key1 key2

    1
    sunionstore set3 set1 set2

    sdiffstore destination key1 key2

    1
    sdiffstore set3 set1 set2

  • 将指定数据从原始集合中移动到目标集合中


    smove source destination member

    1
    2
    smove set1 set2 1
    smembers set2 // ==> "1" "4" "5" "6" "7" "8"



sorted_set

  • 新的存储需求:数据排序有利于数据的有效展示,需要提供一种可以根据自身特征进行排序的方式

  • 需要的存储结构:新的存储模型,可以保存可排序的数据

  • sorted_类型:在set的存储结构基础上添加可排序字段(注意:score字段仅用于排序,不能用于存储数据)

    image-20220226110016770


  • 添加数据


    zadd key score1 member1 score2 member2

    1
    2
    3
    zadd sset 99 lisi
    zadd sset 94 zhangsan
    zadd sset 100 wangwu

  • 获取全部的数据


    zrange key start stop [withscores]

    1
    2
    3
    4
    zrange sset 0 -1 // ==> 由小到大
    "zhangsan"
    "lisi"
    "wangwu"

    zrevrange key start stop [withscores]

    1
    2
    3
    4
    5
    6
    7
    zrevrange sset 0 -1 withscores(带上scores字段) // ==> 由大到小
    "wangwu"
    "100"
    "lisi"
    "99"
    "zhangsan"
    "94"

  • 删除数据


    zrem key member1 member2…

    1
    zrem sset wangwu 

sorted_set类型数据的基本操作

  • 按条件获取数据


    由小到大获取数据

    zrangebyscore key min max [withscores] [limit(限定查出多少个数据)]

    1
    2
    3
    4
    5
    zrangebyscore sset 93 99 withscores // ==>
    "zhangsan"
    "94"
    "lisi"
    "99"

    由大到小获取数据

    zrevrangebyscore key max min [withscores]

    1
    2
    3
    zrevrangebyscore sset 99 93 // ==>
    "lisi"
    "zhangsan"

  • 条件删除数据


    按索引范围删除

    zremrangebyrank key start stop

    1
    zremrangebyrank sset 0 1

    按score字段范围删除

    zremrangebyscore key min max

    1
    zremrangebyscore sset 93 99 // 删除score字段93 99之间的数据

  • 获取集合数据总量

    zcard key


    按score字段范围获取总量

    zcount key min max


  • 集合交、并操作

    zinterstore destination numkeys key1 key2 …

    zunionstore destination numkeys key1 key2 …


  • 获取数据对应的索引


    由小到大查找索引

    zrank key member

    1
    2
    zadd movies 100 a 99 b 150 c
    zrank movies b // ==> 0

    由大到小查找索引

    zrevrank key member

    1
    2
    zadd movies 100 a 99 b 150 c
    zrevrank movies b // ==> 2

  • score值获取与修改


    zscore key member

    1
    zscore movies b // ==> 99

    对某一个数据的score字段进行加操作

    zincrby key increment member

    1
    zincrby movies b 1

注:

  • score保存的数据存储空间是64位
  • score保存的数据也可以是一个双精度的double值,基于双精度浮点数的特征,可能会丢失精度,所以在排序时可能出现排序的问题,使用时候要谨慎
  • sorted_set底层存储的还是基于set结构的,因此数据不能重复,新值会覆盖旧值的score字段

通用命令


key通用操作

  • 删除指定key


    del key

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    set str str1 str2
    hset hash hash1 hash2
    lpush list list1 list2
    sadd set set1 set2
    zadd sset sset1 sset2

    del str
    del hash
    del list
    del set
    del sset

  • 获取key的类型


    type key

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    set str str1 str2
    hset hash hash1 hash2
    lpush list list1 list2
    sadd set set1 set2
    zadd sset sset1 sset2

    type str // ==> string
    type hash // ==> hash
    type list // ==> list
    type set // ==> set
    type sset // ==> sored_set

  • 判断key是否存在


    exists key

    1
    exists list

  • 为key改名


    rename key newkey

    1
    rename user1 user2

    如果newkey不存在才改名

    renamenx key newkey

    1
    renamenx user1 user2

  • 对所有key排序


    对一个集合类型进行排序

    sort

    1
    2
    3
    lpush aa 1 aa 3 aa 2
    lrange aa 0 -1 // ==> "2" "3" "1"
    sort aa // ==> "1" "2" "3" (不改变元数据)

key扩展操作(时效性控制)
  • 指定key设置有效期


    expire key seconds

    1
    expire user 3

    pexpire key milliseconds(毫秒)

    1
    pexpire user 3000

    expireat key timestamp(时间戳)

    1
      

    pexpireat key milliseconds-timestamp

    1
      

  • 获取key的有效时间


    ttl key

    1
    ttl user // ==> 24(秒)

    pttl key(毫秒)

    1
    pttl user // ==> 300(毫秒)

  • 切换key从时效性转换为永久性


    persist key

    1
    persist user

  • 查询key


    keys pattern

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    查询规则:
    * 匹配任意数量的任意符号
    ?匹配一个任意符号
    [] 匹配一个指定符号


    keys * 查询所有
    keys it* 查询所有以it开头
    keys *heima 查询所有以heima结尾
    keys ??heima 查询所有前面两个字符任意,后面以heima结尾
    keys user: ? 查询所有以user:开头,最后一个字符任意
    keys u[st]er:1 查询所有以u开头,以er:1结尾,中间包含一个字母,s或t

数据库通用操作

  • redis为每个服务提供有16个数据库,编号从0~15
  • 每个数据库之间的数据相互独立

数据库基本操作

  • 切换数据库


    select index

    1
    select 2 //切换到2号数据库

  • 其它操作


    quit(退出)

    1
      

    ping(测试连接)

    1
      

    echo message(打印日志)

    1
    echo asd // ==> "asd"

  • 数据移动


    move key db

    1
    2
    3
    4
    set jjw 1
    move jjw 1
    select 1
    get jjw // ==> "1" (如果要移动到的数据库有这个key,则移动失败)

  • 数据清除


    dbsize(查看数据库里的数据总量)

    1
    dbsize

    flushdb(清除当前所在数据库的所有数据)

    1
    flushdb

    flushall(清除所有数据库的所有数据)

    1
    flushall



Jedis

Java语言连接redis服务:Jedis、SpringData Redis、Lettuce


1、创建maven工程

2、pom.xml文件中引入Jedis所需jar

1
2
3
4
5
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.8.0</version>
</dependency>

3、使用 jedis

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import redis.clients.jedis.Jedis;

public class JedisTest {
public void jedisTest(){
//1、连接redis
//参数一:主机
//参数二:端口号
Jedis jedis = new Jedis("127.0.0.1",6379);
//2、操作redis
jedis.set("name","jjw");
//3、关闭redis
jedis.close();
}
}

Jedis工具类制作

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
package util;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

import java.util.ResourceBundle;

public class JedisUtil {
//主机号
private static String HOST;
//端口号
private static int PORT;
//最大连接数
private static int MAXTOTAL;
//最大活动数
private static int MAXIDLE;
//Jedis连接池
private static JedisPool jedisPool = null;

static{
//读取redis.properties文件
ResourceBundle resourceBundle = ResourceBundle.getBundle("redis");

//Jedis连接池配置
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();

HOST = resourceBundle.getString("redis.host");
PORT = Integer.parseInt(resourceBundle.getString("redis.port"));
MAXTOTAL = Integer.parseInt(resourceBundle.getString("redis.maxTotal"));
MAXIDLE = Integer.parseInt(resourceBundle.getString("redis.maxIdle"));
jedisPoolConfig.setMaxTotal(MAXTOTAL);
jedisPoolConfig.setMaxIdle(MAXIDLE);

jedisPool = new JedisPool(jedisPoolConfig,HOST,PORT);
}

public static Jedis getJedis(){
return jedisPool.getResource();
}
}



Linux下使用Redis

linux下操作mysql:https://blog.csdn.net/longgeaisisi/article/details/78669007

linux下操作redis:https://blog.csdn.net/qq_43543789/article/details/104261786


安装

image-20220301080548399


开启服务

image-20220303210504336


暂停服务

image-20220303210538687


查看状态

image-20220303210619820


打开客户端

image-20220303211004498

image-20220303214939586


修改配置文件

image-20220303211614970

基础配置

image-20220303212245415

image-20220303212515654

image-20220303212617045




持久化

持久化就是利用永久性存储介质将数据进行保存,在特定的时间将保存的数据进行恢复的工作机制称为持久化,为了防止数据的意外丢失,确保数据安全


RDB(快照)启动方式

描述:将当前数据进行保存,快照形式,存储数据结果,二进制存储

  • 在配置文件中修改文件名

    1
    dbfilename dump.rdb		通常设为 dump-端口号.rdb
  • 配置文件中修改rdb文件存放路径

    1
    dir /var/lib/redis		通常放到 data 文件夹下
  • 配置文件中开启压缩、开启加载检测

    1
    2
    rdbcompression yes		设置存储至本地数据库时是否压缩数据
    rdbchecksum yes 设置是否进行RDB文件格式校验

1
save // 持久化

sava指令的执行会阻塞当前Redis服务器,有可能会造成阻塞时间过长,线上环境不建议使用

是否有方法解决?

后台执行

  • 操作者发起指令;redis服务器在合理的时间控制指令执行

image-20220304200858981

1
bgsave // 不是立即执行的

忘记执行持久化操作了怎么办?

自动执行

  • redis服务器在满足条件的时候发起指令
  • 在conf配置文件中配置

image-20220304202142201

1
save 100 10 // 100秒内 key发生了10次变化就执行save指令

注:

  • save配置要根据实际业务情况进行设置,频率过高或过低都会出现性能问题
  • save配置对于second和changes设置通常具有互补对应关系
  • save配置启动后执行的是bgsave操作

image-20220304202646522


RDB特殊启动形式
  • 全量复制

  • 服务器运行过程中重启

    1
    debug reload
  • 关闭服务器是指定保存数据

    1
    shutdown save

RDB优点

  • RDB是一个紧凑压缩的二进制文件,存储效率较高

  • RDB内部存储的是redis在某个时间点的数据快照,非常适合用于数据备份,全量复制等场景

  • RDB恢复数据的速度要比AOF快很多

  • 应用:服务器中每X小时执行bgsave备份,并将RDB文件拷贝到远程机器中,用于灾难恢复。


RDB缺点

  • RDB方式无论是执行指令还是利用配置,无法做到实时持久化,具有较大的可能性丢失数据
  • bgsave指令每次运行要执行fork操作创建子进程,要牺牲掉一些性能
  • Redis的众多版本中未进行RDB文件格式的版本统一,有可能出现各版本服务之间数据格式无法兼容现象

AOF(日志)启动形式

描述:将数据的操作过程进行保存,日志形式,存储操作过程,存储格式复杂

以独立日志的方式记录每次写命令,重启时再重新执行AOF文件中命令达到恢复数据的目的,可以简单描述为改记录数据为记录数据产生的过程

AOF的主要作用时解决了数据持久化的实时性,目前已经是Redis持久化的主流方式


image-20220304203908830


AOF写数据的三种策略

  • always(每次)

    每次写入操作均同步到AOF文件中,数据零误差,性能较低

  • everysec(每秒)

    每秒将缓冲区中的指令同步到AOF文件中,数据准确性较高,性能较高,建议使用

  • no(系统控制)

    由操作系统控制每次同步周期,整体过程不可控


在配置文件中开启

1
appendonly yes|no // 开启后尽管RDB文件有配置,但优先使用AOF文件

配置选择哪一种策略

1
appendfsync always|everysec|no

配置文件名

1
appendfilename filename // 建议配置为appendonly-端口号.aof

配置存储路径

1
dir

AOF文件重写

参考:https://blog.csdn.net/hezhiqiang1314/article/details/69396887

由于不断的对redis进行操作会使aof文件越来越大,因此aof提供了如下几种重写规则

image-20220304210222010


手动重写

image-20220304210733852

1
bgrewriteaof // 后台重写aof

自动重写

配置文件中修改配置

1
2
auto-aof-rewrite-min-size size // 触发条件是当前aof文件大小大于设置的最小值
auto-aof-rewrite-percentage percentage // (当前大小-基础大小)/基础大小 > 设定百分比

重写流程

image-20220304212835111


image-20220304212737253


RDB 和 AOF 的区别

image-20220304213913840

选择:

image-20220305093210383




Redis事务

Redis事务就是一个命令执行的队列,将一系列预定义命令包装成一个整体(一个队列)。当执行时,一次性按照添加顺序依次执行,中间不会被打断


事务基本操作


开启事务,设定事务的开启位置,此指令执行后,后续的所有指令均加入到事务中

1
multi

执行事务,设定事务的结束位置,同时执行事务

1
exec

取消事务,终止当前事务的定义,multi之后,exec之前

1
discard

注:加入事务的命令暂时进入到任务队列中,并没有立即执行,只有执行exec命令才开始执行


事务的工作流程

image-20220305101520726


事务的注意事项


定义事务的过程中,命令格式输入错误怎么办?

  • 语法错误:命令书写格式有误

  • 处理结果:如果定义的事务中所包含的命令存在语法错误,整体的事务中所有命令都不执行

  • 运行错误:命令格式正确,但无法正确的执行。如对list进行incr操作

  • 处理结果:能够正确执行的命令将会执行,运行错误的命令将不会执行

  • 需要自己手动回滚


监视锁

必须在事务开启前执行

对key添加监视锁,在执行exec前如果key发生了变化,终止事务执行

1
watch key1 ...

取消对所有key的监视

1
unwatch

客户端1 客户端2
image-20220305104446896 image-20220305104510188

分布式锁

参考:https://blog.csdn.net/weixin_39764379/article/details/111006501

使用setnx设置一个公共锁,利用setnx命令达到返回值特征,有值返回设置失败,无值返回设置成功

1
setnx lock-key value

操作完毕通过del删除锁

1
del lock-key
image-20220306105031716

分布式锁的死锁解决方案

业务分析

  • 锁由用户控制加锁,若用户在加锁之后停电了,宕机了锁就解锁不了,存在风险
  • 需要解锁操作不仅由用户控制,也能由系统控制

使用expire为锁添加时间限定,到时不释放锁,放弃锁

1
2
expire lock-key second
pexpire lock-key milliseconds // 毫秒级

客户端一:image-20220306110220224


客户端二:image-20220306110331241




删除策略

数据会存放在expires,redis中有16个expires,以hash的类型存储

过期数据

过期的数据不会马上删除,因为其他的指令更需要被执行,先执行其他的指令


过期数据数据删除策略

  1. 定时删除

    创建一个定时器,当key设置有过期时间,且过期时间到达时,由定时器任务立即执行对键的删除操作

    优点:节约内存,到时就删,快速释放

    缺点:CPU压力大,无论CPU此时负载量多高,均占用CPU,会影响redis服务器响应时间和指令吞吐量


  2. 惰性删除

    数据到达过期时间,不做处理,等下次访问该数据时发现过期了就删除,返回不存在

    优点:节约CPU性能,发现必须删除时才删除

    缺点:内存压力大,出现长期占用内存的数据


  3. 定期删除

    Redis启动服务初始化时,读取配置server.hz的值,默认为10;每秒钟执行server.hz次serverCron()

    image-20220307211711276


逐出算法

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

Redis使用内存存储数据,在执行每一个命令前,会调用freeMemorylfNeeded()检测内存是否充足。如果内存不满足新加入数据的最低存储要求,redis要临时删除一些数据为当前指令清理存储空间。清理数据的策略称为逐出算法。

注意:逐出数据的过程不是100%能够清理出足够的可使用的内存空间,如果不成功则反复执行。当对所有数据尝试完毕后,如果不能达到内存清理的要求,将出现错误信息。


配置文件中配置最大可用内存,占用物理内存的比例,默认值为0,表示不限制

1
maxmemory

配置文件中配置每次选取待删除数据的个数,选取数据时并不会全库扫描,采用随机获取数据的方式作为待检测删除数据

1
maxmemory-samples

配置文件中配置数据逐出策略

1
2
3
4
5
6
7
8
9
10
11
12
13
14
maxmemory-policy

参数:
检测易失数据(可能会过期的数据集server.db[i].expires )
volatile-lru:挑选最近最少使用的数据淘汰
volatile-Ifu:挑选最近使用次数最少的数据淘汰
volatile-ttl:挑选将要过期的数据淘汰
volatile-random:任意选择数据淘汰
检测全库数据(所有数据集server.db[i].dict )
allkeys-Iru:挑选最近最少使用的数据淘汰
allkeys-lfu:挑选最近使用次数最少的数据淘汰
allkeys-random:任意选择数据淘汰
放弃数据驱逐
no-enviction(驱逐)︰禁止驱逐数据(redis4.0中默认策略),会引发错误OOM(OutOf Memory)



服务器基础配置

设置服务器以守护进程的方式运行

1
daemonize yes|no

绑定主机地址

1
bind ...

设置服务器端口号

1
port 6379

设置数据库数量

1
databases 16

日志记录文件名

1
logfile 端口号.log

设置服务器以指定日志记录级别

1
loglevel debug|verbose|notice|warning

设置同一时间最大客户端连接数,默认无限制。当客户端连接达到上限,Redis会关闭新的连接

1
maxclients 0

客户端闲置等待最大时长,达到最大值后关闭连接,如需关闭该功能,设置为0

1
timeout 300

导入并加载指定配置文件信息,用于快速创建redis公共配置较多的redis实例配置文件,便于维护

1
include /path/server-端口号.conf



高级数据类型


Bitmaps

获取指定key对应偏移量上的bit值

gerbit key offset

1
gerbit bit 0 1

设置指定key对应偏移量上的bit值,value只能是1或0

setbit key offset value

1
2
3
4
setbit bit 0 1
setbit bit 3 1
setbit bit 4 1
// ==> 1101

Bitmaps扩展操作


计算bitmaps中有多少个1

bitcount key [start end]

1
bitcount bit(1101) // ==> 3 

对指定key按位进行交、并、非、异或操作,并将结果保存到destKey中

bitop op destKey key1 ….

1
bitop and destKey aa bb

HyperLogLog

基数统计;基数是数据集去重后的元素个数

HyperLogLog是用来做基数统计的,运用了LogLog的算法


HyperLogLog类型的基本操作

添加数据

1
pfadd key element

统计数据

1
pfcount key

合并数据

1
pfmerge destkey sourcekey

image-20220308100500397


GEO

GEO类型的基本操作

添加坐标点

geoadd key longitude latitude member

1
geoadd geos 1 1 a

获取坐标点

geopos key member

1
geopos geos a

计算坐标点距离

geodist key member1 member2

1
geodist geos a b

根据坐标求范围内的数据

georadius key longitude latitude redius m|km|ft|mi

1
georadius geos 1.5 1.5 90 km

根据点求范围内数据

georadiusbymember key member redius m|km|ft|mi

1
georadiusbymember geos a 180 km

获取指定点对应的坐标hash值

geohash key member …

1




Redis集群


主从复制(需要再理解一遍)

简介

image-20220309145409667

image-20220309145545884


作用

  • 读写分离:master写、slave读,提高服务器的读写负载能力
  • 负载均衡:基于主从结构,配合读写分离,由slave分担master负载,并根据需求的变化,改变slave的数量,通过多个节点分担数据读取负载,大大提高redis服务器并发量和吞吐量
  • 故障恢复:当master出现问题时,由slave提供服务,实现快速的故障恢复
  • 数据冗余:实现数据热备份,是持久化之外的一种数据冗余方式

工作流程

image-20220309150505312


大致可以分为三个阶段


  • 建立连接(slave连接master)

步骤:

1、设置mater的地址和端口,保存master信息

2、建立socket连接

3、发送ping命令

4、身份验证

5、发送slave端口信息

6、连接成功


方式一:客户端发送命令

slaveof masterip masterport

1
2
//开启两个服务器 一个端口号6380(slave) 一个端口号6379(master)
slaveof 127.0.0.1 6379

方式二:以配置文件启动时连接

redis-server …./redis-6380.conf –slaveof masterip masterport

1
redis-server ..../redis-6380.conf --slaveof 127.0.0.1 6379

方式三:改变配置文件连接

在配置文件中添加 slaveof masterip masterport

1
slaveof 127.0.0.1 6379

断开连接

1
slaveof no one

  • 数据同步

在slave初次连接master后,复制master中的所有数据到slave

将slave的数据库状态更新成master当前的数据库状态


步骤:

1、请求同步数据

2、 创建RDB同步数据

3、恢复RDB同步数据

4、请求部分同步数据

5、恢复部分同步数据

6、同步工作完成


image-20220309164159534


  • 命令传播




哨兵模式

简介

哨兵(sentinel)是一个分布式系统,用于对主从结构中的每台服务器进行监控,当出现故障时通过投票机制选择新的master并将所有slave连接到新的master


作用

  • 监控
    不断的检查master和slave是否正常运行。master存活检测、master与slave运行情况检测
  • 通知(提醒)
    当被监控的服务器出现问题时,向其他(哨兵间,客户端)发送通知。
  • 自动故障转移
    断开master与slave连接,选取一个slave作为master,将其他slave连接到新的master,并告知客户端新的服务器地址

启用哨兵模式

注:需要先创建一个sentinel.conf的文件,默认是没有的

配置:

  1. port :当前 Sentinel服务运行的端口

  2. dir : Sentinel服务运行时使用的临时文件夹

  3. sentinel monitor master001 192.168.110.101 6379 2 : Sentinel去监视一个名为 master001 的主redis实例,这个主实例的IP地址为本机地址 192.168 . 110.101 ,端口号为 6379 ,而将这个主实例判断为失效至少需要 2 个 Sentinel进程的同意,只要同意Sentinel的数量不达标,自动failover就不会执行

  4. sentinel down-after-milliseconds master001 30000 : 指定了Sentinel认为Redis实例已经失效所需的毫秒数。 当实例超过该时间没有返回PING,或者直接返回错误,那么Sentinel将这个实例标记为主观下线。只有一个 Sentinel进程将实例标记为主观下线并不一定会引起实例的自动故障迁移:只有在足够数量的Sentinel都将一个实例标记为主观下线之后,实例才会被标记为客观下线,这时自动故障迁移才会执行

  5. sentinel parallel-syncs master001 1 :指定了在执行故障转移时,最多可以有多少个从Redis实例在同步新的主实例,在从Redis实例较多的情况下这个数字越小,同步的时间越长,完成故障转移所需的时间就越长

  6. sentinel failover-timeout master001 180000 :如果在该时间(ms)内未能完成failover操作,则认为该failover失败

  7. sentinel notification-script : 指定sentinel检测到该监控的redis实例指向的实例异常时,调用的报警脚本。该配置项可选,但是很常用

启动哨兵

1
2
//启动之前需要先启动主机和从机,先搭建好主从模式
redis-sentinel sentinel-端口号.conf

工作原理

哨兵在进行主从切换的过程中经历了三个阶段

  • 监控

    同步各个节点的状态信息

    • 获取各个sentinel的状态
    • 获取master的状态和各个slave的详细信息

image-20220310164857301


  • 通知

image-20220310165533115


  • 故障转移
    • 发现问题
    • 竞选负责人
    • 优选新master
    • 新master上任,其他slave切换master,原master作为slave故障恢复后连接

image-20220311144112988




集群

简介:集群就是使用网络将若干台计算机连通起来,并提供统一的管理方式,使其对外呈现单机的服务效果


作用

  • 分散单台服务器的访问压力,实现负载均衡
  • 分散单台服务器的存储压力,实现可扩展性
  • 降低单台服务器宕机带来的业务灾难

集群结构设计

数据存储设计

  • 通过算法设计,计算出key应该保存的位置
  • 将所有的存储空间计划切割成16384份,每台主机保存一部分
  • 每份代表的是一个存储空间,不是一个key的保存空间
  • 将key按照计算出来的结果放到对应的存储空间

image-20220311145956927


集群内部通讯设计

  • 各个数据库相互通信,保存各个库中槽的编号数据
  • 一次命中,直接返回
  • 一次未命中,告知具体位置

image-20220311150824956


集群搭建

参考:https://blog.csdn.net/qq_42815754/article/details/82912130

配置文件中添加配置,表示这个为一个cluster节点

1
cluster-enabled yes 

配置文件中添加配置,集群配置文件

1
cluster-config-file nodes-6379.conf

配置文件中添加配置,表示超时时间

1
cluster-node-timeout 10000

image-20220311155652638


Cluster节点指令

查看集群节点信息

1
cluster nodes

进入一个从节点redis,切换其主节点

1
cluster replicate <master-id>

发现一个新节点,新增主节点

1
cluster meet ip:port

忽略一个没有solt的节点

1
cluster forget <id>

手动故障转移

1
cluster failover



企业级解决方案

缓存预热

原因:

1、请求数量较高

2、主从之间数据吞吐量较大,数据同步操作频度较高


解决方案:

image-20220311163929680


缓存雪崩

原因:

1、在一个较短的时间内,缓存中较多的key集中过期

2、此周期内请求访问过期的数据,redis未命中,redis向数据库获取数据

3、数据库同时接收到大量的请求无法及时处理

4、Redis大量请求被积压,开始出现超时现象

5、数据库流量激增,数据库崩溃

6、重启后仍然面对缓存中无数据可用

7、Redis服务器资源被严重占用,Redis服务器崩溃

8、Redis集群呈现崩塌,集群瓦解

9、应用服务器无法及时得到数据响应请求,来自客户端的请求数量越来越多,应用服务器崩溃

10、应用服务器,redis,数据库全部重启,效果不理想


解决方案:

image-20220311164524794

image-20220311164709916


缓存击穿

原因:

1、Redis中某个key过期,该key访问量巨大

2、多个数据请求从服务器直接压到Redis后,均未命中

3、Redis在短时间内发起了大量对数据库中同一数据的访问


解决方案:

image-20220311165632643


缓存穿透

原因:

1、Redis中大面积出现未命中

2、出现非正常URL访问


解决方案:

image-20220311170256349