复习

  • 缓存
    • 作用:使用内存存储,加快查询;可能产生数据不一致;
    • 手写缓存
      • 查询缓存,若存在,直接返回缓存内容 —-> redis
      • 若不存在,查询数据库 —-> mysql
      • 将数据库返回的结果加入到缓存中 —-> redis
    • 注解 官方文档
      • @Cacheable 新增
      • @CachePut 修改
      • @CacheEvict 删除
      • @EnableCaching 启动缓存
    • yaml配置
      • spring.cache.redis.time-to-live=30M 过期时间
    • redis缓存配置支持
      • maxmemory 最大内存
      • maxmemory-policy 达到最大内存时的 内存清理策略
        • noeviction 不清理,直接报错,支持删除等操作
        • allkeys-lru 回收最近最少使用的key
        • volatile-lru 回收最近最少使用的带过期时间的key
        • allkeys-random 随机回收
        • volatile-random 随机回收带有过期时间的key
        • volatile-ttl 按照存活时间长短,优先回收存活时间较短
    • 缓存问题
      • 缓存击穿: 1个key过期,大量查询进入,造成数据库压力过大
        • 给请求加互斥锁
      • 缓存穿透:大量查询不存在的key,造成数据库压力过大
        • 缓存空值 或者 使用布隆过滤器
      • 缓存雪崩:多个key同时失效
        • 给key设置不同的过期时间

持久化

redis是内存存储,特点是快,缺点断电数据消失;redis提供持久化特性,便于数据的保存和恢复
容灾
RDB:redis-database 指定的时间间隔能对你的数据进行快照存储.
AOF:append-only-file 记录每次对服务器写的操作,当服务器重启的时候会重新执行这些命令来恢复原始的数据,AOF命令以redis协议追加保存每次写的操作到文件末尾. 支持后台重写,缩小文件大小
两种方式可以一个也不用,也可以同时启动,同时启用时优先使用aof,因为数据更为完整。
RDB:

  • 优势:
    • 文件紧凑,适于备份,便于传输及恢复
    • fork子进程进行持久化,最大redis性能
    • 大数据集恢复时,速度比aof快
  • 劣势:
    • 保存间隔较大,数据丢失量较大
    • 数据集大时,经常fork仍然存在消耗较大性能问题

AOF:

  • 优势:
    • 持久化频率达到秒级,默认情况下,最多丢失1秒数据
    • 日志文件时只追加的文件,所以持久化不会造成文件的永久损坏
    • 支持AOF重写,压缩文件大小,同时过程绝对安全
    • 以 Redis 协议格式保存,人类可读,便于解析及导出
  • 劣势
    • 文件体积大于rdb
    • 根据持久化频率设置,aof速度慢于rdb

选择:

  • 同时开启
  • 单独开启RDB
  • 不建议单独开启AOF

配置:

  1. # rdb持久化配置
  2. # save 时间(秒) 变动的key数量
  3. # 15分钟1个key
  4. save 900 1
  5. # 5分中10个key变动
  6. save 300 10
  7. # 1分中10000个key变动持久化
  8. save 60 10000
  9. stop-writes-on-bgsave-error yes
  10. rdbcompression yes
  11. rdbchecksum yes
  12. # rdb文件名
  13. dbfilename wndump.rdb
  14. # aof 默认关闭
  15. appendonly yes
  16. # aof文件名字
  17. appendfilename "wnappendonly.aof"
  18. # 保存频率
  19. # no: 不保存,最快
  20. # always: 每次写后保存,慢,最安全
  21. # everysec: 每秒,折中
  22. appendfsync everysec
序号 配置项 说明
1 daemonize no Redis 默认不是以守护进程的方式运行,可以通过该配置项修改,使用 yes 启用守护进程(Windows 不支持守护线程的配置为 no )
*2 pidfile /var/run/redis.pid 当 Redis 以守护进程方式运行时,Redis 默认会把 pid 写入 /var/run/redis.pid 文件,可以通过 pidfile 指定
*3 port 6379 指定 Redis 监听端口,默认端口为 6379,作者在自己的一篇博文中解释了为什么选用 6379 作为默认端口,因为 6379 在手机按键上 MERZ 对应的号码,而 MERZ 取自意大利歌女 Alessia Merz 的名字
*4 bind 127.0.0.1 绑定的主机地址
5 timeout 300 当客户端闲置多长时间后关闭连接,如果指定为 0,表示关闭该功能
6 loglevel notice 指定日志记录级别,Redis 总共支持四个级别:debug、verbose、notice、warning,默认为 notice
7 logfile stdout 日志记录方式,默认为标准输出,如果配置 Redis 为守护进程方式运行,而这里又配置为日志记录方式为标准输出,则日志将会发送给 /dev/null
8 databases 16 设置数据库的数量,默认数据库为0,可以使用SELECT 命令在连接上指定数据库id
*9 save <seconds> <changes>
Redis 默认配置文件中提供了三个条件:
save 900 1

