Redis
Redis

Redis

Redis 特性

速度快: 10W QPS,基于内存,C语言实现
单线程
持久化
支持多种数据结构
支持多种编程语言
功能丰富: 支持Lua脚本,发布订阅,事务,pipeline等功能
简单: 代码短小精悍(单机核心代码只有23000行左右),单线程开发容易,不依赖外部库,使用简单
主从复制
支持高可用和分布式

单线程

Redis 6.0版本前一直是单线程方式处理用户的请求

单线程为何如此快?
纯内存
非阻塞
避免线程切换和竞态消耗

注意事项:
一次只运行一条命令
避免执行长(慢)命令:keys *, flushall, flushdb, slow lua script, mutil/exec, operate big value(collection)
其实不是单线程: 早期版本是单进程单线程,3.0 版本后实际还有其它的线程, 实现特定功能,如: fysnc file descriptor,close file descri

Redis 对比 Memcached

比较类别Redismemcached
支持的数据结构哈希、列表、集合、有序集合纯kev-value
持久化支持
高可用支持redis支持集群功能,可以实现主动复制,读写分离。官方也提供了sentinel集群管理工具,能够实现主从服务监控,故障自动转移,这一切,对于客户端都是透明的,无需程序改动,也无需人工介入需要二次开发
存储value容量最大512M最大1M
内存分配临时申请空间,可能导致碎片预分配内存池的方式管理内存,能够省去内存分配时间
虚拟内存使用有自己的VM机制,理论上能够存储比物理内存更多的数据,当数据超量时,会引发swap,把冷数据刷到磁盘上所有的数据存储在物理内存里
网络模型非阻塞IO复用模型,提供一些非KV存储之外的排序,聚合功能,在执行这些功能时,复杂的CPU计算,会阻塞整个IO调度非阻塞IO复用模型
水平扩展的支持redis cluster 可以横向扩展暂无
多线程Redis6.0之前是只支持单线程Memcached支持多线程,CPU利用方面Memcache优于Redis
过期策略有专门线程,清除缓存数据懒淘汰机制:每次往缓存放入数据的时候,都会存一个时间,在读取的时候要和设置的时间做TTL比较来判断是否过期
单机QPS约10W约60W
源代码可读性代码清爽简洁可能是考虑了太多的扩展性,多系统的兼容性,代码不清爽
适用场景复杂数据结构、有持久化、高可用需求、value存储内容较大纯KV,数据量非常大,并发量非常大的业务

Redis 常见应用场景

缓存:缓存RDBMS中数据,比如网站的查询结果、商品信息、微博、新闻、消息
Session 共享:实现Web集群中的多服务器间的session共享
计数器:商品访问排行榜、浏览数、粉丝数、关注、点赞、评论等和次数相关的数值统计场景
社交:朋友圈、共同好友、可能认识他们等
地理位置: 基于地理信息系统GIS(Geographic Information System)实现摇一摇、附近的人、外卖等功能
消息队列:ELK等日志系统缓存、业务的订阅/发布系统

缓存的实现流程

数据更新操作流程:

数据读操作流程:

缓存穿透,缓存击穿和缓存雪崩

缓存穿透

缓存穿透是指缓存和数据库中都没有的数据,而用户不断发起请求,比如: 发起为id为 “-1” 的数据或id为特别大不存在的数据。这时的用户很可能是攻击者,攻击会导致数据库压力过大。
解决方法:
·接口层增加校验,如用户鉴权校验,id做基础校验,id<=0的直接拦截
·从缓存取不到的数据,在数据库中也没有取到,这时也可以将key-value对写为key-null,缓存有效时间可以设置短点,如30秒(设置太长会导致正常情况也没法使用)。这样可以防止攻击用户反复用同一个id暴力攻击

缓存击穿

缓存击穿是指缓存中没有但数据库中有的数据,比如:热点数据的缓存时间到期后,这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力
解决方法:
·设置热点数据永远不过期.

缓存雪崩

缓存雪崩是指缓存中数据大批量到过期时间,而查询数据量巨大,引起数据库压力过大甚至down机。和缓存击穿不同的是,缓存击穿指并发查同一条数据,缓存雪崩是不同数据都过期了,很多数据都查不到从而查数据库。
解决方法:
·缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生
·如果缓存数据库是分布式部署,将热点数据均匀分布在不同搞得缓存数据库中
·设置热点数据永远不过期

Redis 安装及连接

apt -y install redis
yum -y install redis

http://download.redis.io/releases/
#!/bin/bash
#
#********************************************************************
#Author:            shuhong
#QQ:                985347841
#Date:              2022-10-29
#FileName:          install_redis.sh
#URL:               hhhhh
#Description:       The test script
#Copyright (C):     2022 All rights reserved
#********************************************************************
#redis-7.0.5.tar.gz
REDIS_VERSION=redis-7.0.5
#REDIS_VERSION=redis-7.0.3
#REDIS_VERSION=redis-6.2.6
#REDIS_VERSION=redis-5.0.14
#REDIS_VERSION=redis-4.0.14
REDIS_URL=http://download.redis.io/releases
PASSWORD=123456
INSTALL_DIR=/apps/redis


CPUS=lscpu |awk '/^CPU\(s\)/{print $2}'
. /etc/os-release

color () {
    RES_COL=60
    MOVE_TO_COL="echo -en \\033[${RES_COL}G"
    SETCOLOR_SUCCESS="echo -en \\033[1;32m"
    SETCOLOR_FAILURE="echo -en \\033[1;31m"
    SETCOLOR_WARNING="echo -en \\033[1;33m"
    SETCOLOR_NORMAL="echo -en \E[0m"
    echo -n "$1" && $MOVE_TO_COL
    echo -n "["
    if [ $2 = "success" -o $2 = "0" ] ;then
        ${SETCOLOR_SUCCESS}
        echo -n $"  OK  "    
    elif [ $2 = "failure" -o $2 = "1"  ] ;then 
        ${SETCOLOR_FAILURE}
        echo -n $"FAILED"
    else
        ${SETCOLOR_WARNING}
        echo -n $"WARNING"
    fi
    ${SETCOLOR_NORMAL}
    echo -n "]"
    echo 
}


prepare(){
    if [ $ID = "centos" -o $ID = "rocky" ];then
        yum  -y install gcc make jemalloc-devel systemd-devel
    else
        apt update 
        apt -y install  gcc make libjemalloc-dev libsystemd-dev
    fi
    if [ $? -eq 0 ];then
        color "安装软件包成功"  0
    else
        color "安装软件包失败,请检查网络配置" 1
        exit
    fi
}
install() {   
    if [ ! -f ${REDIS_VERSION}.tar.gz ];then
        wget ${REDIS_URL}/${REDIS_VERSION}.tar.gz || { color "Redis 源码下载失败" 1 ; exit; }
    fi
    tar xf ${REDIS_VERSION}.tar.gz -C /usr/local/src
    cd /usr/local/src/${REDIS_VERSION}
    make -j $CUPS USE_SYSTEMD=yes PREFIX=${INSTALL_DIR} install && color "Redis 编译安装完成" 0 || { color "Redis 编译安装失败" 1 ;exit ; }

    ln -s ${INSTALL_DIR}/bin/redis-*  /usr/bin/
    
    mkdir -p ${INSTALL_DIR}/{etc,log,data,run}
  
    cp redis.conf  ${INSTALL_DIR}/etc/

    sed -i -e 's/bind 127.0.0.1/bind 0.0.0.0/'  -e "/# requirepass/a requirepass $PASSWORD"  -e "/^dir .*/c dir ${INSTALL_DIR}/data/"  -e "/logfile .*/c logfile ${INSTALL_DIR}/log/redis-6379.log"  -e  "/^pidfile .*/c  pidfile ${INSTALL_DIR}/run/redis_6379.pid" ${INSTALL_DIR}/etc/redis.conf


    if id redis &> /dev/null ;then 
         color "Redis 用户已存在" 1 
    else
         useradd -r -s /sbin/nologin redis
         color "Redis 用户创建成功" 0
    fi

    chown -R redis.redis ${INSTALL_DIR}

    cat >> /etc/sysctl.conf <<EOF
net.core.somaxconn = 1024
vm.overcommit_memory = 1
EOF
    sysctl -p 
    if [ $ID = "centos" -o $ID = "rocky" ];then
        echo 'echo never > /sys/kernel/mm/transparent_hugepage/enabled' >> /etc/rc.d/rc.local
        chmod +x /etc/rc.d/rc.local
        /etc/rc.d/rc.local 
    else 
        echo -e '#!/bin/bash\necho never > /sys/kernel/mm/transparent_hugepage/enabled' >> /etc/rc.local
        chmod +x /etc/rc.local
        /etc/rc.local
    fi


cat > /lib/systemd/system/redis.service <<EOF
[Unit]
Description=Redis persistent key-value database
After=network.target

[Service]
ExecStart=${INSTALL_DIR}/bin/redis-server ${INSTALL_DIR}/etc/redis.conf --supervised systemd
ExecStop=/bin/kill -s QUIT \$MAINPID
Type=notify
User=redis
Group=redis
RuntimeDirectory=redis
RuntimeDirectoryMode=0755
LimitNOFILE=1000000

[Install]
WantedBy=multi-user.target

EOF
     systemctl daemon-reload 
     systemctl enable --now  redis &> /dev/null 
     if [ $? -eq 0 ];then
         color "Redis 服务启动成功,Redis信息如下:"  0 
     else
         color "Redis 启动失败" 1 
         exit
     fi
     sleep 2
     redis-cli -a $PASSWORD INFO Server 2> /dev/null
}

prepare 
install 

消除启动时的三个Warning提示信息(可选)

前面直接启动Redis时有三个Waring信息,可以用下面方法消除告警,但非强制消除

1.Tcp backlogT:
WARNING: The TCP backlog setting of 511 cannot be enforced because/proc/sys/net/core/somaxconn is set to the lower value of 128.

#vim /etc/sysctl.conf
net.core.somaxconn = 1024
#sysctl -p

[root@ubuntu2004 ~]#cat /proc/sys/net/core/somaxconn
1024
#Tcp backlog 是指TCP的第三次握手服务器端收到客户端 ack确认号之后到服务器用Accept函数处理请求前的队列长度,即全连接队列


2.overcommit_memory:
WARNING overcommit_memory is set to 0! Background save may fail under low memorycondition. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this totake effect.

#vim /etc/sysctl.conf
vm.overcommit_memory = 1
#sysctl -p

[root@ubuntu2004 ~]#sysctl -a |egrep vm.overcommit_memory
vm.overcommit_memory = 1
内核参数说明:
内核参数overcommit_memory 实现内存分配策略,可选值有三个:0、1、2
0 表示内核将检查是否有足够的可用内存供应用进程使用;如果有足够的可用内存,内存申请允许;否则内存申请失败,并把错误返回给应用进程
1 表示内核允许分配所有的物理内存,而不管当前的内存状态如何
2 表示内核允许分配超过所有物理内存和交换空间总和的内存

3.transparent hugepage
WARNING you have Transparent Huge Pages (THP) support enabled in your kernel.This will create latency and memory usage issues with Redis. To fix this issuerun the command 'echo never > /sys/kernel/mm/transparent_hugepage/enabled' asroot, and add it to your /etc/rc.local in order to retain the setting after areboot. Redis must be restarted after THP is disabled.
警告:您在内核中启用了透明大页面(THP,不同于一般4k内存页,而为2M)支持。 这将在Redis中造成延迟和内存使用问题。 要解决此问题,请以root 用户身份运行命令“echo never>/sys/kernel/mm/transparent_hugepage/enabled”,并将其添加到您的/etc/rc.local中,以便在重启后保留设置。禁用THP后,必须重新启动Redis。

