当前位置:   article > 正文

Redis详细使用(含高级)_redis使用

redis使用

Redis

1、NoSQL

简介

  • 是什么
    • NoSQL(NoSQL = Not Only SQL ),意即“不仅仅是SQL”
    • 非关系型的数据库
    • NoSQL数据库的产生就是为了解决大规模数据集合多重数据种类带来的挑战,尤其是大数据应用难题,包括超大规模数据的存储
  • 你干嘛
    • 易扩展
      • NoSQL数据库种类繁多,但是一个共同的特点都是去掉关系数据库的关系型特性。
      • 数据之间无关系,这样就非常容易扩展。也无形之间,在架构的层面上带来了可扩展的能力
    • 大数据量高性能
      • NoSQL数据库都具有非常高的读写性能,尤其在大数据量下,同样表现优秀。
      • 一般MySQL使用Query Cache,每次表的更新Cache就失效,是一种大粒度的Cache,在针对web2.0的交互频繁的应用,Cache性能不高。而NoSQL的Cache是记录级的,是一种细粒度的Cache,所以NoSQL在这个层面上来说就要性能高很多了
    • 多样灵活的数据模型
      • NoSQL无需事先为要存储的数据建立字段,随时可以存储自定义的数据格式。而在关系数据库里,增删字段是一件非常麻烦的事情。如果是非常大数据量的表,增加字段简直就是一个噩梦



NoSQL数据库的四大分类

  • KV键值

  • 文档型数据库(bson格式比较多)

  • 列存储数据库

  • 图关系数据库

    在这里插入图片描述



分布式数据库中CAP原理

  • C:Consistency(强一致性)
  • A:Availability(可用性)
  • P:Partition tolerance(分区容错性)

  • CAP理论就是说在分布式存储系统中,最多只能实现上面的两点。由于当前的网络硬件肯定会出现延迟丢包等问题,所以分区容忍性是我们必须需要实现的。所以我们只能在一致性和可用性之间进行权衡,没有NoSQL系统能同时保证这三点。
  • CA :传统Oracle数据库
    • 单点集群,满足一致性,可用性的系统,通常在可扩展性上不太强大
  • AP :大多数网站架构的选择
    • 满足可用性,分区容忍性的系统,通常可能对一致性要求低一些
  • CP :Redis、Mongodb
    • 满足一致性,分区容忍必的系统,通常性能不是特别高

  • 注意:分布式架构的时候必须做出取舍。
  • 一致性和可用性之间取一个平衡。多余大多数web应用,其实并不需要强一致性。
  • 因此牺牲C换取P,这是目前分布式数据库产品的方向




2、Redis安装启动

简介

官网:https://redis.io/

中文网:http://www.redis.cn/

  • 是什么

    • Redis:REmote DIctionary Server(远程字典服务器)
    • 是完全开源免费的,用C语言编写的,遵守BSD协议,是一个高性能的(key/value)分布式内存数据库,基于内存运行并支持持久化的NoSQL数据库,是当前最热门的NoSql数据库之一,也被人们称为数据结构服务器
  • 特点

    • Redis支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用
    • Redis不仅仅支持简单的key-value类型的数据,同时还提供list,set,zset,hash等数据结构的存储
    • Redis支持数据的备份,即master-slave模式的数据备份
  • 能干吗

    • 内存存储和持久化:redis支持异步将内存中的数据写到硬盘上,同时不影响继续服务
    • 取最新N个数据的操作,如:可以将最新的10条评论的ID放在Redis的List集合里面
    • 模拟类似于HttpSession这种需要设定过期时间的功能
    • 发布、订阅消息系统
    • 定时器、计数器



安装

推荐安装地址



命令解释

  • redis-benchmark性能测试工具,可以在自己本子运行,看看自己本子性能如何

  • redis-check-aof :修复有问题的AOF文件,rdb和aof后面讲

  • redis-check-dump :修复有问题的dump.rdb文件

  • redis-cli :客户端,操作入口

  • redis-sentinel :redis集群使用

  • redis-server :Redis服务器启动命令

    在这里插入图片描述



启动

  • 服务器启动./redis-server redis.conf文件
    • 例如:./redis-server /opt/redis/redis-5.0.5/redis.conf
  • 进入到客户端操作入口(服务器启动之后才能操作)./redis-cli -p 端口号
    • 例如:./redis-cli -p 6379

在这里插入图片描述




3、数据类型

  • String(字符串)
    • string类型是Redis最基本的数据类型,一个redis中字符串value最多可以是512M
  • Hash(哈希,类似java里的Map)
    • hash是一个string类型的field和value的映射表,hash特别适合用于存储对象
  • List(列表)
    • 列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素导列表的头部(左边)或者尾部(右边)
  • Set(集合)
    • Set是string类型的无序集合。它是通过HashTable实现实现的
  • Zset(sorted set:有序集合)
    • zset 和 set 一样也是string类型元素的集合,且不允许重复的成员。不同的元素都会关联一个double类型的分数
    • redis正是通过分数来为集合中的成员进行从小到大的排序。zset的成员是唯一的,但分数(score)却可以重复



数据库之间的操作

  • 切换数据库(0 到15)Select 0-15
    • select 4:切换到数据库 4
  • 查看当前数据库的key的数量Dbsize
  • 清空当前库Flushdb
  • 通杀全部库 Flushall



Redis 键(key)

在这里插入图片描述



Redis字符串(String)

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



Redis列表(List)

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