save 300 10

save 60 10000
分别表示 900 秒(15 分钟)内有 1 个更改,300 秒(5 分钟)内有 10 个更改以及 60 秒内有 10000 个更改。
指定在多长时间内,有多少次更新操作,就将数据同步到数据文件,可以多个条件配合
10 rdbcompression yes 指定存储至本地数据库时是否压缩数据,默认为 yes,Redis 采用 LZF 压缩,如果为了节省 CPU 时间,可以关闭该选项,但会导致数据库文件变的巨大
11 dbfilename dump.rdb 指定本地数据库文件名,默认值为 dump.rdb
12 dir ./ 指定本地数据库存放目录
*13 slaveof <masterip> <masterport> 设置当本机为 slave 服务时,设置 master 服务的 IP 地址及端口,在 Redis 启动时,它会自动从 master 进行数据同步
14 masterauth <master-password> 当 master 服务设置了密码保护时,slav 服务连接 master 的密码
*15 requirepass foobared 设置 Redis 连接密码,如果配置了连接密码,客户端在连接 Redis 时需要通过 AUTH 命令提供密码,默认关闭
16 maxclients 128 设置同一时间最大客户端连接数,默认无限制,Redis 可以同时打开的客户端连接数为 Redis 进程可以打开的最大文件描述符数,如果设置 maxclients 0,表示不作限制。当客户端连接数到达限制时,Redis 会关闭新的连接并向客户端返回 max number of clients reached 错误信息
17 maxmemory <bytes> 指定 Redis 最大内存限制,Redis 在启动时会把数据加载到内存中,达到最大内存后,Redis 会先尝试清除已到期或即将到期的 Key,当此方法处理 后,仍然到达最大内存设置,将无法再进行写入操作,但仍然可以进行读取操作。Redis 新的 vm 机制,会把 Key 存放内存,Value 会存放在 swap 区
*18 appendonly no 指定是否在每次更新操作后进行日志记录,Redis 在默认情况下是异步的把数据写入磁盘,如果不开启,可能会在断电时导致一段时间内的数据丢失。因为 redis 本身同步数据文件是按上面 save 条件来同步的,所以有的数据会在一段时间内只存在于内存中。默认为 no
*19 appendfilename appendonly.aof 指定更新日志文件名,默认为 appendonly.aof
*20 appendfsync everysec 指定更新日志条件,共有 3 个可选值:
no
:表示等操作系统进行数据缓存同步到磁盘(快)
always
:表示每次更新操作后手动调用 fsync() 将数据写到磁盘(慢,安全)
everysec
:表示每秒同步一次(折中,默认值)
21 vm-enabled no 指定是否启用虚拟内存机制,默认值为 no,简单的介绍一下,VM 机制将数据分页存放,由 Redis 将访问量较少的页即冷数据 swap 到磁盘上,访问多的页面由磁盘自动换出到内存中(在后面的文章我会仔细分析 Redis 的 VM 机制)
22 vm-swap-file /tmp/redis.swap 虚拟内存文件路径,默认值为 /tmp/redis.swap,不可多个 Redis 实例共享
23 vm-max-memory 0 将所有大于 vm-max-memory 的数据存入虚拟内存,无论 vm-max-memory 设置多小,所有索引数据都是内存存储的(Redis 的索引数据 就是 keys),也就是说,当 vm-max-memory 设置为 0 的时候,其实是所有 value 都存在于磁盘。默认值为 0
24 vm-page-size 32 Redis swap 文件分成了很多的 page,一个对象可以保存在多个 page 上面,但一个 page 上不能被多个对象共享,vm-page-size 是要根据存储的 数据大小来设定的,作者建议如果存储很多小对象,page 大小最好设置为 32 或者 64bytes;如果存储很大大对象,则可以使用更大的 page,如果不确定,就使用默认值
25 vm-pages 134217728 设置 swap 文件中的 page 数量,由于页表(一种表示页面空闲或使用的 bitmap)是在放在内存中的,,在磁盘上每 8 个 pages 将消耗 1byte 的内存。
26 vm-max-threads 4 设置访问swap文件的线程数,最好不要超过机器的核数,如果设置为0,那么所有对swap文件的操作都是串行的,可能会造成比较长时间的延迟。默认值为4
27 glueoutputbuf yes 设置在向客户端应答时,是否把较小的包合并为一个包发送,默认为开启
28 hash-max-zipmap-entries 64 hash-max-zipmap-value 512 指定在超过一定的数量或者最大的元素超过某一临界值时,采用一种特殊的哈希算法
29 activerehashing yes 指定是否激活重置哈希,默认为开启(后面在介绍 Redis 的哈希算法时具体介绍)
30 include /path/to/local.conf 指定包含其它的配置文件,可以在同一主机上多个Redis实例之间使用同一份配置文件,而同时各个实例又拥有自己的特定配置文件