[root@ubuntu2004 ~]#cat /sys/kernel/mm/transparent_hugepage/enabled
always madvise [never]

#ubuntu开机配置
[root@ubuntu2004 ~]#cat /etc/rc.local
#!/bin/bash
echo never > /sys/kernel/mm/transparent_hugepage/enabled
[root@ubuntu2004 ~]#chmod +x /etc/rc.local

#CentOS开机配置
[root@centos8 ~]#echo 'echo never > /sys/kernel/mm/transparent_hugepage/enabled' >> /etc/rc.d/rc.local
[root@centos8 ~]#cat /etc/rc.d/rc.local
#!/bin/bash
touch /var/lock/subsys/local
echo never > /sys/kernel/mm/transparent_hugepage/enabled
[root@centos8 ~]#chmod +x /etc/rc.d/rc.local

#案例
[root@rocky8 ~]#tail -f -n30 /var/log/redis/redis.log 
2245:C 30 Oct 2022 09:45:20.056 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
2245:C 30 Oct 2022 09:45:20.056 # Redis version=5.0.3, bits=64, commit=00000000, modified=0, pid=2245, just started
2245:C 30 Oct 2022 09:45:20.056 # Configuration loaded
2245:C 30 Oct 2022 09:45:20.056 * supervised by systemd, will signal readiness
                _._                                                  
           _.-__ ''-._                                             
      _.-    .  _.  ''-._           Redis 5.0.3 (00000000/0) 64 bit
  .- .-.  \/    _.,_ ''-._                                   
 (    '      ,       .-  | ,    )     Running in standalone mode
 |-._-...- __...-.-._|' _.-'|     Port: 6379
 |    -._   ._    /     _.-'    |     PID: 2245
  -._    -._  -./  _.-'    _.-'                                   
 |-._-._    -.__.-'    _.-'_.-'|                                  
 |    -._-._        _.-'_.-'    |           http://redis.io        
  -._    -._-.__.-'_.-'    _.-'                                   
 |-._-._    -.__.-'    _.-'_.-'|                                  
 |    -._-._        _.-'_.-'    |                                  
  -._    -._-.__.-'_.-'    _.-'                                   
      -._    -.__.-'    _.-'                                       
          -._        _.-'                                           
              -.__.-'                                               

2245:M 30 Oct 2022 09:45:20.057 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
2245:M 30 Oct 2022 09:45:20.057 # Server initialized
2245:M 30 Oct 2022 09:45:20.057 # WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect.
2245:M 30 Oct 2022 09:45:20.057 # WARNING you have Transparent Huge Pages (THP) support enabled in your kernel. This will create latency and memory usage issues with Redis. To fix this issue run the command 'echo never > /sys/kernel/mm/transparent_hugepage/enabled' as root, and add it to your /etc/rc.local in order to retain the setting after a reboot. Redis must be restarted after THP is disabled.

[root@rocky8 ~]#egrep -v "^#|^$" /etc/sysctl.conf 
net.core.somaxconn = 1024
vm.overcommit_memory = 1
[root@rocky8 ~]#sysctl -p 
net.core.somaxconn = 1024
vm.overcommit_memory = 1

[root@rocky8 ~]#cat /sys/kernel/mm/transparent_hugepage/enabled
[always] madvise never
[root@rocky8 ~]#echo never > /sys/kernel/mm/transparent_hugepage/enabled
[root@rocky8 ~]#echo 'never > /sys/kernel/mm/transparent_hugepage/enabled' >> /etc/rc.d/rc.local 

[root@rocky8 ~]#tail -f -n30 /var/log/redis/redis.log 
2245:M 30 Oct 2022 09:50:59.499 # User requested shutdown...
2245:M 30 Oct 2022 09:50:59.499 * Saving the final RDB snapshot before exiting.
2245:M 30 Oct 2022 09:50:59.501 * DB saved on disk
2245:M 30 Oct 2022 09:50:59.501 * Removing the pid file.
2245:M 30 Oct 2022 09:50:59.501 # Redis is now ready to exit, bye bye...
2423:C 30 Oct 2022 09:50:59.518 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
2423:C 30 Oct 2022 09:50:59.518 # Redis version=5.0.3, bits=64, commit=00000000, modified=0, pid=2423, just started
2423:C 30 Oct 2022 09:50:59.518 # Configuration loaded
2423:C 30 Oct 2022 09:50:59.518 * supervised by systemd, will signal readiness
                _._                                                  
           _.-__ ''-._                                             
      _.-    .  _.  ''-._           Redis 5.0.3 (00000000/0) 64 bit
  .- .-.  \/    _.,_ ''-._                                   
 (    '      ,       .-  | ,    )     Running in standalone mode
 |-._-...- __...-.-._|' _.-'|     Port: 6379
 |    -._   ._    /     _.-'    |     PID: 2423
  -._    -._  -./  _.-'    _.-'                                   
 |-._-._    -.__.-'    _.-'_.-'|                                  
 |    -._-._        _.-'_.-'    |           http://redis.io        
  -._    -._-.__.-'_.-'    _.-'                                   
 |-._-._    -.__.-'    _.-'_.-'|                                  
 |    -._-._        _.-'_.-'    |                                  
  -._    -._-.__.-'_.-'    _.-'                                   
      -._    -.__.-'    _.-'                                       
          -._        _.-'                                           
              -.__.-'                                               

2423:M 30 Oct 2022 09:50:59.519 # Server initialized
2423:M 30 Oct 2022 09:50:59.520 * DB loaded from disk: 0.000 seconds
2423:M 30 Oct 2022 09:50:59.520 * Ready to accept connections

Redis 的多实例

[root@ubuntu2004 redis]#tree .
.
├── bin
│   ├── redis-benchmark
│   ├── redis-check-aof -> redis-server
│   ├── redis-check-rdb -> redis-server
│   ├── redis-cli
│   ├── redis-sentinel -> redis-server
│   └── redis-server
├── data
│   └── dump.rdb
├── etc
│   └── redis.conf
├── log
│   └── redis-6379.log
└── run
    └── redis_6379.pid


[root@ubuntu2004 redis]#cp etc/redis.conf etc/redis6380.conf 
[root@ubuntu2004 redis]#cp etc/redis.conf etc/redis6381.conf 
[root@ubuntu2004 redis]#mv etc/redis.conf etc/redis6379.conf
[root@ubuntu2004 redis]#tree etc/
etc/
├── redis6379.conf
├── redis6380.conf
└── redis6381.conf

[root@ubuntu2004 redis]#egrep "6379"  etc/redis6379.conf 
# Accept connections on the specified port, default is 6379 (IANA #815344).
port 6379
# tls-port 6379
pidfile /apps/redis/run/redis_6379.pid
logfile /apps/redis/log/redis-6379.log
# cluster-config-file nodes-6379.conf
# cluster-announce-tls-port 6379

[root@ubuntu2004 redis]#sed -i "s/6379/6380/g" etc/redis6380.conf 
[root@ubuntu2004 redis]#sed -i "s/6379/6381/g" etc/redis6381.conf

[root@ubuntu2004 redis]#vim  redis6379.conf
dbfilename dump6379.rdb
[root@ubuntu2004 redis]#vim  redis6380.conf
dbfilename dump6380.rdb
[root@ubuntu2004 redis]#vim  redis6381.conf
dbfilename dump6381.rdb


[root@ubuntu2004 redis]#egrep "6380"  etc/redis6380.conf 
# Accept connections on the specified port, default is 6380 (IANA #815344).
port 6380
# tls-port 6380
pidfile /apps/redis/run/redis_6380.pid
logfile /apps/redis/log/redis-6380.log
# cluster-config-file nodes-6380.conf
# cluster-announce-tls-port 6380
# cluster-announce-bus-port 6380
[root@ubuntu2004 redis]#egrep "6381"  etc/redis6381.conf 
# Accept connections on the specified port, default is 6381 (IANA #815344).
port 6381
# tls-port 6381
pidfile /apps/redis/run/redis_6381.pid
logfile /apps/redis/log/redis-6381.log
# cluster-config-file nodes-6381.conf
# cluster-announce-tls-port 6381

[root@ubuntu2004 redis]#cd /lib/systemd/system/
[root@ubuntu2004 system]#cat redis.service 
[Unit]
Description=Redis persistent key-value database
After=network.target

[Service]
ExecStart=/apps/redis/bin/redis-server /apps/redis/etc/redis.conf --supervised systemd
ExecStop=/bin/kill -s QUIT $MAINPID
Type=notify
User=redis
Group=redis
RuntimeDirectory=redis
RuntimeDirectoryMode=0755
LimitNOFILE=1000000

[Install]
WantedBy=multi-user.target

[root@ubuntu2004 system]#cp redis.service redis6380.service 
[root@ubuntu2004 system]#cp redis.service redis6381.service 
[root@ubuntu2004 system]#mv redis.service redis6379.service

[root@ubuntu2004 system]#ll redis63*
-rw-r--r-- 1 root root 353 10月 29 22:30 redis6379.service
-rw-r--r-- 1 root root 353 10月 30 09:57 redis6380.service
-rw-r--r-- 1 root root 353 10月 30 09:57 redis6381.service

[root@ubuntu2004 system]#vim redis6379.service 
ExecStart=/apps/redis/bin/redis-server /apps/redis/etc/redis6379.conf --supervised systemd

[root@ubuntu2004 system]#vim redis6380.service 
ExecStart=/apps/redis/bin/redis-server /apps/redis/etc/redis6380.conf --supervised systemd

[root@ubuntu2004 system]#vim redis6381.service 
ExecStart=/apps/redis/bin/redis-server /apps/redis/etc/redis6381.conf --supervised systemd

[root@ubuntu2004 system]#ss -ntlp
State       Recv-Q       Send-Q             Local Address:Port             Peer Address:Port      Process                                           
LISTEN      0            4096               127.0.0.53%lo:53                    0.0.0.0:*          users:(("systemd-resolve",pid=42463,fd=13))      
LISTEN      0            128                      0.0.0.0:22                    0.0.0.0:*          users:(("sshd",pid=779,fd=3))                    
LISTEN      0            128                         [::]:22                       [::]:*          users:(("sshd",pid=779,fd=4))                    
[root@ubuntu2004 system]#systemctl start redis6379.service 
[root@ubuntu2004 system]#systemctl start redis6380.service 
[root@ubuntu2004 system]#systemctl start redis6381.service 
[root@ubuntu2004 system]#ss -ntlp
State       Recv-Q       Send-Q             Local Address:Port             Peer Address:Port      Process                                           
LISTEN      0            4096               127.0.0.53%lo:53                    0.0.0.0:*          users:(("systemd-resolve",pid=42463,fd=13))      
LISTEN      0            128                      0.0.0.0:22                    0.0.0.0:*          users:(("sshd",pid=779,fd=3))                    
LISTEN      0            511                      0.0.0.0:6379                  0.0.0.0:*          users:(("redis-server",pid=54170,fd=6))          
LISTEN      0            511                      0.0.0.0:6380                  0.0.0.0:*          users:(("redis-server",pid=54186,fd=6))          
LISTEN      0            511                      0.0.0.0:6381                  0.0.0.0:*          users:(("redis-server",pid=54193,fd=6))          
LISTEN      0            128                         [::]:22                       [::]:*          users:(("sshd",pid=779,fd=4))                    
LISTEN      0            511                        [::1]:6379                     [::]:*          users:(("redis-server",pid=54170,fd=7))          
LISTEN      0            511                        [::1]:6380                     [::]:*          users:(("redis-server",pid=54186,fd=7))          
LISTEN      0            511                        [::1]:6381                     [::]:*          users:(("redis-server",pid=54193,fd=7)) 

[root@ubuntu2004 system]#redis-cli --no-auth-warning -a 123456 -p 6379 ping
PONG
[root@ubuntu2004 system]#redis-cli --no-auth-warning -a 123456 -p 6380 ping
PONG
[root@ubuntu2004 system]#redis-cli --no-auth-warning -a 123456 -p 6381 ping
PONG

[root@ubuntu2004 ~]#tree /apps/redis/
/apps/redis/
├── bin
│   ├── redis-benchmark
│   ├── redis-check-aof -> redis-server
│   ├── redis-check-rdb -> redis-server
│   ├── redis-cli
│   ├── redis-sentinel -> redis-server
│   └── redis-server
├── data
│   ├── dump6379.rdb
│   ├── dump6380.rdb
│   └── dump6381.rdb
├── etc
│   ├── redis6379.conf
│   ├── redis6380.conf
│   └── redis6381.conf
├── log
│   ├── redis-6379.log
│   ├── redis-6380.log
│   └── redis-6381.log
└── run
    ├── redis_6379.pid
    ├── redis_6380.pid
    └── redis_6381.pid

Redis 相关工具和客户端连接

[root@ubuntu2004 ~]#ll /apps/redis/bin/
总用量 31032
drwxr-xr-x 2 redis redis     4096 10月 29 22:30 ./
drwxr-xr-x 7 redis redis     4096 10月 29 22:30 ../
-rwxr-xr-x 1 redis redis  8113680 10月 29 22:30 redis-benchmark*                   #性能测试程序
lrwxrwxrwx 1 redis redis       12 10月 29 22:30 redis-check-aof -> redis-server*   #AOF文件检查程序
lrwxrwxrwx 1 redis redis       12 10月 29 22:30 redis-check-rdb -> redis-server*   #RDB文件检查程序
-rwxr-xr-x 1 redis redis  8083712 10月 29 22:30 redis-cli*                         #客户端程序
lrwxrwxrwx 1 redis redis       12 10月 29 22:30 redis-sentinel -> redis-server*    #哨兵程序,软连接到服务器端主程序
-rwxr-xr-x 1 redis redis 15566776 10月 29 22:30 redis-server*                      #服务端主程序

客户端程序 redis-cli
#默认为本机无密码连接
redis-cli
#远程客户端连接,注意:Redis没有用户的概念
redis-cli -h <Redis服务器IP> -p <PORT> -a <PASSWORD>

程序连接 Redis
Redis 支持多种开发语言访问
https://redis.io/clients

Shell 脚本访问 Redis

#!/bin/bash
NUM=100
PASS=123456
for i in seq $NUM;do
redis-cli -h 127.0.0.1 -a "$PASS" --no-auth-warning set key${i} value${i}
echo "key${i} value${i} 写入完成"
done
echo "$NUM个key写入完成"

Python 程序连接 Redis

[root@ubuntu2004 ~]#apt list Python3-redis
正在列表... 完成
python3-redis/focal 3.3.11-2 all

[root@ubuntu2004 ~]#apt -y install python3-redis

[root@ubuntu2004 ~]#vim test.py
#!/usr/bin/python3 
import redis
pool = redis.ConnectionPool(host="127.0.0.1",port=6379,password="123456",decode_responses=True)
r = redis.Redis(connection_pool=pool)
for i in range(100000):
    r.set("k%d" % i,"v%d" % i)
    data=r.get("k%d" % i)
    print(data)

[root@ubuntu2004 ~]#python3 test.py 
......
v99993
v99994
v99995
v99996
v99997
v99998
v99999

[root@ubuntu2004 ~]#redis-cli --no-auth-warning -a 123456 -p 6379 dbsize
(integer) 100000

图形工具

Redis 配置管理

Redis 配置文件说明

bind 0.0.0.0 #指定监听地址,支持用空格隔开的多个监听IP

protected-mode yes #redis3.2之后加入的新特性,在没有设置bind IP和密码的时候,redis只允许访问127.0.0.1:6379,可以远程连接,但当访问将提示警告信息并拒绝远程访问

port 6379 #监听端口,默认6379/tcp

tcp-backlog 511 #三次握手的时候server端收到client ack确认号之后的队列值,即全连接队列长度

timeout 0 #客户端和Redis服务端的连接超时时间,默认是0,表示永不超时

tcp-keepalive 300 #tcp 会话保持时间300s

daemonize no #默认no,即直接运行redis-server程序时,不作为守护进程运行,而是以前台方式运行,如果想在后台运行需改成yes,当redis作为守护进程运行的时候,它会写一个 pid 到/var/run/redis.pid 文件

supervised no #和OS相关参数,可设置通过upstart和systemd管理Redis守护进程,centos7后都使用systemd

pidfile /var/run/redis_6379.pid #pid文件路径,可以修改为/apps/redis/run/redis_6379.pid

loglevel notice #日志级别

logfile "/path/redis.log" #日志路径,示例:logfile "/apps/redis/log/redis_6379.log"

databases 16 #设置数据库数量,默认:0-15,共16个库

always-show-logo yes #在启动redis 时是否显示或在日志中记录记录redis的logo

save 900 1 #在900秒内有1个key内容发生更改,就执行快照机制
save 300 10 #在300秒内有10个key内容发生更改,就执行快照机制
save 60 10000 #60秒内如果有10000个key以上的变化,就自动快照备份

stop-writes-on-bgsave-error yes #默认为yes时,可能会因空间满等原因快照无法保存出错时,会禁止redis写入操作,生产建议为no #此项只针对配置文件中的自动save有效

rdbcompression yes #持久化到RDB文件时,是否压缩,"yes"为压缩,"no"则反之

rdbchecksum yes #是否对备份文件开启RC64校验,默认是开启

dbfilename dump.rdb #快照文件名

dir ./ #快照文件保存路径,示例:dir "/apps/redis/data"

#主从复制相关
# replicaof <masterip> <masterport> #指定复制的master主机地址和端口,5.0版之前的指令为slaveof
# masterauth <master-password> #指定复制的master主机的密码

replica-serve-stale-data yes #当从库同主库失去连接或者复制正在进行,从机库有两种运行方式:
1、设置为yes(默认设置),从库会继续响应客户端的读请求,此为建议值
2、设置为no,除去特定命令外的任何请求都会返回一个错误"SYNC with master in progress"。

replica-read-only yes #是否设置从库只读,建议值为yes,否则主库同步从库时可能会覆盖数据,造成数据丢失

repl-diskless-sync no #是否使用socket方式复制数据(无盘同步),新slave第一次连接master时需要做数据的全量同步,redis server就要从内存dump出新的RDB文件,然后从master传到slave,有两种方式把RDB文件传输给客户端:
1、基于硬盘(disk-backed):为no时,master创建一个新进程dump生成RDB磁盘文件,RDB完成之后由父进程(即主进程)将RDB文件发送给slaves,此为默认值
2、基于socket(diskless):master创建一个新进程直接dump RDB至slave的网络socket,不经过主进程和硬盘#推荐使用基于硬盘(为no),是因为RDB文件创建后,可以同时传输给更多的slave,但是基于socket(为
yes), 新slave连接到master之后得逐个同步数据。只有当磁盘I/O较慢且网络较快时,可用diskless(yes),否则一般建议使用磁盘(no)

repl-diskless-sync-delay 5 #diskless时复制的服务器等待的延迟时间,设置0为关闭,在延迟时间内到达的客户端,会一起通过diskless方式同步数据,但是一旦复制开始,master节点不会再接收新slave的复制请求,直到下一次同步开始才再接收新请求。即无法为延迟时间后到达的新副本提供服务,新副本将排队等待下一次RDB传输,因此服务器会等待一段时间才能让更多副本到达。推荐值:30-60

repl-ping-replica-period 10 #slave根据master指定的时间进行周期性的PING master,用于监测master状态,默认10s

repl-timeout 60 #复制连接的超时时间,需要大于repl-ping-slave-period,否则会经常报超时

repl-disable-tcp-nodelay no #是否在slave套接字发送SYNC之后禁用 TCP_NODELAY,如果选择"yes",Redis将合并多个报文为一个大的报文,从而使用更少数量的包向slaves发送数据,但是将使数据传输到slave上有延迟,Linux内核的默认配置会达到40毫秒,如果 "no" ,数据传输到slave的延迟将会减少,但要使用更多的带宽

repl-backlog-size 512mb #复制缓冲区内存大小,当slave断开连接一段时间后,该缓冲区会累积复制副本数据,因此当slave 重新连接时,通常不需要完全重新同步,只需传递在副本中的断开连接后没有同步的部分数据即可。只有在至少有一个slave连接之后才分配此内存空间,建议建立主从时此值要调大一些或在低峰期配置,否则会导致同步到slave失败

repl-backlog-ttl 3600 #多长时间内master没有slave连接,就清空backlog缓冲区

replica-priority 100 #当master不可用,哨兵Sentinel会根据slave的优先级选举一个master,此值最低的slave会优先当选master,而配置成0,永远不会被选举,一般多个slave都设为一样的值,让其自动选择

#min-replicas-to-write 3 #至少有3个可连接的slave,mater才接受写操作
#min-replicas-max-lag 10 #和上面至少3个slave的ping延迟不能超过10秒,否则master也将停止写操作

requirepass foobared #设置redis连接密码,之后需要AUTH pass,如果有特殊符号,用" "引起来,生产建议设置

rename-command #重命名一些高危命令,示例:rename-command FLUSHALL "" 禁用命令 #示例: rename-command del wang

maxclients 10000 #Redis最大连接客户端

maxmemory <bytes> #redis使用的最大内存,单位为bytes字节,0为不限制,建议设为物理内存一半,8G内存的计算方式8(G)*1024(MB)1024(KB)*1024(Kbyte),需要注意的是缓冲区是不计算在maxmemory内,生产中如果不设置此项,可能会导致OOM

#maxmemory-policy noeviction 此为默认值
# MAXMEMORY POLICY:当达到最大内存时,Redis 将如何选择要删除的内容。您可以从以下行为中选择一种:
#
# volatile-lru -> Evict 使用近似 LRU,只有设置了过期时间的键。
# allkeys-lru -> 使用近似 LRU 驱逐任何键。
# volatile-lfu -> 使用近似 LFU 驱逐,只有设置了过期时间的键。
# allkeys-lfu -> 使用近似 LFU 驱逐任何键。
# volatile-random -> 删除设置了过期时间的随机密钥。
# allkeys-random -> 删除一个随机密钥,任何密钥。
# volatile-ttl -> 删除过期时间最近的key(次TTL)
# noeviction -> 不要驱逐任何东西,只是在写操作时返回一个错误。
#
# LRU 表示最近最少使用
# LFU 表示最不常用
#
# LRU、LFU 和 volatile-ttl 都是使用近似随机算法实现的。
#
# 注意:使用上述任何一种策略,当没有合适的键用于驱逐时,Redis 将在需要更多内存的写操作时返回错误。这些通常是创建新密钥、添加数据或修改现有密钥的命令。一些示例是:SET、INCR、HSET、LPUSH、SUNIONSTORE、SORT(由于 STORE 参数)和 EXEC(如果事务包括任何需要内存的命令)。

#MAXMEMORY POLICY:当达到最大内存时,Redis 将如何选择要删除的内容。可以从下面行为中进行选择:
# volatile-lru -> 在具有过期集的键中使用近似 LRU 驱逐。
# allkeys-lru -> 使用近似 LRU 驱逐任何键。
# volatile-lfu -> 在具有过期集的键中使用近似 LFU 驱逐。
# allkeys-lfu -> 使用近似 LFU 驱逐任何键。
# volatile-random -> 从具有过期设置的密钥中删除一个随机密钥。
# allkeys-random -> 删除一个随机密钥,任何密钥。
# volatile-ttl -> 删除过期时间最近的key(次TTL)
# noeviction -> 不要驱逐任何东西,只是在写操作时返回一个错误。
#
# LRU 表示最近最少使用
# LFU 表示最不常用
#
# LRU、LFU 和 volatile-ttl 均使用近似实现随机算法。
#
# 注意:使用上述任何一种策略,Redis 都会在写入时返回错误操作,当没有合适的键用于驱逐时。

appendonly no #是否开启AOF日志记录,默认redis使用的是rdb方式持久化,这种方式在许多应用中已经足够用了,但是redis如果中途宕机,会导致可能有几分钟的数据丢失(取决于dump数据的间隔时间),根据save来策略进行持久化,Append Only File是另一种持久化方式,可以提供更好的持久化特性,Redis会把每次写入的数据在接收后都写入 appendonly.aof 文件,每次启动时Redis都会先把这个文件的数据读入内存里,先忽略RDB文件。默认不启用此功能

appendfilename "appendonly.aof" #文本文件AOF的文件名,存放在dir指令指定的目录中

appendfsync everysec #aof持久化策略的配置
#no表示由操作系统保证数据同步到磁盘,Linux的默认fsync策略是30秒,最多会丢失30s的数据
#always表示每次写入都执行fsync,以保证数据同步到磁盘,安全性高,性能较差
#everysec表示每秒执行一次fsync,可能会导致丢失这1s数据,此为默认值,也生产建议值

#同时在执行bgrewriteaof操作和主进程写aof文件的操作,两者都会操作磁盘,而bgrewriteaof往往会涉及大量磁盘操作,这样就会造成主进程在写aof文件的时候出现阻塞的情形,以下参数实现控制

no-appendfsync-on-rewrite no #在aof rewrite期间,是否对aof新记录的append暂缓使用文件同步策略,主要考虑磁盘IO开支和请求阻塞时间。
#默认为no,表示"不暂缓",新的aof记录仍然会被立即同步到磁盘,是最安全的方式,不会丢失数据,但是要忍受阻塞的问题
#为yes,相当于将appendfsync设置为no,这说明并没有执行磁盘操作,只是写入了缓冲区,因此这样并不会造成阻塞(因为没有竞争磁盘),但是如果这个时候redis挂掉,就会丢失数据。丢失多少数据呢?Linux
的默认fsync策略是30秒,最多会丢失30s的数据,但由于yes性能较好而且会避免出现阻塞因此比较推荐

#rewrite 即对aof文件进行整理,将空闲空间回收,从而可以减少恢复数据时间

auto-aof-rewrite-percentage 100 #当Aof log增长超过指定百分比例时,重写AOF文件,设置为0表示不自动重写Aof日志,重写是为了使aof体积保持最小,但是还可以确保保存最完整的数据

auto-aof-rewrite-min-size 64mb #触发aof rewrite的最小文件大小

aof-load-truncated yes #是否加载由于某些原因导致的末尾异常的AOF文件(主进程被kill/断电等),建议yes

aof-use-rdb-preamble no #redis4.0新增RDB-AOF混合持久化格式,在开启了这个功能之后,AOF重写产生的文件将同时包含RDB格式的内容和AOF格式的内容,其中RDB格式的内容用于记录已有的数据,而AOF格式的内容则用于记录最近发生了变化的数据,这样Redis就可以同时兼有RDB持久化和AOF持久化的优点(既能够快速地生成重写文件,也能够在出现问题时,快速地载入数据),默认为no,即不启用此功能lua-time-limit 5000 #lua脚本的最大执行时间,单位为毫秒

cluster-enabled yes #是否开启集群模式,默认不开启,即单机模式

cluster-config-file nodes-6379.conf #由node节点自动生成的集群配置文件名称

cluster-node-timeout 15000 #集群中node节点连接超时时间,单位ms,超过此时间,会踢出集群

cluster-replica-validity-factor 10 #单位为次,在执行故障转移的时候可能有些节点和master断开一段时间导致数据比较旧,这些节点就不适用于选举为master,超过这个时间的就不会被进行故障转移,不能当选master,计算公式:(node-timeout * replica-validity-factor) + repl-ping-replica-period

cluster-migration-barrier 1 #集群迁移屏障,一个主节点至少拥有1个正常工作的从节点,即如果主节点的slave节点故障后会将多余的从节点分配到当前主节点成为其新的从节点。

cluster-require-full-coverage yes #集群请求槽位全部覆盖,如果一个主库宕机且没有备库就会出现集群槽位不全,那么yes时redis集群槽位验证不全,就不再对外提供服务(对key赋值时,会出现CLUSTERDOWN The cluster is down的提示,cluster_state:fail,但ping 仍PONG),而no则可以继续使用,但是会出现查询数据查不到的情况(因为有数据丢失)。生产建议为no

cluster-replica-no-failover no #如果为yes,此选项阻止在主服务器发生故障时尝试对其主服务器进行故障转移。 但是,主服务器仍然可以执行手动强制故障转移,一般为no

#Slow log 是 Redis 用来记录超过指定执行时间的日志系统,执行时间不包括与客户端交谈,发送回复等I/O操作,而是实际执行命令所需的时间(在该阶段线程被阻塞并且不能同时为其它请求提供服务),由于slow log 保存在内存里面,读写速度非常快,因此可放心地使用,不必担心因为开启 slow log 而影响Redis 的速度

slowlog-log-slower-than 10000 #以微秒为单位的慢日志记录,为负数会禁用慢日志,为0会记录每个命令操作。默认值为10ms,一般一条命令执行都在微秒级,生产建议设为1ms-10ms之间

slowlog-max-len 128 #最多记录多少条慢日志的保存队列长度,达到此长度后,记录新命令会将最旧的命令从命令队列中删除,以此滚动删除,即,先进先出,队列固定长度,默认128,值偏小,生产建议设为1000以上

config 命令实现动态修改配置

onfig 命令用于查看当前redis配置、以及不重启redis服务实现动态更改redis配置等
注意:不是所有配置都可以动态修改,且此方式无法持久保存

CONFIG SET parameter value
时间复杂度:O(1)
CONFIG SET 命令可以动态地调整 Redis 服务器的配置(configuration)而无须重启。

可以使用它修改配置参数,或者改变 Redis 的持久化(Persistence)方式。
CONFIG SET 可以修改的配置参数可以使用命令 CONFIG GET * 来列出,所有被 CONFIG SET 修改的配置参数都会立即生效。

CONFIG GET parameter
时间复杂度: O(N),其中 N 为命令返回的配置选项数量。
CONFIG GET 命令用于取得运行中的 Redis 服务器的配置参数(configuration parameters),在Redis 2.4 版本中, 有部分参数没有办法用 CONFIG GET 访问,但是在最新的 Redis 2.6 版本中,所有配置参数都已经可以用 CONFIG GET 访问了。

CONFIG GET 接受单个参数 parameter 作为搜索关键字,查找所有匹配的配置参数,其中参数和值以“键-值对”(key-value pairs)的方式排列。比如执行 CONFIG GET s* 命令,服务器就会返回所有以 s 开头的配置参数及参数的值:

设置客户端连接密码

[root@ubuntu2004 ~]#redis-cli -a 123456
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
127.0.0.1:6379> CONFIG get requirepass
1) "requirepass"
2) "123456"
127.0.0.1:6379> CONFIG set requirepass 654321
OK
127.0.0.1:6379> CONFIG get requirepass
1) "requirepass"
2) "654321"