Redis哈希(Hash)

在这里插入图片描述



Redis集合(Set)

在这里插入图片描述



Redis有序集合Zset(sorted set)

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




4、解析配置文件(redis.conf,sentinel.conf)

redis.conf 常用配置项说明

NETWORK(网络配置)

  1. 绑定的主机地址
    bind 127.0.0.1

    1. bind 127.0.0.1, 只能在本地机器连接
    2. bind 127.0.0.1 192.168.2.14, 可以通过 redis-cli -p 127.0.0.1 或 redis-cli -p 192.168.2.14 链接,就是说127.0.0.1只能是本地使用,所以bind 127.0.0.1仅支持本机,而bind 192.168.2.14 所有可以通过192.168.2.14访问到 192.168.2.14这个服务器的机器,都可以通过 redis-cli -p 192.168.2.14链接redis
    3. bind 0.0.0.0 配置成0.0.0.0 这个redis所在的服务器支持几个ip来连接,就可以通过这几个ip来连接redis(举例说明:假设A服务器 通过 ip addr 查看这个服务器的ip有三个,分别是127.0.0.1,192.168.2.14,336.334.224.1,那么就意味着你可以通过这三个ip来连接redis,你能通过哪个ip可以链接到A服务器,你就可以通过这个ip来连接redis)
    4. 如果不填 ,效果和 bind 0.0.0.0一样
  2. 保护模式,一层安全保护,以避免Redis实例在互联网上开放被访问和利用。默认保护模式是启用的yes。你应该禁用它只有当你确定你想要其他主机的客户端连接到Redis即使没有配置身份验证,也没有特定的一组接口使用bind指令显式列出

    protected-mode no

  3. 指定Redis监听端口,默认端口为6379
    port 6379

  4. 当客户端闲置多长时间后关闭连接,如果指定为0,表示关闭该功能
    timeout 300

GENERAL(基本配置)

  1. Redis默认不是以守护进程的方式运行,可以通过该配置项修改,使用yes启用守护进程(推荐使用yes)
    daemonize yes
  2. 当Redis以守护进程方式运行时,Redis默认会把pid写入/var/run/redis_6379.pid文件,可以通过pidfile指定
    pidfile /var/run/redis_6379.pid
  3. 指定日志记录级别,Redis总共支持四个级别:debug、verbose、notice、warning,默认为notice
    loglevel notice
  4. 日志记录方式,默认为标准输出,如果配置Redis为守护进程方式运行,而这里又配置为日志记录方式为标准输出,则日志将会发送给/dev/null
    logfile stdout
  5. 设置数据库的数量,默认数据库为0,可以使用SELECT <dbid>命令在连接上指定数据库id
    databases 16

SNAPSHOTTING(快照配置)

  1. 指定在多长时间内,有多少次更新操作,就将数据同步到数据文件,可以多个条件配合
    Redis默认配置文件中提供了三个条件:
    save 900 1
    save 300 10
    save 60 10000
    分别表示900秒(15分钟)内有1个更改,300秒(5分钟)内有10个更改以及60秒内有10000个更改。

    save <seconds> <changes>

  2. 指定存储至本地数据库时是否压缩数据,默认为yes,Redis采用LZF压缩,如果为了节省CPU时间,可以关闭该选项,但会导致数据库文件变的巨大
    rdbcompression yes

  3. 指定本地数据库文件名,默认值为dump.rdb
    dbfilename dump.rdb

  4. 指定本地数据库存放目录
    dir ./

REPLICATION(复制配置)

  1. 设置当本机为replicaof服务(从机slave)时,设置master服务的IP地址及端口,在Redis启动时,它会自动从master进行数据同步
    replicaof <masterip> <masterport>

    slaveof <masterip> <masterport>
  2. 当master服务设置了密码保护时,slav服务连接master的密码
    masterauth <master-password>

SECURITY(安全配置)

  1. 设置Redis连接密码,如果配置了连接密码,客户端在连接Redis时需要通过AUTH 命令提供密码,默认关闭
    requirepass foobared

CLIENTS(客户端配置)

  1. 设置同一时间最大客户端连接数,默认无限制,Redis可以同时打开的客户端连接数为Redis进程可以打开的最大文件描述符数,如果设置 maxclients 0,表示不作限制。当客户端连接数到达限制时,Redis会关闭新的连接并向客户端返回max number of clients reached错误信息
    maxclients 128

MEMORY MANAGEMENT(内存管理配置)

  1. 指定Redis最大内存限制,Redis在启动时会把数据加载到内存中,达到最大内存后,Redis会先尝试清除已到期或即将到期的Key,当此方法处理 后,仍然到达最大内存设置,将无法再进行写入操作,但仍然可以进行读取操作。Redis新的vm机制,会把Key存放内存,Value会存放在swap区
    maxmemory <bytes>

  2. 删除策略,达到最大内存后的,对被挑选出来的数据进行删除的策略。

    volatile-lru -> 在具有过期集的键中挑选最近最少使用的数据淘汰(推荐使用)
    allkeys-lru -> 在所有数据集中挑选最近最少使用的数据淘汰
    volatile-lfu -> 在具有过期集的键中挑选最近使用次数最少的数据淘汰
    allkeys-lfu -> 在所有数据集中挑选最近使用次数最少的数据淘汰
    volatile-random -> 在具有过期集的键中挑选任意选择数据淘汰
    allkeys-random -> 在所有数据集中挑选任意选择数据淘汰
    volatile-ttl -> 挑选将要过期的数据淘汰
    noeviction -> 禁止驱逐数据(redis4.0中默认策略), 在写操作时返回一个错误OOM (OutOf Memory)

    maxmemory-policy volatile-lru

  3. 每次选取待删除数据的个数。默认是5产生足够好的结果。10非常接近正确的LRU,但花费更多的CPU。3比较快,但是不太准确

    maxmemory-samples 5