事务

命令:MULTIEXECDISCARDWATCH

  • multi 开启事务
  • exec 提交
  • discard 撤销
  • watch 监听某个key

image.png

事务出错处理

  • exec 之前,取消停止事务,不发送到服务端
  • exec 之后,出现错误,继续执行
    1. 127.0.0.1:6379> set a 10
    2. OK
    3. 127.0.0.1:6379> watch a
    4. OK
    5. 127.0.0.1:6379> multi
    6. OK
    7. 127.0.0.1:6379> set a 11
    8. QUEUED
    9. 127.0.0.1:6379> set a 12
    10. QUEUED
    11. 127.0.0.1:6379> exec
    12. (nil)
    13. 127.0.0.1:6379> get a
    14. "20"
    15. 127.0.0.1:6379> multi
    16. OK
    17. 127.0.0.1:6379> set a 11
    18. QUEUED
    19. 127.0.0.1:6379> set a 12
    20. QUEUED
    21. 127.0.0.1:6379> exec
    22. 1) OK
    23. 2) OK
    24. 127.0.0.1:6379> get a
    25. "12"
    26. 127.0.0.1:6379> multi
    27. OK
    28. 127.0.0.1:6379> set a 11
    29. QUEUED
    30. 127.0.0.1:6379> set a 20
    31. QUEUED
    32. 127.0.0.1:6379> discard
    33. OK
    34. 127.0.0.1:6379> get a
    35. "12"
    36. 127.0.0.1:6379>
    详情请见 官方文档: http://redis.cn/topics/transactions.html

    高可用及集群

    高可用 : High Avaliablity
    几个9 : 99%

编译安装5.0版本redis

  1. yum remove redis
  2. # 安装
  3. yum install -y wget
  4. # 下载
  5. yum install wget
  6. wget https://download.redis.io/releases/redis-5.0.10.tar.gz
  7. # 解压
  8. tar -C /usr/local/ -xzf redis-5.0.10.tar.gz
  9. #编译安装
  10. cd /usr/local/redis-5.0.10/
  11. yum install gcc
  12. make MALLOC=libc
  13. cd src && make install
  14. # 测试
  15. redis-server -v

高可用Sentinel

概念

image.png
搭建主机与从机的集群,主机会自动将数据同步给从机(单向),一般采用主机负责写,从机负责读
但是如果主机故障无法提供服务,则整个集群无法写入新数据,集群不可用
redis提供sentinel哨兵方案,哨兵监控主机的健康状况,检测到主机故障后将之下线,并从从机中选取新的主机,并修改其他从机配置指向新主机
http://redis.cn/topics/replication.html
image.png