获取当前配置

127.0.0.1:6379> config get *
  1) "maxmemory-policy"
  2) "noeviction"
  3) "shutdown-on-sigint"
  4) "default"
  5) "hash-max-listpack-entries"
  6) "512"
  7) "hash-max-ziplist-value"
  8) "64"
  9) "min-replicas-max-lag"
 10) "10"
 11) "crash-memcheck-enabled"
 12) "yes"
 13) "databases"
 14) "16"
 15) "slave-announce-ip"
 16) ""
 17) "cluster-allow-replica-migration"
 18) "yes"
 19) "appendfsync"
 20) "everysec"
......

127.0.0.1:6379> config get bind
1) "bind"
2) "0.0.0.0 -::1"
127.0.0.1:6379> config set bind 10.0.0.201
OK
127.0.0.1:6379> config get bind
1) "bind"
2) "10.0.0.201"
#Redis5.0有些设置无法修改,Redis6.2.6版本支持修改bind

设置 Redis 使用的最大内存量

127.0.0.1:6379> config get maxmemory
1) "maxmemory"
2) "0"

127.0.0.1:6379> config set maxmemory 1G   # 或 8589934592
OK
127.0.0.1:6379> config get maxmemory
1) "maxmemory"
2) "1000000000"

慢查询

[root@ubuntu2004 ~]#egrep slowlog /apps/redis/etc/redis6379.conf 
slowlog-log-slower-than 10000   #单位为us,指定超过1us即为慢的指令,默认值为10000us
slowlog-max-len 1024            #指定只保存最近的1024条慢记录,默认值为128