APPEND ONLY MODE(AOF文件配置)

  1. 指定是否在每次更新操作后进行日志记录,Redis在默认情况下是异步的把数据写入磁盘,如果不开启,可能会在断电时导致一段时间内的数据丢失。因为 redis本身同步数据文件是按上面save条件来同步的,所以有的数据会在一段时间内只存在于内存中。默认为no
    appendonly no

  2. 指定更新日志文件名,默认为appendonly.aof
    appendfilename "appendonly.aof"

  3. 指定更新日志条件,共有3个可选值:
    no:表示等操作系统进行数据缓存同步到磁盘(快)
    always:表示每次更新操作后手动调用fsync()将数据写到磁盘(慢,安全)
    everysec:表示每秒同步一次(折衷,默认值)
    appendfsync everysec

  4. 重写时是否可以运用Appendfsync(配合aof使用),用默认no即可,保证数据安全性

    no-appendfsync-on-rewrite no

  5. 设置重写的基准值(配合aof使用),默认:AOF文件大小是上次rewrite后大小的一倍且文件大于64M时重写
    auto-aof-rewrite-percentage 100
    auto-aof-rewrite-min-size 64mb

REDIS CLUSTER(集群配置)

  1. 集群节点。默认未开启 no,开启:yes

    cluster-enabled yes

  2. 集群配置文件。这个文件不是需要手工编辑。它由Redis节点创建和更新。每个Redis集群节点需要不同的集群配置文件。确保在同一系统中运行的实例不存在重叠集群配置文件名。

    cluster-config-file nodes-6379.conf

  3. 群集节点超时时间,默认是15000(15秒)

    cluster-node-timeout 15000



sentinel.conf 常用配置项说明

  1. 端口
    port 26379

  2. 工作路径,sentinel一般指定/tmp比较简单

    dir /tmp

  3. 哨兵监控这个master,在至少quorum个哨兵实例都认为master down后把master标记为odown。slaves是自动发现,所以你没必要明确指定slaves。

    sentinel monitor <master-name> <ip> <redis-port> <quorum>

    sentinel monitor mymaster 127.0.0.1 6379 2

  4. 设置master和slaves验证密码

    sentinel auth-pass mymaster MySUPER--secret-0123passw0rd

    sentinel auth-pass mymaster 123456

  5. master或slave多长时间(默认30秒)不能使用后标记为sdown状态

    sentinel down-after-milliseconds mymaster 30000

  6. 如果master重新选出来后,其它slave节点能同时并行从新master同步数据的台数有多少个,显然该值越大,所有slave节点完成同步切换的整体速度越快,但如果此时正好有人在访问这些slave,可能造成读取失败,影响面会更广。最保守的设置为1,同一时间,只能有一台干这件事,这样其它slave还能继续服务,但是所有slave全部完成缓存更新同步的进程将变慢

    sentinel parallel-syncs mymaster 1

  7. 该参数指定一个时间段,在该时间段内没有实现故障转移成功,则会再一次发起故障转移的操作,单位毫秒

    sentinel failover-timeout mymaster 180000

  8. 不允许使用SENTINEL SET设置notification-script和client-reconfig-script

    sentinel deny-scripts-reconfig yes




5、持久化

RDB(Redis DataBase)

简介

  • RDB 保存的是 .rdb 文件
  • 在指定的时间间隔内将内存中的数据集快照写入磁盘,恢复时是将快照文件直接读到内存里
  • Redis会单独创建(fork)一个子进程来进行持久化,会先将数据写入到一个临时文件中,待持久化过程都结束了,再用这个临时文件替换上次持久化好的文件
  • 优势
    • 适合大规模的数据恢复
    • 对数据完整性和一致性要求不高
  • 劣势
    • 在一定间隔时间做一次备份,所以如果redis意外down掉的话,就会丢失最后一次快照后的所有修改
    • Fork的时候,内存中的数据被克隆了一份,大致2倍的膨胀性需要考虑

在这里插入图片描述



配置(redis.conf)

#配置复合的快照触发条件,默认是:1分钟内改了1万次,或5分钟内改了10次,或15分钟内改了1次。
save 900 1
save 300 10
save 60 10000
#存储到磁盘中的快照,是否进行压缩存储。如果是的话,采用LZF算法进行压缩。如果不想消耗CPU来压缩,可以设置为关闭
rdbcompression yes
#在存储快照后,可以使用CRC64算法进行数据校验,这样会增加约10%的性能消耗,如果希望获取到最大的性能提升,可以关闭
rdbchecksum yes
#指定本地数据库文件名,默认值为 dump.rdb
dbfilename dump.rdb
#指定本地数据库存放目录,默认为 ./
dir ./
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12



使用

触发RDB快照
  • save命令
    • 在命令行中执行 save
    • save时只管保存,其它不管,全部阻塞
  • bgsave命令
    • 在命令行中执行 bgsave
    • 在后台异步进行快照操作,快照同时还可以响应客户端请求。可以通过lastsave命令获取最后一次成功执行快照的时间