示例步骤

  1. +--------------+
  2. +-+ sentinel哨兵 |
  3. | +--------------+
  4. |
  5. |
  6. +-----v-----+
  7. |6380:master+---------+
  8. +--+--------+ |
  9. | +------v------+
  10. | |6381:slave |
  11. +-----v------+ +-------------+
  12. |6382:slave |
  13. +------------+
  1. 复制三份配置文件,分别命名为redis-6380.conf redis-6381.conf redis-6382.conf
  2. 修改所有配置的端口 、pid 和bind的ip ```nginx

    Accept connections on the specified port, default is 6379 (IANA #815344).

    If port 0 is specified Redis will not listen on a TCP socket.

    port 6380

If a pid file is specified, Redis writes it where specified at startup

and removes it at exit.

#

When the server runs non daemonized, no pid file is created if none is

specified in the configuration. When the server is daemonized, the pid file

is used even if not specified, defaulting to “/var/run/redis.pid”.

#

Creating a pid file is best effort: if Redis is not able to create it

nothing bad happens, the server will start and run normally.

pidfile /var/run/redis_6380.pid

IF YOU ARE SURE YOU WANT YOUR INSTANCE TO LISTEN TO ALL THE INTERFACES

JUST COMMENT THE FOLLOWING LINE.

~~~~~~~~~~~~~~~~

bind 192.168.60.179

  1. 3. 修改salve,指向主机6380
  2. ```nginx
  3. replicaof 192.168.60.179 6380
  1. 启动6380 、6381、6382 ```bash /usr/local/redis-5.0.10/src/redis-server redis-6380.conf &

[root@localhost redisha]# redis-cli -h 192.168.60.179 -p 6380 192.168.60.179:6380> ping PONG 192.168.60.179:6380> info replication

Replication

role:master connected_slaves:0 master_replid:bdcd5e9af6d380441172189e1457ff91a906f964 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 192.168.60.179:6380>

/usr/local/redis-5.0.10/src/redis-server redis-6381.conf &

[root@localhost redisha]# redis-cli -h 192.168.60.179 -p 6380 192.168.60.179:6380> info replication

Replication

role:master connected_slaves:2 slave0:ip=192.168.60.179,port=6381,state=online,offset=42,lag=0 slave1:ip=192.168.60.179,port=6382,state=online,offset=42,lag=0 master_replid:94f2bc0458034c6d633fb5727bd4eb1b83a1d3fc master_replid2:0000000000000000000000000000000000000000 master_repl_offset:56 second_repl_offset:-1 repl_backlog_active:1 repl_backlog_size:1048576 repl_backlog_first_byte_offset:1 repl_backlog_histlen:56 192.168.60.179:6380> exit [root@localhost redisha]# redis-cli -h 192.168.60.179 -p 6381 192.168.60.179:6381> info replication

Replication

role:slave master_host:192.168.60.179 master_port:6380 master_link_status:up master_last_io_seconds_ago:6 master_sync_in_progress:0 slave_repl_offset:70 slave_priority:100 slave_read_only:1 connected_slaves:0 master_replid:94f2bc0458034c6d633fb5727bd4eb1b83a1d3fc master_replid2:0000000000000000000000000000000000000000 master_repl_offset:70 second_repl_offset:-1 repl_backlog_active:1 repl_backlog_size:1048576 repl_backlog_first_byte_offset:1 repl_backlog_histlen:70 192.168.60.179:6381>

  1. 5. 拷贝sentinel.conf,修改配置如下
  2. ```nginx
  3. sentinel monitor mymaster 192.168.60.179 6380 1
  1. 启动哨兵,/usr/local/redis-5.0.10/src/redis-sentinel sentinel.conf 不要关闭,新开一个标签
  2. kill掉6380,观察发现sentinel重新选择其他实例作为主机

    spring-boot连接sentinel

  3. 修改redis配置文件,允许外网访问,改完需要重启

    1. bind 开放哪个IP 绑定 192.168.60.179 在60网段都可以被访问
    2. protected-mode no 关闭保护模式,确保外网可以被访问
  4. 修改redis-sentinel的配置,哨兵默认端口为26379 允许外网访问,重启

    1. bind
    2. protected-mode
      1. bind 192.168.60.179
      2. protected-mode no
  5. 关闭防火墙或者开放26379

  6. spring-boot修改yaml配置

    1. spring:
    2. redis:
    3. # host: localhost
    4. # port: 6379
    5. sentinel:
    6. master: mymaster
    7. nodes: 192.168.60.179:26379

    数据扩容Cluster

    解决单机内存有限的问题,支持扩容 http://redis.cn/topics/cluster-tutorial.html
    image.png

    示例步骤

    手动方式

  7. 给出6个配置文件 redis.conf

    1. port 7000
    2. cluster-enabled yes
    3. cluster-config-file nodes.conf
    4. cluster-node-timeout 5000
    5. appendonly yes
    6. protected-mode no
    7. bind {机器IP} 不绑定时springboot访问会出错
  8. 分别放在6个文件夹下

    1. .
    2. ├── 7000
    3. └── redis.conf
    4. ├── 7001
    5. └── redis.conf
    6. ├── 7002
    7. └── redis.conf
    8. ├── 7003
    9. └── redis.conf
    10. ├── 7004
    11. └── redis.conf
    12. └── 7005
    13. └── redis.conf
  9. 启动6个实例

    1. cd 7000
    2. redis-server redis.conf &
  10. 创建集群

    1. redis-cli --cluster create 192.168.60.179:7000 192.168.60.179:7001 \
    2. 192.168.60.179:7002 192.168.60.179:7003 192.168.60.179:7004 192.168.60.179:7005 \
    3. --cluster-replicas 1
  11. 访问集群

    1. # 加-c命令访问才可以
    2. redis-cli -c -h 192.168.50.28 -p 7000

    6.关闭集群

    1. ps aux | grep redis-server |grep -v grep | awk '{print $2}' | xargs kill

    自动方式

    官方脚本: /usr/local/redis-5.0.10/utils/create-cluster/create-cluster
    修改版脚本,支持本地启动多个实例,并外网可访问,只需要修改脚本的SELF_HOST变量为自己的ip即可 ```bash

    !/bin/bash

Settings

PORT=30000 TIMEOUT=2000 NODES=6 REPLICAS=1 SELF_HOST=192.168.60.179

You may want to put the above config parameters into config.sh in order to

override the defaults without modifying this script.

if [ -a config.sh ] then source “config.sh” fi

Computed vars

ENDPORT=$((PORT+NODES))

if [ “$1” == “start” ] then while [ $((PORT < ENDPORT)) != “0” ]; do PORT=$((PORT+1)) echo “Starting $PORT” ../../src/redis-server —port $PORT —cluster-enabled yes —cluster-config-file nodes-${PORT}.conf —cluster-node-timeout $TIMEOUT —appendonly yes —appendfilename appendonly-${PORT}.aof —dbfilename dump-${PORT}.rdb —logfile ${PORT}.log —daemonize yes —protected-mode no —bind $SELF_HOST done exit 0 fi

if [ “$1” == “create” ] then HOSTS=”” while [ $((PORT < ENDPORT)) != “0” ]; do PORT=$((PORT+1)) HOSTS=”$HOSTS $SELF_HOST:$PORT” done ../../src/redis-cli —cluster create $HOSTS —cluster-replicas $REPLICAS exit 0 fi

if [ “$1” == “stop” ] then while [ $((PORT < ENDPORT)) != “0” ]; do PORT=$((PORT+1)) echo “Stopping $PORT” ../../src/redis-cli -h $SELF_HOST -p $PORT shutdown nosave done exit 0 fi

if [ “$1” == “watch” ] then PORT=$((PORT+1)) while [ 1 ]; do clear date ../../src/redis-cli -h $SELF_HOST -p $PORT cluster nodes | head -30 sleep 1 done exit 0 fi

if [ “$1” == “tail” ] then INSTANCE=$2 PORT=$((PORT+INSTANCE)) tail -f ${PORT}.log exit 0 fi

if [ “$1” == “call” ] then while [ $((PORT < ENDPORT)) != “0” ]; do PORT=$((PORT+1)) ../../src/redis-cli -h $SELF_HOST -p $PORT $2 $3 $4 $5 $6 $7 $8 $9 done exit 0 fi

if [ “$1” == “clean” ] then rm -rf .log rm -rf appendonly.aof rm -rf dump.rdb rm -rf nodes.conf exit 0 fi

if [ “$1” == “clean-logs” ] then rm -rf *.log exit 0 fi

echo “Usage: $0 [start|create|stop|watch|tail|clean]” echo “start — Launch Redis Cluster instances.” echo “create — Create a cluster using redis-cli —cluster create.” echo “stop — Stop Redis Cluster instances.” echo “watch — Show CLUSTER NODES output (first 30 lines) of first node.” echo “tail — Run tail -f of instance at base port + ID.” echo “clean — Remove all instances data, logs, configs.” echo “clean-logs — Remove just instances logs.”

  1. 使用方式
  2. ```bash
  3. # 这个目录下有同名的脚本文件,没有后缀
  4. cd /usr/local/redis-5.0.10/utils/create-cluster
  5. # 启动6个实例
  6. ./create-cluster start
  7. # 创建集群
  8. ./create-cluster create
  9. # 停止集群
  10. ./create-cluster stop
  11. # 查看集群输出信息
  12. ./create-cluster watch
  13. # 清理集群
  14. ./create-cluster clean

spring-boot链接

  1. spring:
  2. redis:
  3. cluster:
  4. nodes: 192.168.90.142:7000,192.168.90.142:7001,192.168.90.142:7002,192.168.90.142:7003,192.168.90.142:7004,192.168.90.142:7005
  5. timeout: 50000

Redis性能高的原因

Redisson