127.0.0.1:6379> CONFIG get  slowlog-log-slower-than  
1) "slowlog-log-slower-than"
2) "10000"
127.0.0.1:6379> CONFIG set  slowlog-log-slower-than 1 
OK
127.0.0.1:6379> CONFIG get  slowlog-log-slower-than
1) "slowlog-log-slower-than"
2) "1"
127.0.0.1:6379> SLOWLOG reset  #清空慢日志
OK
127.0.0.1:6379> SLOWLOG len    #查看慢日志的记录条数
(integer) 1
127.0.0.1:6379> SLOWLOG get    #查看慢日志的最近n条记录,默认为10
1) 1) (integer) 7
   2) (integer) 1667097992
   3) (integer) 15
   4) 1) "SLOWLOG"
      2) "len"
   5) "127.0.0.1:45724"
   6) ""
2) 1) (integer) 6
   2) (integer) 1667097989
   3) (integer) 25
   4) 1) "SLOWLOG"
      2) "reset"
   5) "127.0.0.1:45724"
   6) ""

Redis 持久化

Redis 是基于内存型的NoSQL, 和MySQL是不同的,使用内存进行数据保存
如果想实现数据的持久化,Redis也也可支持将内存数据保存到硬盘文件中
Redis支持两种数据持久化保存方法
RDB:Redis DataBase
AOF:AppendOnlyFile

快照 ——————> 1.MySQL Dump
2.Redis RDB

写日志 ——————> 1.MySQL Binlog
2.Hbase Hlog
3.Redis AOF

RDB

RDB 工作原理

1.redis(neicun)——————>RDB文件(二进制)硬盘,创建

2.redis(neicun)<——————RDB文件(二进制)硬盘,启动载入

RDB(Redis DataBase):是基于某个时间点的快照,注意RDB只保留当前最新版本的一个快照
RDB 持久化功能所生成的 RDB 文件是一个经过压缩的二进制文件,通过该文件可以还原生成该 RDB 文件时数据库的状态。因为 RDB 文件是保存在磁盘中的,所以即便 Redis 服务进程甚至服务器宕机,只要磁盘中 RDB 文件存在,就能将数据恢复

RDB bgsave 实现快照的具体过程:

首先从redis 主进程先fork生成一个新的子进程,此子进程负责将Redis内存数据保存为一个临时文件tmp-<子进程pid>.rdb,当数据保存完成后,再将此临时文件改名为RDB文件,如果有前一次保存的RDB文件则会被替换,最后关闭此子进程
由于Redis只保留最后一个版本的RDB文件,如果想实现保存多个版本的数据,需要人为实现