恢复
  • **1、备份文件 (dump.rdb)**名称改为 redis.conf 配置文件中的 dbfilename 的名称
  • **2、**将文件移动到 redis.conf 配置文件中的 dir 目录(或在命令行中执行config get dir来获取目录)
  • 3、启动服务



修复rdb文件中的错误
  • redis-check-dump --fix 需要修复的rdb文件
    • 例如:redis-check-dump --fix dump.rdb



AOF(Append Only File)

简介

  • AOF保存的是 .aof 文件
  • 日志的形式来记录每个写操作,将Redis执行过的所有写指令记录下来(读操作不记录),只许追加文件但不可以改写文件,redis启动之初会读取该文件重新构建数据,换言之,redis重启的话就根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作
  • 优势
    • 每修改同步:appendfsync always,同步持久化,数据变更会被立即记录到磁盘,性能较差但数据完整性比较好
    • 每秒同步:appendfsync everysec 异步操作,每秒记录 如果一秒内宕机,有数据丢失
    • 不同步:appendfsync no 从不同步
    • AOF文件有序地保存了对数据库执行的所有写入操作,这些写入操作以 Redis协议的格式保存,因此AOF文件的内容非常容易被人读懂,对文件进行分析也很轻松
  • 劣势
    • 相同数据集的数据而言aof文件要远大于rdb文件,恢复速度慢于rdb
    • aof运行效率要慢于rdb,每秒同步策略效率较好,不同步效率和rdb相同

在这里插入图片描述



Rewrite

  • 重写机制
    • 当AOF文件的大小超过所设定的阈值时,Redis就会启动AOF文件的内容压缩,只保留可以恢复数据的最小指令集,可以使用命令bgrewriteaof
  • 原理
    • AOF文件持续增长而过大时,会fork出一条新进程来将文件重写(也是先写临时文件最后再rename),遍历新进程的内存中数据,每条记录有一条的Set语句。重写aof文件的操作,并没有读取旧的aof文件,而是将整个内存中的数据库内容用命令的方式重写了一个新的aof文件,这点和快照有点类似
  • 触发机制
    • Redis会记录上次重写时的AOF大小,默认配置是当AOF文件大小是上次rewrite后大小的一倍且文件大于64M时触发



配置(redis.conf)

#是否开启AOF,默认 no
appendonly yes
#指定更新日志文件名,默认为appendonly.aof
appendfilename "appendonly.aof"
#指定更新日志条件,共有3个可选值:
	# no:表示等操作系统进行数据缓存同步到磁盘(快)
	# always:表示每次更新操作后手动调用fsync()将数据写到磁盘(慢,安全)
	# everysec:表示每秒同步一次(折衷,默认值)
appendfsync everysec
#重写时是否可以运用Appendfsync,用默认no即可,保证数据安全性
no-appendfsync-on-rewrite no
#设置重写的基准值,默认:AOF文件大小是上次rewrite后大小的一倍且文件大于64M时重写
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14



使用

恢复
  • 1、将备份文件 (dump.rdb)名称改为 redis.conf 配置文件中的 appendfilename 的名称
  • 2、将文件移动到 redis.conf 配置文件中的 dir 目录(或在命令行中执行config get dir来获取目录)
  • 3、启动服务



修复aof文件中的错误
  • redis-check-aof --fix 需要恢复的aof文件名称
    • 例如:redis-check-aof --fix appendonly.aof



总结(同时开启两种持久化方式)

  • RDB持久化方式能够在指定的时间间隔能对你的数据进行快照存储
  • AOF持久化方式记录每次对服务器写的操作,当服务器重启的时候会重新执行这些命令来恢复原始的数据,AOF命令以redis协议追加保存每次写的操作到文件末尾。Redis还能对AOF文件进行后台重写,使得AOF文件的体积不至于过大

同时开启两种持久化方式

  • 当redis重启的时候会优先载入AOF文件来恢复原始的数据,因为在通常情况下AOF文件保存的数据集要比RDB文件保存的数据集要完整.
  • RDB的数据不实时,同时使用两者时服务器重启也只会找AOF文件。那要不要只使用AOF呢?
    • 建议不要,因为RDB更适合用于备份数据库(AOF在不断变化不好备份),快速重启,而且不会有AOF可能潜在的bug,留着作为一个万一的手段。

性能建议

  • 因为RDB文件只用作后备用途,建议只在Slave上持久化RDB文件,而且只要15分钟备份一次就够了,只保留save 900 1这条规则。
  • 如果开启 AOF,好处是在最恶劣情况下也只会丢失不超过两秒数据,启动脚本较简单只load自己的AOF文件就可以了。代价一是带来了持续的IO,二是AOF rewrite的最后将rewrite过程中产生的新数据写到新文件造成的阻塞几乎是不可避免的。只要硬盘许可,应该尽量减少AOF rewrite的频率,AOF重写的基础大小默认值64M太小了,可以设到5G以上。默认超过原大小100%大小时重写可以改到适当的数值。
  • 如果不开启 AOF ,仅靠Master-Slave Replication 实现高可用性也可以。能省掉一大笔IO也减少了rewrite时带来的系统波动。代价是如果Master/Slave同时倒掉,会丢失十几分钟的数据,启动脚本也要比较两个Master/Slave中的RDB文件,载入较新的那个。




6、事务

简介

  • 可以一次执行多个命令,本质是一组命令的集合。一个事务中的所有命令都会序列化,按顺序地串行化执行而不会被其它命令插入,不许加塞
  • 特性
    • 单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
    • 没有隔离级别的概念:队列中的命令没有提交之前都不会实际的被执行,因为事务提交前任何指令都不会被实际执行,也就不存在”事务内的查询要看到事务里的更新,在事务外查询不能看到”这个让人万分头痛的问题
    • 不保证原子性:redis同一个事务中如果有一条命令执行失败,其后的命令仍然会被执行,没有回滚



命令

在这里插入图片描述



使用

步骤

  • 1、开启:以MULTI开始一个事务
  • 2、入队:将多个命令入队到事务中,接到这些命令并不会立即执行,而是放到等待执行的事务队列里面
  • 3、执行:由EXEC命令触发事务



正常执行

在这里插入图片描述



放弃事务

在这里插入图片描述



全体失效

在这里插入图片描述



部分失效,冤头债主

在这里插入图片描述



watch监控

watch

  • 监控了key,如果key被修改了,后面一个事务的执行失效

    在这里插入图片描述



unwatch

  • 一旦执行了exec之前加的监控锁都会被取消掉了

    在这里插入图片描述



小结

  • Watch指令,类似乐观锁,事务提交时,如果Key的值已被别的客户端改变,比如某个list已被别的客户端push/pop过了,整个事务队列都不会被执行
  • 通过WATCH命令在事务执行之前监控了多个Keys,倘若在WATCH之后有任何Key的值发生了变化,EXEC命令执行的事务都将被放弃,同时返回Nullmulti-bulk应答以通知调用者事务执行失败




7、发布订阅

简介

  • 进程间的一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息。

    在这里插入图片描述



命令

在这里插入图片描述




8、删除策略

过期数据

  • Redis是一种内存级数据库 ,所有数据均存放在内存中,内存中的数据可以通过TTL指令获取其状态
    • XX : 具有时效性的数据
    • -1 : 永久有效的数据
    • -2 : 已经过期的数据或被删除的数据或未定义的数据



数据删除策略

  • 定时删除
    • 创建一个定时器, 当key设置有过期时间,且过期时间到达时,由定时器任务立即执行对键的删除操作
    • 优点:节约内存,到时就删除,快速释放掉不必要的内存占用
    • 缺点: CPU压力很大,无论CPU此时负载量多高,均占用CPU,会影响redis服务 器响应时间和指令吞吐量
  • 惰性删除
    • 数据到达过期时间,不做处理。等下次访问该数据时
      • 如果未过期,返回数据
      • 发现已过期,删除,返回不存在
    • 优点:节约CPU性能,发现必须删除的时候才删除
    • 缺点:内存压力很大,出现长期占用内存的数据
  • 定期删除
    • 周期性轮询redis库中的时效性数据,采用随机抽取的策略,利用过期数据占比的方式控制删除频度
    • 特点
      • CPU性能占用设置有峰值,检测频度可自定义设置
      • 内存压力不是很大,长期占用内存的冷数据会被持续清理



逐出算法

  • Redis使用内存存储数据,在执行每一个命令前,会调用freeMemorylfNeeded)检测内存是否充足。 如果内存不满足新加入数据的最低存储要求,redis要临时删除一些数据为当前指令清理存储空间。 清理数据的策略称为逐出算法。
  • 注意
    • 逐出数据的过程不是100%能够清理出足够的可使用的内存空间,如果不成功则反复执行。当对所有数据尝试完毕后,如果不能达到内存清理的要求,将出现错误信息。
  • 相关配置
    • 检测易失数据(可能会过期的数据集)
      • volatile-lru:挑选最近最少使用的数据淘汰
      • volatile-lfu:挑选最近使用次数最少的数据淘汰
      • volatile-ttl: 挑选将要过期的数据淘汰
      • volatile-random:任意选择数据淘汰
    • 检测全库数据(所有数据集 )
      • allkeys-lru:挑选最近最少使用的数据淘汰
      • allkeys- lfu:挑选最近使用次数最少的数据海汰
      • allkeys-random:任意选择数据淘汰
    • 放弃数据驱逐
      • no- enviction (驱逐) : 禁止驱逐数据(redis4.0中默认策略), 会引发错误OOM (OutOf Memory)




9、主从复制

简介

  • 主从复制
    • 主机数据更新后根据配置和策略,自动同步到备机的master/slaver机制,Master以写为主,Slave以读为主
  • 特性
    • 读写分离
    • 容灾恢复
  • 缺点
    • 由于所有的写操作都是先在Master上操作,然后同步更新到Slave上,所以从Master同步到Slave机器有一定的延迟,当系统很繁忙的时候,延迟问题会更加严重,Slave机器数量的增加也会使这个问题更加严重。



原理

建立连接 + 数据同步 + 命令传播

  • 建立连接

    在这里插入图片描述

  • 数据同步

    在这里插入图片描述

  • 数据同步 + 命令传播

    x



环境搭建

使用三台主机,一台主机两台从机

192.168.37.131 : 主机,master
192.168.37.132 : 从机,slave
192.168.37.133 : 从机,slave