范例: save 执行过程会使用主进程进行快照
[root@ubuntu2004 ~]#redis-cli -a 123456 save&
[root@ubuntu2004 ~]#pstree -p |grep redis ;ll /apps/redis/data
           |-redis-server(56593)-+-{redis-server}(56603)
           |                     |-{redis-server}(56604)
           |                     |-{redis-server}(56605)
           |                     -{redis-server}(56606)
           |-redis-server(56609)-+-{redis-server}(56610)
           |                     |-{redis-server}(56611)
           |                     |-{redis-server}(56612)
           |                     -{redis-server}(56613)
           |-redis-server(56614)-+-{redis-server}(56615)
           |                     |-{redis-server}(56616)
           |                     |-{redis-server}(56617)
           |                     -{redis-server}(56618)
           |           |-sshd(52855)---bash(52933)---redis-cli(57212)
总用量 32972
drwxr-xr-x 2 redis redis     4096 10月 30 11:23 ./
drwxr-xr-x 7 redis redis     4096 10月 29 22:30 ../
-rw-r--r-- 1 redis redis 20186915 10月 30 11:22 dump6379.rdb
-rw-r--r-- 1 redis redis  1477877 10月 30 11:07 dump6380.rdb
-rw-r--r-- 1 redis redis  1477877 10月 30 11:07 dump6381.rdb
-rw-r--r-- 1 redis redis 10608640 10月 30 11:23 temp-56593.rdb</code></pre></div>
<!-- /wp:loos-hcb/code-block -->

<!-- wp:image {"id":1878,"sizeSlug":"large","linkDestination":"none"} -->
<figure class="wp-block-image size-large"><img src="https://www.shuzihan.com/wp-content/uploads/2022/10/image-57-1024x449.png" alt="" class="wp-image-1878"/></figure>
<!-- /wp:image -->

<!-- wp:heading {"level":4} -->
<h4>RDB 相关配置</h4>
<!-- /wp:heading -->

<!-- wp:loos-hcb/code-block {"langType":"bash","langName":"Bash"} -->
<div class="hcb_wrap"><pre class="prism undefined-numbers lang-bash" data-lang="Bash"><code>#在配置文件中的 save 选项设置多个保存条件,只有任何一个条件满足,服务器都会自动执行 BGSAVE 命令
save 900 1 #900s内修改了1个key即触发保存RDB
save 300 10 #300s内修改了10个key即触发保存RDB
save 60 10000 #60s内修改了10000个key即触发保存RDB
dbfilename dump.rdb
dir ./ #编泽编译安装时默认RDB文件存放在Redis的工作目录,此配置可指定保存的数据目录
stop-writes-on-bgsave-error yes #当快照失败是否仍允许写入,yes为出错后禁止写入,建议为no
rdbcompression yes
rdbchecksum yes</code></pre></div>
<!-- /wp:loos-hcb/code-block -->

<!-- wp:loos-hcb/code-block {"langType":"bash","langName":"Bash"} -->
<div class="hcb_wrap"><pre class="prism undefined-numbers lang-bash" data-lang="Bash"><code>#禁用系统的自动快照
[root@ubuntu2004 ~]#grep save /apps/redis/etc/redis6379.conf 
save ""</code></pre></div>
<!-- /wp:loos-hcb/code-block -->

<!-- wp:heading {"level":4} -->
<h4>实现 RDB 方法</h4>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>save: 同步,不推荐使用,使用主进程完成快照,因此会阻赛其它命令执行<br>bgsave: 异步后台执行,不影响其它命令的执行,会开启独立的子进程,因此不会阻赛其它命令执行<br>配置文件实现自动保存: 在配置文件中制定规则,自动执行bgsave</p>
<!-- /wp:paragraph -->

<!-- wp:heading {"level":4} -->
<h4>RDB 模式的优缺点</h4>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>RDB快照只保存某个时间点的数据,恢复的时候直接加载到内存即可,不用做其他处理,这种文件适合用于做灾备处理.可以通过自定义时间点执行redis指令bgsave或者save保存快照,实现多个版本的备份<br>比如: 可以在最近的24小时内,每小时备份一次RDB文件,并且在每个月的每一天,也备份一个RDB文件。这样的话,即使遇上问题,也可以随时将数据集还原到指定的不同的版本。<br>RDB在大数据集时恢复的速度比AOF方式要快</p>
<!-- /wp:paragraph -->

<!-- wp:heading {"level":4} -->
<h4>RDB 模式缺点</h4>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>不能实时保存数据,可能会丢失自上一次执行RDB备份到当前的内存数据如果需要尽量避免在服务器故障时丢失数据,那么RDB并不适合。虽然Redis允许设置不同的保存点(save point)来控制保存RDB文件的频率,但是,因为RDB文件需要保存整个数据集的状态,<br>所以它可能并不是一个非常快速的操作。因此一般会超过5分钟以上才保存一次RDB文件。在这种情况下,一旦发生故障停机,就可能会丢失较长时间的数据。<br>在数据集比较庞大时,fork()子进程可能会非常耗时,造成服务器在一定时间内停止处理客户端请求,如果数据集非常巨大,并且CPU时间非常紧张的话,那么这种停止时间甚至可能会长达整整一秒或更久。另外子进程完成生成RDB文件的时间也会花更长时间.</p>
<!-- /wp:paragraph -->

<!-- wp:heading {"level":4} -->
<h4>范例: 手动执行备份RDB</h4>
<!-- /wp:heading -->

<!-- wp:loos-hcb/code-block {"langType":"bash","langName":"Bash"} -->
<div class="hcb_wrap"><pre class="prism undefined-numbers lang-bash" data-lang="Bash"><code>[root@ubuntu2004 ~]#redis-cli -a 123456
127.0.0.1:6379> bgsave
Background saving started

[root@ubuntu2004 ~]#ll /apps/redis/data/
总用量 22680
drwxr-xr-x 2 redis redis     4096 10月 30 11:47 ./
drwxr-xr-x 7 redis redis     4096 10月 29 22:30 ../
-rw-r--r-- 1 redis redis 20255657 10月 30 11:47 dump6379.rdb   #注意修改时间
-rw-r--r-- 1 redis redis  1477877 10月 30 11:07 dump6380.rdb
-rw-r--r-- 1 redis redis  1477877 10月 30 11:07 dump6381.rdb</code></pre></div>
<!-- /wp:loos-hcb/code-block -->

<!-- wp:heading {"level":4} -->
<h4>范例: 手动备份RDB文件的脚本</h4>
<!-- /wp:heading -->

<!-- wp:loos-hcb/code-block {"langType":"bash","langName":"Bash"} -->
<div class="hcb_wrap"><pre class="prism undefined-numbers lang-bash" data-lang="Bash"><code>[root@ubuntu2004 ~]#vim /apps/redis/etc/redis6379.conf 
save ""
dbfilename dump6379.rdb
dir /apps/redis/data/
appendonly no

[root@ubuntu2004 ~]#cat redis_backup_rdb.sh
#!/bin/bash
#
#********************************************************************
#Author:            shuhong
#QQ:                985347841
#Date:              2022-10-30
#FileName:          redis_backup_rdb.sh
#URL:               hhhhh
#Description:       The test script
#Copyright (C):     2022 All rights reserved
#********************************************************************

BACKUP=/backup/redis-rdb
DIR=/apps/redis/data/
FILE=dump6379.rdb
PASS=123456

color () {
    RES_COL=60
    MOVE_TO_COL="echo -en \\033[${RES_COL}G"
    SETCOLOR_SUCCESS="echo -en \\033[1;32m"
    SETCOLOR_FAILURE="echo -en \\033[1;31m"
    SETCOLOR_WARNING="echo -en \\033[1;33m"
    SETCOLOR_NORMAL="echo -en \E[0m"
    echo -n "$1" && $MOVE_TO_COL
    echo -n "["
    if [ $2 = "success" -o $2 = "0" ] ;then
        ${SETCOLOR_SUCCESS}
        echo -n $" OK "
    elif [ $2 = "failure" -o $2 = "1" ] ;then
        ${SETCOLOR_FAILURE}
        echo -n $"FAILED"
    else
        ${SETCOLOR_WARNING}
        echo -n $"WARNING"
    fi
    ${SETCOLOR_NORMAL}
    echo -n "]"
    echo
}

redis-cli -h 127.0.0.1 -a $PASS --no-auth-warning bgsave

result=-cli -a $PASS --no-auth-warning info Persistence |grep rdb_bgsave_in_progress| sed -rn 's/.*:([0-9]+).*/\1/p'

until [ $result -eq 0 ] ;do
    sleep 1
    result=-cli -a $PASS --no-auth-warning info Persistence |grep rdb_bgsave_in_progress | sed -rn 's/.*:([0-9]+).*/\1/p'
done