配置 redis.conf(基本配置,可按需求添加或修改配置)

  • 192.168.37.131机器的 redis.conf 文件配置(不知道配置意思可以在目录的 4、解析配置文件(redis.conf) 中查看)

    # 192.168.37.131 机器
    bind 192.168.37.131
    port 6379
    daemonize yes
    pidfile "/var/run/redis_6379.pid"
    loglevel notice
    logfile "log_6379.log"
    databases 16
    save 900 1
    save 300 10
    save 60 10000
    rdbcompression yes
    rdbchecksum yes
    dbfilename "dump_6379.rdb"
    dir "./"
    appendonly yes
    appendfilename "appendonly_6379.aof"
    appendfsync everysec
    # master密码
    masterauth "123456"
    # 本机密码
    requirepass "123456"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
  • 192.168.37.132机器的 redis.conf 文件配置(不知道配置意思可以在目录的 4、解析配置文件(redis.conf) 中查看)

    # 192.168.37.132 机器
    bind 192.168.37.132
    port 6379
    daemonize yes
    pidfile "/var/run/redis_6379.pid"
    loglevel notice
    logfile "log_6379.log"
    databases 16
    save 900 1
    save 300 10
    save 60 10000
    rdbcompression yes
    rdbchecksum yes
    dbfilename "dump_6379.rdb"
    dir "./"
    appendonly yes
    appendfilename "appendonly_6379.aof"
    appendfsync everysec
    # 连接master实行主从复制
    replicaof 192.168.37.131 6379
    # master密码
    masterauth "123456"
    # 本机密码
    requirepass "123456"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
  • 192.168.37.133机器的 redis.conf 文件配置(不知道配置意思可以在目录的 4、解析配置文件(redis.conf) 中查看)

    # 192.168.37.133 机器
    bind 192.168.37.133
    port 6379
    daemonize yes
    pidfile "/var/run/redis_6379.pid"
    loglevel notice
    logfile "log_6379.log"
    databases 16
    save 900 1
    save 300 10
    save 60 10000
    rdbcompression yes
    rdbchecksum yes
    dbfilename "dump_6379.rdb"
    dir "./"
    appendonly yes
    appendfilename "appendonly_6379.aof"
    appendfsync everysec
    # 连接master实行主从复制
    replicaof 192.168.37.131 6379
    # master密码
    masterauth "123456"
    # 本机密码
    requirepass "123456"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

测试主从复制

  • 启动三台机器,先启动主机再启动从机
    • ./redis-server redis.conf配置文件
  • 分别进入到三台机器的客户端操作入口
    • ./redis-cli -p 端口号
  • 使用 info replication 命令来查看主从复制是否搭建成功
    • 当master机器上显示有两个连接,slave机器上显示连接的是master,说明搭建成功




10、哨兵模式

简介

  • 哨兵
    • 哨兵(sentinel)是一个分布式系统,用于对主从结构中的每台服务器进行监控,当出现故障时通过投票机制选择新的master并将所有slave连接到新的master
  • 哨兵模式
    • 部署多台redis,将一台作为master、其他配置成slave,数据修改时,主从同时修改;当master挂掉之后会从slave中选出一台作为master
  • 作用
    • 监控
      • 不断的检查master和slave是否正常运行。
      • master存活检测、master 与slave运行情况检测
    • 通知(提醒)
      • 当被监控的服务器出现问题时,向其他(哨兵间,客户端)发送通知。
    • 自动故障转移
      • 断开master与slave连接,选取一个slave作为master,将其他slave连接到新的master,并告知客户端新的服务器地址



环境搭建

配置sentinel.conf(基本配置,可按需求添加或修改配置)

  • 搭建主从复制环境

  • 192.168.37.131机器的 sentinel.conf 文件配置(不知道配置意思可以在目录的 4、解析配置文件(sentiment.conf) 中查看)

    port 26379
    daemonize no
    dir "/tmp"
    # 监控本机ip
    sentinel monitor mymaster 192.168.37.131 6379 2
    # master密码
    sentinel auth-pass mymaster 123456
    sentinel down-after-milliseconds mymaster 30000
    sentinel parallel-syncs mymaster 1
    sentinel failover-timeout mymaster 180000
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
  • 192.168.37.132机器的 sentinel.conf 文件配置(不知道配置意思可以在目录的 4、解析配置文件(sentiment.conf) 中查看)

    port 26379
    daemonize no
    dir "/tmp"
    # 监控本机ip
    sentinel monitor mymaster 192.168.37.132 6379 2
    # master密码
    sentinel auth-pass mymaster 123456
    sentinel down-after-milliseconds mymaster 30000
    sentinel parallel-syncs mymaster 1
    sentinel failover-timeout mymaster 180000
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
  • 192.168.37.133机器的 sentinel.conf 文件配置(不知道配置意思可以在目录的 4、解析配置文件(sentiment.conf) 中查看)

    port 26379
    daemonize no
    dir "/tmp"
    # 监控本机ip
    sentinel monitor mymaster 192.168.37.133 6379 2
    # master密码
    sentinel auth-pass mymaster 123456
    sentinel down-after-milliseconds mymaster 30000
    sentinel parallel-syncs mymaster 1
    sentinel failover-timeout mymaster 180000
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10



测试哨兵模式

  • 测试主从复制环境是否搭建成功,成功后继续
  • 启动三台机器的哨兵
    • ./redis-sentinel sentinel.conf配置文件
  • 将master主机的Redis挂掉
    • 主机挂掉一段时间后,会从slave中通过投票重新选择一个作为master,这样即为环境搭建成功




11、集群