DATE= +%F_%H-%M-%S`

[ -e $BACKUP ] || { mkdir -p $BACKUP ; chown -R redis.redis $BACKUP; }
cp $DIR/$FILE $BACKUP/dump_6379-${DATE}.rdb

color "Backup redis RDB" 0

[root@ubuntu2004 ~]#bash redis_backup_rdb.sh 
Background saving started
Backup redis RDB                                           [ OK ]
[root@ubuntu2004 ~]#ll /backup/redis-rdb/
总用量 19792
drwxr-xr-x 2 redis redis     4096 10月 30 11:58 ./
drwxr-xr-x 3 root  root      4096 10月 30 11:58 ../
-rw-r--r-- 1 root  root  20255657 10月 30 11:58 dump_6379-2022-10-30_11-58-31.rdb

AOF

AOF 工作原理

AOF 即 AppendOnlyFile,AOF 和 RDB 都采有COW机制,AOF可以指定不同的保存策略,默认为每秒钟执行一次 fsync,按照操作的顺序地将变更命令追加至指定的AOF日志文件尾部

在第一次启用AOF功能时,会做一次完全备份,后续将执行增量性备份,相当于完全数据备份+增量变化

如果同时启用RDB和AOF,进行恢复时,默认AOF文件优先级高于RDB文件,即会使用AOF文件进行恢复

在第一次开启AOF功能时,会自动备份所有数据到AOF文件中,后续只会记录数据的更新指令

注意: AOF 模式默认是关闭的,第一次开启AOF后,并重启服务生效后,会因为AOF的优先级高于RDB,而AOF默认没有数据文件存在,从而导致所有数据丢失

范例错误开启
[root@ubuntu2004 ~]#redis-cli -a 123456 dbsize
(integer) 1100000

[root@ubuntu2004 ~]#vim /apps/redis/etc/redis6379.conf 
appendonly yes

[root@ubuntu2004 ~]#systemctl restart redis6379.service 
[root@ubuntu2004 ~]#redis-cli -a 123456 dbsize
(integer) 0
范例: 正确启用AOF功能,访止数据丢失
[root@ubuntu2004 ~]#redis-cli -a 123456 dbsize
(integer) 1100000

[root@ubuntu2004 ~]#redis-cli 
127.0.0.1:6379> AUTH 123456
OK
127.0.0.1:6379> CONFIG get appendonly
1) "appendonly"
2) "no"
127.0.0.1:6379> CONFIG set appendonly yes #自动触发AOF重写,会自动备份所有数据到AOF文件
OK

[root@ubuntu2004 ~]#tree /apps/redis/data/
/apps/redis/data/
├── appendonlydir
│   ├── appendonly.aof.1.base.rdb
│   ├── appendonly.aof.1.incr.aof
│   └── appendonly.aof.manifest
├── dump6379.rdb
├── dump6380.rdb
└── dump6381.rdb

[root@ubuntu2004 ~]#vim /apps/redis/etc/redis6379.conf 
appendonly yes

[root@ubuntu2004 ~]#systemctl restart redis6379.service 
[root@ubuntu2004 ~]#redis-cli -a 123456 dbsize
(integer) 1100000

AOF 相关配置

appendonly no #是否开启AOF日志记录,默认redis使用的是rdb方式持久化,这种方式在许多应用中已经足够用了,但是redis如果中途宕机,会导致可能有几分钟的数据丢失(取决于dump数据的间隔时间),根据save来策略进行持久化,Append Only File是另一种持久化方式,可以提供更好的持久化特性,Redis会把每次写入的数据在接收后都写入 appendonly.aof 文件,每次启动时Redis都会先把这个文件的数据读入内存里,先忽略RDB文件。默认不启用此功能

appendfilename "appendonly.aof" #文本文件AOF的文件名,存放在dir指令指定的目录中

appendfsync everysec #aof持久化策略的配置
#no表示由操作系统保证数据同步到磁盘,Linux的默认fsync策略是30秒,最多会丢失30s的数据
#always表示每次写入都执行fsync,以保证数据同步到磁盘,安全性高,性能较差
#everysec表示每秒执行一次fsync,可能会导致丢失这1s数据,此为默认值,也生产建议值
dir /path

#rewrite相关
no-appendfsync-on-rewrite yes
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
aof-load-truncated yes

AOF rewrite 重写

将一些重复的,可以合并的,过期的数据重新写入一个新的AOF文件,从而节约AOF备份占用的硬盘空间,也能加速恢复过程
可以手动执行bgrewriteaof 触发AOF,第一次开启AOF功能,或定义自动rewrite 策略

AOF rewrite 重写相关配置

#同时在执行bgrewriteaof操作和主进程写aof文件的操作,两者都会操作磁盘,而bgrewriteaof往往会涉及大量磁盘操作,这样就会造成主进程在写aof文件的时候出现阻塞的情形,以下参数实现控制

no-appendfsync-on-rewrite no #在aof rewrite期间,是否对aof新记录的append暂缓使用文件同步策略,主要考虑磁盘IO开支和请求阻塞时间。
#默认为no,表示"不暂缓",新的aof记录仍然会被立即同步到磁盘,是最安全的方式,不会丢失数据,但是要忍受阻塞的问题
#为yes,相当于将appendfsync设置为no,这说明并没有执行磁盘操作,只是写入了缓冲区,因此这样并不会造成阻塞(因为没有竞争磁盘),但是如果这个时候redis挂掉,就会丢失数据。丢失多少数据呢?Linux的默认fsync策略是30秒,最多会丢失30s的数据,但由于yes性能较好而且会避免出现阻塞因此比较推荐

#rewrite 即对aof文件进行整理,将空闲空间回收,从而可以减少恢复数据时间
auto-aof-rewrite-percentage 100 #当Aof log增长超过指定百分比例时,重写AOF文件,设置为0表示不自动重写Aof日志,重写是为了使aof体积保持最小,但是还可以确保保存最完整的数据

auto-aof-rewrite-min-size 64mb #触发aof rewrite的最小文件大小

aof-load-truncated yes #是否加载由于某些原因导致的末尾异常的AOF文件(主进程被kill/断电等),建议yes

手动执行AOF重写 BGREWRITEAOF 命令

BGREWRITEAOF
时间复杂度: O(N), N 为要追加到 AOF 文件中的数据数量。
执行一个 AOF文件 重写操作。重写会创建一个当前 AOF 文件的体积优化版本。

即使 BGREWRITEAOF 执行失败,也不会有任何数据丢失,因为旧的 AOF 文件在 BGREWRITEAOF 成功之前不会被修改。

重写操作只会在没有其他持久化工作在后台执行时被触发,也就是说:如果 Redis 的子进程正在执行快照的保存工作,那么 AOF 重写的操作会被预定(scheduled),等到保存工作完成之后再执行 AOF 重写。在这种情况下, BGREWRITEAOF 的返回值仍然是 OK ,但还会加上一条额外的信息,说明 BGREWRITEAOF 要等到保存操作完成之后才能执行。在 Redis 2.6 或以上的版本,可以使用 INFO [section] 命令查看 BGREWRITEAOF 是否被预定。

如果已经有别的 AOF 文件重写在执行,那么 BGREWRITEAOF 返回一个错误,并且这个新的BGREWRITEAOF 请求也不会被预定到下次执行。

从 Redis 2.4 开始, AOF 重写由 Redis 自行触发, BGREWRITEAOF 仅仅用于手动触发重写操作。

范例: 手动 bgrewriteaof

[root@ubuntu2004 ~]#redis-cli -a 123456
127.0.0.1:6379> BGREWRITEAOF 
Background append only file rewriting started

#同时可以观察到下面显示,多了一个子进程

AOF 模式优缺点

AOF 模式优点
数据安全性相对较高,根据所使用的fsync策略(fsync是同步内存中redis所有已经修改的文件到存储设备),默认是appendfsync everysec,即每秒执行一次 fsync,在这种配置下,Redis 仍然可以保持良好的性能,并且就算发生故障停机,也最多只会丢失一秒钟的数据( fsync会在后台线程执行,所以主线程可以继续努力地处理命令请求)

由于该机制对日志文件的写入操作采用的是append模式,因此在写入过程中不需要seek, 即使出现宕机现象,也不会破坏日志文件中已经存在的内容。然而如果本次操作只是写入了一半数据就出现了系统崩溃问题,不用担心,在Redis下一次启动之前,可以通过 redis-check-aof 工具来解决数据一致性的问题

Redis可以在 AOF文件体积变得过大时,自动地在后台对AOF进行重写,重写后的新AOF文件包含了恢复当前数据集所需的最小命令集合。整个重写操作是绝对安全的,因为Redis在创建新 AOF文件的过程中,append模式不断的将修改数据追加到现有的 AOF文件里面,即使重写过程中发生停机,现有的 AOF文件也不会丢失。而一旦新AOF文件创建完毕,Redis就会从旧AOF文件切换到新AOF文件,并开始对新AOF文件进行追加操作。

AOF包含一个格式清晰、易于理解的日志文件用于记录所有的修改操作。事实上,也可以通过该文件完成数据的重建

AOF文件有序地保存了对数据库执行的所有写入操作,这些写入操作以Redis协议的格式保存,因此 AOF文件的内容非常容易被人读懂,对文件进行分析(parse)也很轻松。导出(export)AOF文件也非常简单:举个例子,如果不小心执行了FLUSHALL.命令,但只要AOF文件未被重写,那么只要停止服务器,移除 AOF文件末尾的FLUSHAL命令,并重启Redis ,就可以将数据集恢复到FLUSHALL执行之前的状态。
AOF 模式缺点
即使有些操作是重复的也会全部记录,AOF 的文件大小一般要大于 RDB 格式的文件
AOF 在恢复大数据集时的速度比 RDB 的恢复速度要慢
如果 fsync 策略是appendfsync no, AOF保存到磁盘的速度甚至会可能会慢于RDB
bug 出现的可能性更多

RDB和AOF 的选择

如果主要充当缓存功能,或者可以承受较长时间,比如数分钟数据的丢失, 通常生产环境一般只需启用RDB即可,此也是默认值
如果一点数据都不能丢失,可以选择同时开启RDB和AOF
一般不建议只开启AOF

Redis 常用命令

官方文档:https://redis.io/commands

参考链接:
http://redisdoc.com/
http://doc.redisfans.com/
https://www.php.cn/manual/view/36359.html

INFO

127.0.0.1:6379> info
# Server
redis_version:7.0.5
redis_git_sha1:00000000
redis_git_dirty:0
redis_build_id:c8b80a54fd430854
redis_mode:standalone
os:Linux 5.4.0-124-generic x86_64
arch_bits:64
monotonic_clock:POSIX clock_gettime
multiplexing_api:epoll
....

127.0.0.1:6379> info server
# Server
redis_version:7.0.5
redis_git_sha1:00000000
redis_git_dirty:0
redis_build_id:c8b80a54fd430854
redis_mode:standalone
os:Linux 5.4.0-124-generic x86_64
arch_bits:64
monotonic_clock:POSIX clock_gettime
multiplexing_api:epoll
atomicvar_api:c11-builtin
gcc_version:9.4.0
process_id:61708
process_supervised:systemd
run_id:401a5e67b641079d7bef8c014f603ff7f505fba0
tcp_port:6379
server_time_usec:1667110589783717
uptime_in_seconds:720
uptime_in_days:0
hz:10
configured_hz:10
lru_clock:6166205
executable:/apps/redis/bin/redis-server
config_file:/apps/redis/etc/redis6379.conf
io_threads_active:0

SELECT

切换数据库,相当于在MySQL的 USE DBNAME 指令

127.0.0.1:6379> select 1
OK
127.0.0.1:6379[1]> select 16
(error) ERR DB index is out of range
127.0.0.1:6379[1]> select 15
OK

KEYS(慎用,容易引起,缓存雪崩等问题)

127.0.0.1:6379> 
127.0.0.1:6379> KEYS *
.....
1099998) "kk956260"
1099999) "k36119"
1100000) "kk98551"
(7.44s)

127.0.0.1:6379> MSET one 1 two 2 three 3 four 4 # 一次设置 4 个 key
OK 
127.0.0.1:6379> KEYS *o*
1) "one"
2) "four"
3) "two"
127.0.0.1:6379> KEYS t??
1) "two"
127.0.0.1:6379> KEYS t[w]*
1) "two"

BGSAVE

手动在后台执行RDB持久化操作

127.0.0.1:6379> BGSAVE
Background saving started

DBSIZE

返回当前库下的所有key 数量

127.0.0.1:6379> DBSIZE
(integer) 1100004

FLUSHDB(慎用,清库操作)

强制清空当前库中的所有key,此命令慎用!

127.0.0.1:6379[1]> SELECT 0
OK
127.0.0.1:6379> DBSIZE
(integer) 4
127.0.0.1:6379> FLUSHDB
OK
127.0.0.1:6379> DBSIZE
(integer) 0
127.0.0.1:6379>

FLUSHALL(慎用,清库操作)

强制清空当前Redis服务器所有数据库中的所有key,即删除所有数据,此命令慎用!

127.0.0.1:6379> FLUSHALL
OK

#生产建议修改配置使用rename-command禁用此命令
vim /etc/redis.conf
rename-command FLUSHALL "“ #flushdb和flushall 配置和AOF功能冲突,需要设置 appendonly no
#rename-command 可能会在后续版本淘汰

SHUTDOWN

可用版本: >= 1.0.0
时间复杂度: O(N),其中 N 为关机时需要保存的数据库键数量。
SHUTDOWN 命令执行以下操作:

关闭Redis服务,停止所有客户端连接

如果有至少一个保存点在等待,执行 SAVE 命令

如果 AOF 选项被打开,更新 AOF 文件

关闭 redis 服务器(server)

如果持久化被打开的话, SHUTDOWN 命令会保证服务器正常关闭而不丢失任何数据。另一方面,假如只是单纯地执行 SAVE 命令,然后再执行 QUIT 命令,则没有这一保证 —— 因为在执行SAVE 之后、执行 QUIT 之前的这段时间中间,其他客户端可能正在和服务器进行通讯,这时如果执行 QUIT就会造成数据丢失。

#建议关闭此指令
vim /etc/redis.conf
rename-command shutdown ""

Redis 数据类型

参考资料:http://www.redis.cn/topics/data-types.html
相关命令参考: http://redisdoc.com/

字符串 string

字符串是一种最基本的Redis值类型。Redis字符串是二进制安全的,这意味着一个Redis字符串能包含任意类型的数据,例如: 一张JPEG格式的图片或者一个序列化的Ruby对象。一个字符串类型的值最多能存储512M字节的内容。Redis 中所有 key 都是字符串类型的。此数据类型最为常用

命令含义复杂度
set key value设置key-valueo(1)
get key获取key-valueo(1)
del key删除key-valueo(1)
setnx setxx根据key是否存在设置key-valueo(1)
Incr decr计数o(1)
mget mset批量操作key-valueo(1)
创建一个key

SET key value [EX seconds] [PX milliseconds] [NX|XX]
时间复杂度: O(1)
将字符串值 value 关联到 key 。

如果 key 已经持有其他值, SET 就覆写旧值, 无视类型。
当 SET 命令对一个带有生存时间(TTL)的键进行设置之后, 该键原有的 TTL 将被清除。

从 Redis 2.6.12 版本开始, SET 命令的行为可以通过一系列参数来修改:
EX seconds : 将键的过期时间设置为 seconds 秒。 执行 SET key value EX seconds 的效果等同于执行 SETEX key seconds value 。
PX milliseconds : 将键的过期时间设置为 milliseconds 毫秒。 执行 SET key value PX milliseconds 的效果等同于执行 PSETEX key milliseconds value 。
NX : 只在键不存在时, 才对键进行设置操作。 执行 SET key value NX 的效果等同于执行 SETNXkey value 。
XX : 只在键已经存在时, 才对键进行设置操作。
案例:
#不论key是否存在.都设置
127.0.0.1:6379> set key1 value1
OK
127.0.0.1:6379> get key1
"value1"
127.0.0.1:6379> TYPE key1
string
127.0.0.1:6379> set title ceo ex 3
OK
127.0.0.1:6379> set name shu
OK
127.0.0.1:6379> get name
"shu"

#Key大小写敏感
127.0.0.1:6379> get NAME
(nil)
127.0.0.1:6379> set NAME hong
OK
127.0.0.1:6379> get NAME
"hong"
127.0.0.1:6379> get name
"shu"

#key不存在,才设置,相当于add
27.0.0.1:6379> get name
"shu"
127.0.0.1:6379> setnx name test
(integer) 0
127.0.0.1:6379> get name
"shu"

#key存在,才设置,相当于update
127.0.0.1:6379> get title
"test"
127.0.0.1:6379> set title ceo xx
OK
127.0.0.1:6379> get title
"ceo"
127.0.0.1:6379> get age
(nil)
127.0.0.1:6379> set age 20 xx
(nil)
127.0.0.1:6379> get age
(nil)

#查看一个key的值
127.0.0.1:6379> get key1
"value1"

#删除key
127.0.0.1:6379> del key1
(integer) 1
127.0.0.1:6379> del name NAME
(integer) 2

#批量设置多个key
127.0.0.1:6379> MSET key1 value1 key2 value2
OK

#批量获取多个key
127.0.0.1:6379> MGET key1 key2
1) "value1"
2) "value2"

#追加key的数据
127.0.0.1:6379> APPEND key1 " append new value"
(integer) 23
127.0.0.1:6379> get key1
"value1 append new value"

#设置新值并返回旧值
127.0.0.1:6379> set name shu
OK
127.0.0.1:6379> getset name hong
"shu"
127.0.0.1:6379> get name
"hong"

 #返回字符串 key 对应值的字节数
127.0.0.1:6379> STRLEN name
(integer) 4
127.0.0.1:6379> APPEND name "shu"
(integer) 7
127.0.0.1:6379> get name
"hongshu"
127.0.0.1:6379> STRLEN name
(integer) 7

#判断 key 是否存在
127.0.0.1:6379> set test time ex 10
OK
127.0.0.1:6379> set age 20
OK
127.0.0.1:6379> exists name
(integer) 1
127.0.0.1:6379> exists name age
(integer) 2

#获取 key 的过期时长
127.0.0.1:6379> TTL name
(integer) -1
127.0.0.1:6379> set name shuhong ex 100
OK
127.0.0.1:6379> TTL name
(integer) 95

#重置key的过期时长
127.0.0.1:6379> set name shu ex 1000
OK
127.0.0.1:6379> ttl name
(integer) 997
127.0.0.1:6379> EXPIRE name 100
(integer) 1
127.0.0.1:6379> ttl name
(integer) 99

#取消key的期限
127.0.0.1:6379> ttl name
(integer) 99
127.0.0.1:6379> PERSIST name
(integer) 1
127.0.0.1:6379> ttl name
(integer) -1

#数字递增
利用INCR命令簇(INCR, DECR, INCRBY,DECRBY)来把字符串当作原子计数器使用。
127.0.0.1:6379> set num 10 
OK
127.0.0.1:6379> INCR num
(integer) 11
127.0.0.1:6379> get num
"11"

#数字递减
127.0.0.1:6379> get num
"11"
127.0.0.1:6379> DECR num
(integer) 10
127.0.0.1:6379> get num
"10"

#数字增加
将key对应的数字加decrement(可以是负数)。如果key不存在,操作之前,key就会被置为0。如果key的value类型错误或者是个不能表示成数字的字符串,就返回错误。这个操作最多支持64位有符号的正型数字。
127.0.0.1:6379> get num
"10"
127.0.0.1:6379> INCRBY num 5
(integer) 15
127.0.0.1:6379> get num
"15"

#数字减少
decrby 可以减小数值(也可以增加)
127.0.0.1:6379> get num
"15"
127.0.0.1:6379> DECRBY num 10
(integer) 5
127.0.0.1:6379> get num
"5"


列表 list

Redis列表就是简单的字符串数组,按照插入顺序排序. 支持双向读写,可以添加一个元素到列表的头部(左边)或者尾部(右边),一个列表最多可以包含2^32-1=4294967295个元素,每个列表元素有下标来标识,下标 0 表示列表的第一个元素,以 1 表示列表的第二个元素,以此类推。 也可以使用负数下标,以 -1 表示列表的最后一个元素, -2 表示列表的倒数第二个元素,元素值可以重复,常用于存入日志等场景,此数据类型比较常用

列表特点:
有序
可重复
左右都可以操作

LPUSH和RPUSH都可以插入列表

LPUSH key value [value ...]
时间复杂度: O(1)
将一个或多个值 value 插入到列表 key 的表头

如果有多个 value 值,那么各个 value 值按从左到右的顺序依次插入到表头: 比如说,对空列表mylist 执行命令 LPUSH mylist a b c ,列表的值将是 c b a ,这等同于原子性地执行 LPUSHmylist a 、 LPUSH mylist b 和 LPUSH mylist c 三个命令。

如果 key 不存在,一个空列表会被创建并执行 LPUSH 操作。
当 key 存在但不是列表类型时,返回一个错误。

RPUSH key value [value ...]
时间复杂度: O(1)
将一个或多个值 value 插入到列表 key 的表尾(最右边)。

如果有多个 value 值,那么各个 value 值按从左到右的顺序依次插入到表尾:比如对一个空列表 mylist执行 RPUSH mylist a b c ,得出的结果列表为 a b c ,等同于执行命令 RPUSH mylist a 、RPUSH mylist b 、 RPUSH mylist c 。

如果 key 不存在,一个空列表会被创建并执行 RPUSH 操作。
当 key 存在但不是列表类型时,返回一个错误。
#列表追加新数据
127.0.0.1:6379> LPUSH list1 tom
(integer) 1
127.0.0.1:6379> RPUSH list1 jack
(integer) 2

#获取列表长度(元素个数)
127.0.0.1:6379> LLEN list1
(integer) 2

#获取列表指定位置元素数据
127.0.0.1:6379> LPUSH list1 a b c d
(integer) 4
127.0.0.1:6379> LINDEX list1 0 #获取0编号的元素
"d"
127.0.0.1:6379> LINDEX list1 3 #获取3编号的元素
"a"
127.0.0.1:6379> LINDEX list1 -1 #获取最后一个的元素
"a"

127.0.0.1:6379> LPUSH list1 a b c d
(integer) 4
127.0.0.1:6379> LRANGE list1 1 2
1) "c"
2) "b"
127.0.0.1:6379> LRANGE list1 0 3 #所有元素
1) "d"
2) "c"
3) "b"
4) "a"
127.0.0.1:6379> LRANGE list1 0 -1 #所有元素
1) "d"
2) "c"
3) "b"
4) "a"

127.0.0.1:6379> RPUSH list2 zhang wang li zhao
(integer) 4
127.0.0.1:6379> LRANGE list2 1 2 #指定范围
1) "wang"
2) "li"
127.0.0.1:6379> LRANGE list2 2 2 #指定位置
1) "li"
127.0.0.1:6379> LRANGE list2 0 -1 #所有元素
1) "zhang"
2) "wang"
3) "li"
4) "zhao"

#修改列表指定索引值
127.0.0.1:6379> RPUSH listkey a b c d e f
(integer) 6
127.0.0.1:6379> lrange listkey 0 -1
1) "a"
2) "b"
3) "c"
4) "d"
5) "e"
6) "f"
127.0.0.1:6379> lset listkey 2 java
OK
127.0.0.1:6379> lrange listkey 0 -1
1) "a"
2) "b"
3) "java"
4) "d"
5) "e"
6) "f"

#删除列表数据
127.0.0.1:6379> LPUSH list1 a b c d
(integer) 4
127.0.0.1:6379> LRANGE list1 0 3
1) "d"
2) "c"
3) "b"
4) "a"
127.0.0.1:6379> LPOP list1 #弹出左边第一个元素,即删除第一个
"d"
127.0.0.1:6379> LLEN list1
(integer) 3
127.0.0.1:6379> LRANGE list1 0 2
1) "c"
2) "b"
3) "a"
127.0.0.1:6379> RPOP list1 #弹出右边第一个元素,即删除最后一个
"a"
127.0.0.1:6379> LLEN list1
(integer) 2
127.0.0.1:6379> LRANGE list1 0 1
1) "c"
2) "b"
#LTRIM 对一个列表进行修剪(trim),让列表只保留指定区间内的元素,不在指定区间之内的元素都将被删除
127.0.0.1:6379> LLEN list1
(integer) 4
127.0.0.1:6379> LRANGE list1 0 3
1) "d"
2) "c"
3) "b"
4) "a"
127.0.0.1:6379> LTRIM list1 1 2 #只保留1,2号元素
OK
127.0.0.1:6379> LLEN list1
(integer) 2
127.0.0.1:6379> LRANGE list1 0 1
1) "c"
2) "b"
#删除list
127.0.0.1:6379> DEL list1
(integer) 1
127.0.0.1:6379> EXISTS list1
(integer) 0

集合 set

Set 是一个无序的字符串合集,同一个集合中的每个元素是唯一无重复的,支持在两个不同的集合中对数据进行逻辑处理,常用于取交集,并集,统计等场景,例如: 实现共同的朋友

集合特点:
无序
无重复
集合间操作

#创建集合
127.0.0.1:6379> SADD set1 v1
(integer) 1
127.0.0.1:6379> SADD set2 v2 v4
(integer) 2
127.0.0.1:6379> TYPE set1
set
127.0.0.1:6379> TYPE set2
set

#集合中追加数据
#追加时,只能追加不存在的数据,不能追加已经存在的数值
127.0.0.1:6379> SADD set1 v2 v3 v4
(integer) 3
127.0.0.1:6379> SADD set1 v2 #已存在的value,无法再次添加
(integer) 0
127.0.0.1:6379> TYPE set1
set
127.0.0.1:6379> TYPE set2
set

#获取集合的所有数据
127.0.0.1:6379> SMEMBERS set1
1) "v4"
2) "v1"
3) "v3"
4) "v2"
127.0.0.1:6379> SMEMBERS set2
1) "v4"
2) "v2"

#删除集合中的元素
127.0.0.1:6379> sadd goods mobile laptop car
(integer) 3
127.0.0.1:6379> srem goods car
(integer) 1
127.0.0.1:6379> SMEMBERS goods
1) "mobile"
2) "laptop"
127.0.0.1:6379>

#集合间操作
#取集合的交集
交集:同时属于集合A且属于集合B的元素
可以实现共同的朋友
127.0.0.1:6379> SINTER set1 set2
1) "v4"
2) "v2"

#取集合的并集
并集:属于集合A或者属于集合B的元素
127.0.0.1:6379> SUNION set1 set2
1) "v2"
2) "v4"
3) "v1"
4) "v3"

#取集合的差集
集:属于集合A但不属于集合B的元素
可以实现我的朋友的朋友
127.0.0.1:6379> SDIFF set1 set2
1) "v1"
2) "v3"

有序集合 sorted set

Redis有序集合和Redis集合类似,是不包含相同字符串的合集。它们的差别是,每个有序集合的成员都关联着一个双精度浮点型的评分,这个评分用于把有序集合中的成员按最低分到最高分排序。有序集合的成员不能重复,但评分可以重复,一个有序集合中最多的成员数为 2^32 – 1=4294967295个,经常用于排行榜的场景

有序集合特点:
有序
无重复元素
每个元素是由score和value组成
score 可以重复
value 不可以重复

#创建有序集合
127.0.0.1:6379> ZADD zset1 1 v1 #分数为1
(integer) 1
127.0.0.1:6379> ZADD zset1 2 v2
(integer) 1
127.0.0.1:6379> ZADD zset1 2 v3 #分数可重复,元素值不可以重复
(integer) 1
127.0.0.1:6379> ZADD zset1 3 v4
(integer) 1
127.0.0.1:6379> TYPE zset1
zset
127.0.0.1:6379> TYPE zset2
zset
#一次生成多个数据:
127.0.0.1:6379> ZADD zset2 1 v1 2 v2 3 v3 4 v4 5 v5
(integer) 5

#实现排名
127.0.0.1:6379> ZADD course 90 linux 99 go 60 python 50 cloud
(integer) 4
127.0.0.1:6379> ZRANGE course 0 -1 #正序排序后显示集合内所有的key,按score从小到大显示
1) "cloud"
2) "python"
3) "linux"
4) "go"
127.0.0.1:6379> ZREVRANGE course 0 -1 #倒序排序后显示集合内所有的key,score从大到小显示
1) "go"
2) "linux"
3) "python"
4) "cloud"
127.0.0.1:6379> ZRANGE course 0 -1 WITHSCORES #正序显示指定集合内所有key和得分情况
1) "cloud"
2) "50"
3) "python"
4) "60"
5) "linux"
6) "90"
7) "go"
8) "99"
127.0.0.1:6379> ZREVRANGE course 0 -1 WITHSCORES #倒序显示指定集合内所有key和得分情况
1) "go"
2) "99"
3) "linux"
4) "90"
5) "python"
6) "60"
7) "cloud"
8) "50"
127.0.0.1:6379>

#查看集合的成员个数
127.0.0.1:6379> ZCARD course
(integer) 4
127.0.0.1:6379> ZCARD zset1
(integer) 4
127.0.0.1:6379> ZCARD zset2
(integer) 4

#基于索引查找数据
127.0.0.1:6379> ZRANGE course 0 2
1) "cloud"
2) "python"
3) "linux"
127.0.0.1:6379> ZRANGE course 0 10 #超出范围不报错
1) "cloud"
2) "python"
3) "linux"
4) "go"
127.0.0.1:6379> ZRANGE zset1 1 3
1) "v2"
2) "v3"
3) "v4"
127.0.0.1:6379> ZRANGE zset1 0 2
1) "v1"
2) "v2"
3) "v3"
127.0.0.1:6379> ZRANGE zset1 2 2
1) "v3"

#查询指定数据的排名
127.0.0.1:6379> ZADD course 90 linux 99 go 60 python 50 cloud
(integer) 4
127.0.0.1:6379> ZRANK course go
(integer) 3 #第4个
127.0.0.1:6379> ZRANK course python
(integer) 1 #第2个

#获取分数
127.0.0.1:6379> zscore course cloud
"30"

#删除元素
127.0.0.1:6379> ZADD course 90 linux 199 go 60 python 30 cloud
(integer) 4
127.0.0.1:6379> ZRANGE course 0 -1
1) "cloud"
2) "python"
3) "linux"
4) "go"
127.0.0.1:6379> ZREM course python go
(integer) 2
127.0.0.1:6379> ZRANGE course 0 -1
1) "cloud"
2) "linux"

哈希 hash

hash 即字典, 用于保存字符串字段field和字符串值value之间的映射,即key/value做为数据部分,hash特别适合用于存储对象场景.
一个hash最多可以包含2^32-1 个key/value键值对
哈希特点:
无序
k/v 对
适用于存放相关的数据

命令复杂度
hgeto(1)
hexistso(1)
hincrbyo(1)
hgetall hvals hkeyso(1)
hmget hmseto(1)
# 创建 hash
HSET hash field value
时间复杂度: O(1)
将哈希表 hash 中域 field 的值设置为 value 。
如果给定的哈希表并不存在, 那么一个新的哈希表将被创建并执行 HSET 操作。
如果域 field 已经存在于哈希表中, 那么它的旧值将被新值 value 覆盖。

127.0.0.1:6379> HSET 9527 name zhouxingxing age 20
(integer) 2
127.0.0.1:6379> TYPE 9527
hash
#查看所有字段的值
127.0.0.1:6379> hgetall 9527
1) "name"
2) "zhouxingxing"
3) "age"
4) "20"
#增加字段
127.0.0.1:6379> HSET 9527 gender male
(integer) 1
127.0.0.1:6379> hgetall 9527
1) "name"
2) "zhouxingxing"
3) "age"
4) "20"
5) "gender"
6) "male"
#查看hash的指定field的value
127.0.0.1:6379> HGET 9527 name
"zhouxingxing"
127.0.0.1:6379> HGET 9527 age
"20"
127.0.0.1:6379> HMGET 9527 name age #获取多个值
1) "zhouxingxing"
2) "20"
127.0.0.1:6379>
#删除hash 的指定的 field/value
127.0.0.1:6379> HDEL 9527 age
(integer) 1
127.0.0.1:6379> HGET 9527 age
(nil)
127.0.0.1:6379> hgetall 9527
1) "name"
2) "zhouxingxing"
127.0.0.1:6379> HGET 9527 name
"zhouxingxing"
 #批量设置hash key的多个field和value
127.0.0.1:6379> HMSET 9527 name zhouxingxing age 50 city hongkong
OK
127.0.0.1:6379> HGETALL 9527
1) "name"
2) "zhouxingxing"
3) "age"
4) "50"
5) "city"
6) "hongkong"
#查看hash指定field的value
127.0.0.1:6379> HMSET 9527 name zhouxingxing age 50 city hongkong
OK
127.0.0.1:6379> HMGET 9527 name age
1) "zhouxingxing"
2) "50"
127.0.0.1:6379>
#查看hash的所有field
127.0.0.1:6379> HMSET 9527 name zhouxingxing age 50 city hongkong #重新设置
OK
127.0.0.1:6379> HKEYS 9527
1) "name"
2) "age"
3) "city"
#查看hash 所有value
127.0.0.1:6379> HMSET 9527 name zhouxingxing age 50 city hongkong
OK
127.0.0.1:6379> HVALS 9527
1) "zhouxingxing"
2) "50"
3) "hongkong"
#查看指定 hash的所有field及value
127.0.0.1:6379> HGETALL 9527
1) "name"
2) "zhouxingxing"
3) "age"
4) "50"
5) "city"
6) "hongkong"
127.0.0.1:6379>
#删除 hash
127.0.0.1:6379> DEL 9527
(integer) 1
127.0.0.1:6379> HMGET 9527 name city
1) (nil)
2) (nil)
127.0.0.1:6379> EXISTS 9527
(integer) 0

消息队列

消息队列: 把要传输的数据放在队列中,从而实现应用之间的数据交换
常用功能: 可以实现多个应用系统之间的解耦,异步,削峰/限流等
常用的消息队列应用: Kafka,RabbitMQ,Redis

消息队列分为两种
生产者/消费者模式: Producer/Consumer
发布者/订阅者模式: Publisher/Subscriber

生产者消费者模式

模式说明

生产者消费者模式下,多个消费者同时监听一个频道(redis用队列实现),但是生产者产生的一个消息只能被最先抢到消息的一个消费者消费一次,队列中的消息由可以多个生产者写入,也可以有不同的消费者取出进行消费处理.此模式应用广泛

生产者生成消息

[root@ubuntu2004 ~]#redis-cli
127.0.0.1:6379> AUTH 123456
OK
127.0.0.1:6379> LPUSH channel1 message1 
(integer) 1
127.0.0.1:6379> LPUSH channel1 message2 
(integer) 2
127.0.0.1:6379> LPUSH channel1 message3
(integer) 3
127.0.0.1:6379> LPUSH channel1 message4
(integer) 4
127.0.0.1:6379> LPUSH channel1 message5
(integer) 5

获取所有消息

127.0.0.1:6379> LRANGE channel1 0 -1
1) "message5"
2) "message4"
3) "message3"
4) "message2"
5) "message1"

消费者消费消息

127.0.0.1:6379> RPOP channel1 
"message1"
127.0.0.1:6379> RPOP channel1 
"message2"
127.0.0.1:6379> RPOP channel1 
"message3"
127.0.0.1:6379> RPOP channel1 
"message4"
127.0.0.1:6379> RPOP channel1 
"message5"
127.0.0.1:6379> RPOP channel1 
(nil)

验证队列消息消费完成

127.0.0.1:6379> LRANGE channel1 0 -1
(empty array)

发布者订阅模式

模式说明

在发布者订阅者Publisher/Subscriber模式下,发布者Publisher将消息发布到指定的频道channel,事先监听此channel的一个或多个订阅者Subscriber都会收到相同的消息。即一个消息可以由多个订阅者获取到. 对于社交应用中的群聊、群发、群公告等场景适用于此模式

订阅者订阅频道

[root@ubuntu2004 ~]#redis-cli
127.0.0.1:6379> AUTH 123456
OK
127.0.0.1:6379> SUBSCRIBE channel01
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "channel01"
3) (integer) 1

发布者发布消息

[root@ubuntu2004 ~]#redis-cli 
127.0.0.1:6379> auth 123456
OK
127.0.0.1:6379> PUBLISH channel01 message1
(integer) 1
127.0.0.1:6379> PUBLISH channel01 message2
(integer) 1
127.0.0.1:6379> 

各个订阅者都能收到消息

[root@ubuntu2004 ~]#redis-cli
127.0.0.1:6379> AUTH 123456
OK
127.0.0.1:6379> SUBSCRIBE channel01
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "channel01"
3) (integer) 1
1) "message"
2) "channel01"
3) "message1"
1) "message"
2) "channel01"
3) "message2"

订阅多个频道

#订阅指定的多个频道
127.0.0.1:6379> SUBSCRIBE channel01 channel02

订阅所有频道

127.0.0.1:6379> PSUBSCRIBE * #支持通配符*

订阅匹配的频道

127.0.0.1:6379> PSUBSCRIBE chann* #匹配订阅多个频道

取消订阅频道

127.0.0.1:6379> unsubscribe channel01
1) "unsubscribe"
2) "channel01"
3) (integer) 0