简介

  • 集群
    • 集群就是使用网络将若干台计算机联通起来,并提供统-的管理方式,使其对外呈现单机的服务效果
  • 作用
    • 分散单台服务器的访问压力,实现负载均衡
    • 分散单台服务器的存储压力,实现可扩展性
    • 降低单台服务器宕机带来的业务灾难



环境搭建

搭建三主三从:master=7000,7001,7002 slave=7003,7004,7005

第一步: 进入一个新目录, 并创建六个以端口号为名字的子目录

mkdir cluster-test
cd cluster-test
mkdir 7000 7001 7002 7003 7004 7005
  • 1
  • 2
  • 3

第二步:将redis.conf文件复制到7000路径下

第三步:修改redis.conf文件

#本机ip
bind 127.0.0.1
#端口号
port 7000
daemonize yes
pidfile /var/run/redis_7000.pid
logfile "7000.log"
masterauth 123456
requirepass 123456
#开启集群模式
cluster-enabled yes
#配置文件
cluster-config-file nodes-7000.conf
#超时时间
cluster-node-timeout 5000
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

第四步:将redis.conf文件复制到7001、7002、7003、7004、7005目录下并修改

  • 修改:在vim下使用命令
    • :%s/7000/7001
      • 将改文件中的7000全部改成7001

第五步:依次启动7000、7001、7002、7003、7004、7005

  • 查看:ps -ef | grep -i redis

第六步:创建集群

  • 命令:

    ./redis-cli --cluster create 127.0.0.1:7000 127.0.0.1:7001 \
    127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005 \
    --cluster-replicas 1
    
    • 1
    • 2
    • 3




12、缓存雪崩、缓存击穿、缓存穿透

缓存雪崩

  • 定义
    • 缓存中如果大量缓存在一段时间内集中过期了,这时候会发生大量的缓存击穿现象,所有的请求都落在了DB上,由于查询数据量巨大,引起DB压力过大甚至导致DB宕机。
  • 解决方案
    • 给缓存的失效时间,加上一个随机值,避免集体失效。如果Redis是集群部署,将热点数据均匀分布在不同的Redis库中也能避免全部失效的问题
    • 使用互斥锁,但是该方案吞吐量明显下降了。·设置热点数据永远不过期。
    • 双缓存。我们有两个缓存,缓存A和缓存B。缓存A的失效时间为20分钟,缓存B不设失效时间。自己做缓存预热操作。

缓存击穿

  • 定义
    • 高并发的情况下,某个热门key突然过期,导致大星请求在Redis未找到缓存数据,进而全部去访问DB请求数据,引起DB压力瞬间增大。
  • 解决方案
    • Redis中的数据不设置过期时间,然后在缓存的对象上添加一个属性标识过期时间,每次获取到数据时,校验对象中的过期时间属性,如果数据即将过期,则异步发起一个线程主动更新缓存中的数据。但是这种方案可能会导致有些请求会拿到过期的值,就得看业务能否可以接受,
    • 如果要求数据必须是新数据,则最好的方案则为热点数据设置为永不过期,然后加一个互斥锁保证缓存的单线程写。

缓存穿透

  • 定义
    • 缓存穿透是指查询缓存和DB中都不存在的数据。比如通过id查询商品信息,id一般大于0,攻击者会故意传id为-1去查询,由于缓存是不命中则从DB中获取数据,这将会导致每次缓存都不命中数据导致每个请求都访问DB,造成缓存穿透。
  • 解决方案
    • 利用互斥锁,缓存失效的时候,先去获得锁,得到锁了,再去请求数据库。没得到锁,则休眠一段时间重试
    • 采用异步更新策略,无论key是否取到值,都直接返回。value值中维护一个缓存失效时间,缓存如果过期,异步起一个线程去读数据库,更新缓存。需要做缓存预热(项目启动前,先加载缓存)操作。
    • 提供一个能迅速判断请求是否有效的拦截机制,比如,利用布隆过滤器,内部维护─系列合法有效的key。迅速判断出,请求所携带的Key是否合法有效。如果不合法,则直接返回。
    • 如果从数据库查询的对象为空,也放入缓存,只是设定的缓存过期时间较短,比如设置为60秒。




13、在SpringBoot中使用Redis

Jedis

步骤(五步)

第一步:导入Jedis包

<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
</dependency>
  • 1
  • 2
  • 3
  • 4

第二步:编写配置文件(application.yml)

spring:
  redis:
    host: 192.168.37.130
    port: 6379
    password: 123456
    jedis:
      pool:
        max-idle: 6  # 最大空闲数
        max-active: 10  # 最大连接数
        min-idle: 2  #最小空闲数
    timeout: 2000  # 连接超时
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

第三步:编写配置类(JedisConfig.java)

@Configuration
@Slf4j
public class JedisConfig {
    //主机ip
    @Value("${spring.redis.host}")
    private String host;

    //端口号
    @Value("${spring.redis.port}")
    private int port;

    //redis密码
    @Value("${spring.redis.password}")
    private String password;

    //最大空闲数
    @Value("${spring.redis.jedis.pool.max-idle}")
    private int maxIdle;

    //最大连接数
    @Value("${spring.redis.jedis.pool.max-active}")
    private int maxActive;

    //最小空闲数
    @Value("${spring.redis.jedis.pool.min-idle}")
    private int minIdle;

    //超时
    @Value("${spring.redis.timeout}")
    private int timeout;

    /**
     * Jedis线程池配配置
     */
    @Bean
    public JedisPool jedisPool(){
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();  
        jedisPoolConfig.setMaxIdle(maxIdle);  //最大空闲数
        jedisPoolConfig.setMinIdle(minIdle);  //最大连接数
        jedisPoolConfig.setMaxTotal(maxActive);  //最小空闲数
        JedisPool jedisPool = new JedisPool(jedisPoolConfig, host, port, timeout, password);  //配置
        log.warn("connect success ...");
        return jedisPool;
    }
}
  • 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

第四步:编写实现类(接口和实现类)
Jedis的调用方法和Redis的命令是一样的,下面只展示string的使用方法,其他类型都是一样的

/**
 * 实现类接口
 */
public interface UserService {
    public String getString(String key);

    public void setString(String key, String value);
}



/**
 * 实现接口类
 */
@Service
@Slf4j
public class UserServiceImpl implements UserService {
    //jedis连接池
    @Resource
    private JedisPool jedisPool;

    /**
     * 获取Redis中的数据(get命令)
     * @param key  Redis中的key值
     * @return  返回数据
     */
    @Override
    public String getString(String key) {
        String val;
        //1、获取资源
        Jedis jedis = jedisPool.getResource();
        //2、判断key是否存在
        if(jedis.exists(key)){
            log.warn("存在key:" + key);
            val = jedis.get(key);
        }else{
            log.warn("不存在key:" + key);
            val = "error";
        }
        //3、关闭连接
        jedis.close();
        return val;
    }
    
    /**
     * 向Redis中写入String类型的数据(set命令)
     * @param key  Redis中的key
     * @param value  Redis中的value
     */
    @Override
    public void setString(String key, String value) {
        Jedis jedis = jedisPool.getResource();
        jedis.set(key, value);
        jedis.close();
    }
}
  • 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
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56

第五步:测试

@Resource
private UserService userService;

@Test
public void getStringTest(){
    String name = userService.getString("name");
    System.out.println(name);
}

@Test
public void setStringTest(){
    userService.setString("fff", "fff123");
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13



Redis

步骤(五步)

第一步:引入依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
</dependency>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

第二步:编写配置文件(application.yml)

spring:
  redis:
    host: 192.168.37.130
    port: 6379
    password: 123456
    lettuce:
      pool:
        max-idle: 8  # 最大空闲数
        max-active: 8  # 最大连接数
        min-idle: 0  #最小空闲数
        max-wait: 1000  # 最大阻塞等待时间
      shutdown-timeout: 100 # 连接超时
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

第三步:编写配置类

@Configuration
public class RedisConfig {
    /**
     * retemplate相关配置
     */
    @Bean
    public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, String> template = new RedisTemplate<>();
        // 配置连接工厂
        template.setConnectionFactory(factory);
        //使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值(默认使用JDK的序列化方式)
        Jackson2JsonRedisSerializer jacksonSeial = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper om = new ObjectMapper();
        // 指定要序列化的域,field,get和set,以及修饰符范围,ANY是都有包括private和public
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        // 指定序列化输入的类型,类必须是非final修饰的,final修饰的类,比如String,Integer等会跑出异常
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jacksonSeial.setObjectMapper(om);

        // 值采用json序列化
        //template.setValueSerializer(jacksonSeial);
        template.setValueSerializer(new StringRedisSerializer());
        //使用StringRedisSerializer来序列化和反序列化redis的key值
        template.setKeySerializer(new StringRedisSerializer());

        // 设置hash key 和value序列化模式
        template.setHashKeySerializer(new StringRedisSerializer());
        template.setHashValueSerializer(jacksonSeial);
        template.afterPropertiesSet();
        return template;
    }

    /**
     * 对hash类型的数据操作
     @Bean public HashOperations<String, String, Object> hashOperations(RedisTemplate<String, Object> redisTemplate) {
     return redisTemplate.opsForHash();
     }
     */

    /**
     * 对redis字符串类型数据操作
     @Bean public ValueOperations<String, Object> valueOperations(RedisTemplate<String, Object> redisTemplate) {
     return redisTemplate.opsForValue();
     }
     */

    /**
     * 对链表类型的数据操作
     @Bean public ListOperations<String, Object> listOperations(RedisTemplate<String, Object> redisTemplate) {
     return redisTemplate.opsForList();
     }
     */

    /**
     * 对无序集合类型的数据操作
     @Bean public SetOperations<String, Object> setOperations(RedisTemplate<String, Object> redisTemplate) {
     return redisTemplate.opsForSet();
     }
     */

    /**
     * 对有序集合类型的数据操作
     @Bean public ZSetOperations<String, Object> zSetOperations(RedisTemplate<String, Object> redisTemplate) {
     return redisTemplate.opsForZSet();
     }
     */
}
  • 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
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67

第四步:编写实现类

下面只展示string的使用方法

  • string类型:opsForValue()
  • hash类型:opsForHash()
  • list类型:opsForList()
  • set类型:opsForSet()
  • zset类型:opsForZSet()
@Service
public class RedisServiceImpl {

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    public String get(String key){
        Object string = redisTemplate.opsForValue().get(key);  //string类型
        return (String) string;
    }

    public void set(String key, String value){
        redisTemplate.opsForValue().set(key, value);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

第五步:测试

@Resource
private RedisServiceImpl redisService;

@Test
public void getStringTest(){
    String fzk = redisService.get("test");
    System.out.println(fzk);
}

@Test
public void setStringTest(){
    redisService.set("test", "123");
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/724811
推荐阅读
相关标签
  

闽ICP备14008679号