ithuang
ithuang
发布于 2025-10-11 / 3 阅读
0

非关系型数据库 Redis 基础

非关系型数据库 Redis 基础

一、背景描述及其方案设计

1. 业务背景描述

时间:2021.9.-2022.6

发布产品类型:互联网动态站点商城

用户数量: 60000(用户量猛增)

PV : 200000-300000(24小时访问次数总和)

DAU: 20000(每日活跃用户数)

2. 模拟运维设计方案

根据以上业务需求,准备加入Redis缓存中间件服务器,可以使用到Redis更加丰富的功能

在商城业务中实现:

① 实现活跃用户数的统计(使用set集合)

② Session存储到Redis

③ Openresty(Nginx+Lua)动态限制IP访问WAF(Web Application Firewalld)

二、Redis 概述

1. 什么是Redis

NoSQL 非关系数据库 key => value 键值对

Redis是Remote Dictionary Server(远程数据服务)的缩写

由意大利人Antirez(Salvatore Sanfilippo萨尔瓦托桑菲利波) 开发的一款内存高速缓存数据库

该软件使用C语言编写,它的数据模型为 key-value

它支持丰富的数据结构,比如 string list(双向链表) hash(哈希) set(集合) sorted set(zset有序集合)

可持久化(保存数据到磁盘中),保证了数据安全

2. 业务使用场合

**① [Sort Set]**排行榜应用,取top n操作,例如Sina微博热门话题(取最热的前10个话题)

**② [List]**获得最新N个数据 或 某个分类的最新数据

**③ [String]**计数器应用

**④ [Set]**SNS(social network site)获得共同好友

**⑤ [Set]**防攻击系统(IP判断)黑白名单等等

三、Redis 安装与配置

官方网址:https://redis.io/

github:https://github.com/antirez/redis

1. CentOS Stream 9 阿里源

cat >/etc/yum.repos.d/aliyun.repo<<EOF
[baseos]
name=CentOS Stream \$releasever - BaseOS
baseurl=https://mirrors.aliyun.com/centos-stream/\$stream/BaseOS/\$basearch/os/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
countme=1
enabled=1

[baseos-debug]
name=CentOS Stream \$releasever - BaseOS - Debug
baseurl=https://mirrors.aliyun.com/centos-stream/\$stream/BaseOS/\$basearch/debug/tree/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0

[baseos-source]
name=CentOS Stream \$releasever - BaseOS - Source
baseurl=https://mirrors.aliyun.com/centos-stream/\$stream/BaseOS/source/tree/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0

[appstream]
name=CentOS Stream \$releasever - AppStream
baseurl=https://mirrors.aliyun.com/centos-stream/\$stream/AppStream/\$basearch/os/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
countme=1
enabled=1

[appstream-debug]
name=CentOS Stream \$releasever - AppStream - Debug
baseurl=https://mirrors.aliyun.com/centos-stream/\$stream/AppStream/\$basearch/debug/tree/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0

[appstream-source]
name=CentOS Stream \$releasever - AppStream - Source
baseurl=https://mirrors.aliyun.com/centos-stream/\$stream/AppStream/\$basearch/debug/tree/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0

[crb]
name=CentOS Stream \$releasever - CRB
baseurl=https://mirrors.aliyun.com/centos-stream/\$stream/CRB/\$basearch/os/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
countme=1
enabled=0

[crb-debug]
name=CentOS Stream \$releasever - CRB - Debug
baseurl=https://mirrors.aliyun.com/centos-stream/\$stream/CRB/\$basearch/debug/tree/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0

[crb-source]
name=CentOS Stream \$releasever - CRB - Source
baseurl=https://mirrors.aliyun.com/centos-stream/\$stream/CRB/source/tree/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0

[highavailability]
name=CentOS Stream \$releasever - HighAvailability
baseurl=http://mirrors.aliyun.com/centos-stream/\$stream/HighAvailability/\$basearch/os/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
countme=1
enabled=0

[highavailability-debug]
name=CentOS Stream \$releasever - HighAvailability - Debug
baseurl=http://mirrors.aliyun.com/centos-stream/\$stream/HighAvailability/\$basearch/debug/tree/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0

[highavailability-source]
name=CentOS Stream \$releasever - HighAvailability - Source
baseurl=http://mirrors.aliyun.com/centos-stream/\$stream/HighAvailability/source/tree/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0

[nfv]
name=CentOS Stream \$releasever - NFV
baseurl=http://mirrors.aliyun.com/centos-stream/\$stream/NFV/\$basearch/os/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
countme=1
enabled=0

[nfv-debug]
name=CentOS Stream \$releasever - NFV - Debug
baseurl=http://mirrors.aliyun.com/centos-stream/\$stream/NFV/\$basearch/debug/tree/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0

[nfv-source]
name=CentOS Stream \$releasever - NFV - Source
baseurl=http://mirrors.aliyun.com/centos-stream/\$stream/NFV/source/tree/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0

[rt]
name=CentOS Stream \$releasever - RT
baseurl=http://mirrors.aliyun.com/centos-stream/\$stream/RT/\$basearch/os/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
countme=1
enabled=0

[rt-debug]
name=CentOS Stream \$releasever - RT - Debug
baseurl=http://mirrors.aliyun.com/centos-stream/\$stream/RT/\$basearch/debug/tree/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0

[rt-source]
name=CentOS Stream \$releasever - RT - Source
baseurl=http://mirrors.aliyun.com/centos-stream/\$stream/RT/source/tree/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0

[resilientstorage]
name=CentOS Stream \$releasever - ResilientStorage
baseurl=http://mirrors.aliyun.com/centos-stream/\$stream/ResilientStorage/\$basearch/os/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
countme=1
enabled=0

[resilientstorage-debug]
name=CentOS Stream \$releasever - ResilientStorage - Debug
baseurl=http://mirrors.aliyun.com/centos-stream/\$stream/ResilientStorage/\$basearch/debug/tree/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0

[resilientstorage-source]
name=CentOS Stream \$releasever - ResilientStorage - Source
baseurl=http://mirrors.aliyun.com/centos-stream/\$stream/ResilientStorage/source/tree/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0

[extras-common]
name=CentOS Stream \$releasever - Extras packages
baseurl=http://mirrors.aliyun.com/centos-stream/SIGs/\$stream/extras/\$basearch/extras-common/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-SIG-Extras-SHA512
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
countme=1
enabled=1

[extras-common-source]
name=CentOS Stream \$releasever - Extras packages - Source
baseurl=http://mirrors.aliyun.com/centos-stream/SIGs/\$stream/extras/source/extras-common/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-SIG-Extras-SHA512
gpgcheck=1
repo_gpgcheck=0
metadata_expire=6h
enabled=0
EOF

可以通过yum方式在线安装,也可以通过源码编译方式安装

这里,采用源码编译方式安装:

第一步:找到对应的安装包资源,使用wget命令下载,这里安装的7.4.0版本
安装包资源地址:https://download.redis.io/releases/

第二步:上传或者下载Redis到Linux系统中

wget https://download.redis.io/releases/redis-7.4.0.tar.gz

第三步:配置=>编译=>安装
tar -zxvf redis-7.4.0.tar.gz
cd redis-7.4.0
yum install epel-release -y
yum install make gcc jemalloc jemalloc-devel -y
make
make install PREFIX=/usr/local/redis 

第四步:配置环境变量
echo 'PATH=$PATH:/usr/local/redis/bin/' >> /etc/profile && source /etc/profile
redis-cli -v

安装成功后,Redis 的可执行文件将被安装到 /usr/local/redis

2. 修改配置

mkdir -p /usr/local/redis/conf
cp /root/redis-7.4.0/redis.conf /usr/local/redis/conf/

# 修改配置
vim /usr/local/redis/conf/redis.conf
88: bind 127.0.0.1 -::1  --> bind 0.0.0.0
310: daemonize no  --> daemonize yes

#过量使用内存设置为1
vm.overcommit_memory 的 3 种模式
0(默认)内核根据启发式算法决定是否允许超分配“看情况给,不一定准”
1允许内存超分配,不做任何限制,想分就分,不管你有没有这么多内存
2严格限制,不允许超分配非常安全,但可能导致程序申请内存失败

echo 'vm.overcommit_memory = 1' >> /etc/sysctl.conf

配置好后执行以下命令让配置生效
sysctl -p

3. 启动Redis

手工实现服务验证

#查看版本
redis-cli -v

#启动
redis-server /usr/local/redis/conf/redis.conf

#查看服务进程
ps aux | grep redis
ps -ef | grep redis
netstat -pantul|grep redis
netstat -pantul|grep 6379
ss -pantul|grep redis
ss -pantul|grep 6379

#登录
redis-cli
127.0.0.1:6379> info

配置redis.service

cat >/etc/systemd/system/redis.service<<EOF
[Unit]
Description=redis-server
After=network.target
 
[Service]
Type=forking
ExecStart=/usr/local/redis/bin/redis-server /usr/local/redis/conf/redis.conf
PrivateTmp=true
 
[Install]
WantedBy=multi-user.target
EOF

启动服务

pkill redis-server
systemctl daemon-reload
systemctl start redis
systemctl enable redis
systemctl status redis --no-pager

4. Redis命令行客户端简单使用

redis属于c/s架构软件,telnet可以连接redis,没有本身redis-cli更加好用

① 简单的数据操作

string类型:字符串类型、文本类型,用于保存文本信息

# redis-cli
127.0.0.1:6379 > set post-name devops
OK
127.0.0.1:6379 > get post-name
"devops"

② 查看操作语法帮助

127.0.0.1:6379 > help
127.0.0.1:6379 > help set

③ 系统状态信息

127.0.0.1:6379 > info
127.0.0.1:6379> info
# Server
redis_version:7.4.0
redis_git_sha1:00000000
redis_git_dirty:0
redis_build_id:b9206d9f7fbfa22a
redis_mode:standalone
os:Linux 5.14.0-511.el9.x86_64 x86_64
arch_bits:64
monotonic_clock:POSIX clock_gettime
multiplexing_api:epoll
atomicvar_api:c11-builtin
gcc_version:11.5.0
process_id:61047
process_supervised:no
run_id:ba3a3c88f38135718b012f77ac1eb8fba01810dd
tcp_port:6379
server_time_usec:1764558428231770
uptime_in_seconds:2622
uptime_in_days:0
hz:10
configured_hz:10
lru_clock:2950748
executable:/usr/local/redis/bin/redis-server
config_file:/usr/local/redis/conf/redis.conf
io_threads_active:0
listener0:name=tcp,bind=0.0.0.0,port=6379

# Clients
connected_clients:1
cluster_connections:0
maxclients:10000
client_recent_max_input_buffer:8
client_recent_max_output_buffer:0
blocked_clients:0
tracking_clients:0
pubsub_clients:0
watching_clients:0
clients_in_timeout_table:0
total_watched_keys:0
total_blocking_keys:0
total_blocking_keys_on_nokey:0

# Memory
used_memory:1095248
used_memory_human:1.04M
used_memory_rss:12165120
used_memory_rss_human:11.60M
used_memory_peak:1239632
used_memory_peak_human:1.18M
used_memory_peak_perc:88.35%
used_memory_overhead:948936
used_memory_startup:946208
used_memory_dataset:146312
used_memory_dataset_perc:98.17%
allocator_allocated:2616032
allocator_active:2842624
allocator_resident:5689344
allocator_muzzy:0
total_system_memory:1826394112
total_system_memory_human:1.70G
used_memory_lua:31744
used_memory_vm_eval:31744
used_memory_lua_human:31.00K
used_memory_scripts_eval:0
number_of_cached_scripts:0
number_of_functions:0
number_of_libraries:0
used_memory_vm_functions:32768
used_memory_vm_total:64512
used_memory_vm_total_human:63.00K
used_memory_functions:192
used_memory_scripts:192
used_memory_scripts_human:192B
maxmemory:0
maxmemory_human:0B
maxmemory_policy:noeviction
allocator_frag_ratio:1.07
allocator_frag_bytes:150560
allocator_rss_ratio:2.00
allocator_rss_bytes:2846720
rss_overhead_ratio:2.14
rss_overhead_bytes:6475776
mem_fragmentation_ratio:11.34
mem_fragmentation_bytes:11092168
mem_not_counted_for_evict:0
mem_replication_backlog:0
mem_total_replication_buffers:0
mem_clients_slaves:0
mem_clients_normal:1928
mem_cluster_links:0
mem_aof_buffer:0
mem_allocator:jemalloc-5.3.0
mem_overhead_db_hashtable_rehashing:0
active_defrag_running:0
lazyfree_pending_objects:0
lazyfreed_objects:0

# Persistence
loading:0
async_loading:0
current_cow_peak:0
current_cow_size:0
current_cow_size_age:0
current_fork_perc:0.00
current_save_keys_processed:0
current_save_keys_total:0
rdb_changes_since_last_save:2
rdb_bgsave_in_progress:0
rdb_last_save_time:1764555806
rdb_last_bgsave_status:ok
rdb_last_bgsave_time_sec:-1
rdb_current_bgsave_time_sec:-1
rdb_saves:0
rdb_last_cow_size:0
rdb_last_load_keys_expired:0
rdb_last_load_keys_loaded:0
aof_enabled:0
aof_rewrite_in_progress:0
aof_rewrite_scheduled:0
aof_last_rewrite_time_sec:-1
aof_current_rewrite_time_sec:-1
aof_last_bgrewrite_status:ok
aof_rewrites:0
aof_rewrites_consecutive_failures:0
aof_last_write_status:ok
aof_last_cow_size:0
module_fork_in_progress:0
module_fork_last_cow_size:0

# Stats
total_connections_received:2
total_commands_processed:8
instantaneous_ops_per_sec:0
total_net_input_bytes:218
total_net_output_bytes:432800
total_net_repl_input_bytes:0
total_net_repl_output_bytes:0
instantaneous_input_kbps:0.00
instantaneous_output_kbps:0.00
instantaneous_input_repl_kbps:0.00
instantaneous_output_repl_kbps:0.00
rejected_connections:0
sync_full:0
sync_partial_ok:0
sync_partial_err:0
expired_subkeys:0
expired_keys:0
expired_stale_perc:0.00
expired_time_cap_reached_count:0
expire_cycle_cpu_milliseconds:96
evicted_keys:0
evicted_clients:0
evicted_scripts:0
total_eviction_exceeded_time:0
current_eviction_exceeded_time:0
keyspace_hits:2
keyspace_misses:0
pubsub_channels:0
pubsub_patterns:0
pubsubshard_channels:0
latest_fork_usec:0
total_forks:0
migrate_cached_sockets:0
slave_expires_tracked_keys:0
active_defrag_hits:0
active_defrag_misses:0
active_defrag_key_hits:0
active_defrag_key_misses:0
total_active_defrag_time:0
current_active_defrag_time:0
tracking_total_keys:0
tracking_total_items:0
tracking_total_prefixes:0
unexpected_error_replies:0
total_error_replies:0
dump_payload_sanitizations:0
total_reads_processed:10
total_writes_processed:12
io_threaded_reads_processed:0
io_threaded_writes_processed:0
client_query_buffer_limit_disconnections:0
client_output_buffer_limit_disconnections:0
reply_buffer_shrinks:3
reply_buffer_expands:1
eventloop_cycles:25987
eventloop_duration_sum:10997368
eventloop_duration_cmd_sum:2580
instantaneous_eventloop_cycles_per_sec:9
instantaneous_eventloop_duration_usec:372
acl_access_denied_auth:0
acl_access_denied_cmd:0
acl_access_denied_key:0
acl_access_denied_channel:0

# Replication
role:master
connected_slaves:0
master_failover_state:no-failover
master_replid:11b588e942329b3f07bc77bc486c5c60be1a7919
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

# CPU
used_cpu_sys:1.296887
used_cpu_user:10.129402
used_cpu_sys_children:0.000000
used_cpu_user_children:0.000000
used_cpu_sys_main_thread:1.297919
used_cpu_user_main_thread:10.128088

# Modules

# Errorstats

# Cluster
cluster_enabled:0

# Keyspace
db0:keys=2,expires=0,avg_ttl=0,subexpiry=0
127.0.0.1:6379>

四、Redis 的 Key-Value

1. 16个逻辑库

默认在redis配置文件redis.conf中,提供了16个库,查看配置文件:

redis-cli

127.0.0.1:6379> config get databases
1) "databases"
2) "16"
127.0.0.1:6379>

数据库的编号都是从0开始,最大值为16-1=15

grep 16 /usr/local/redis/conf/redis.conf
[root@server2 ~]# redis-cli
127.0.0.1:6379> config get databases
1) "databases"
2) "16"
127.0.0.1:6379>
[root@server2 ~]# grep 16 /usr/local/redis/conf/redis.conf
# bind 192.168.1.100 10.0.0.1     # listens on two specific IPv4 addresses
databases 16
# -3: max size: 16 Kb  <-- probably not recommended
# 16 bytes header. When a HyperLogLog using the sparse representation crosses
# A value greater than 16000 is totally useless, since at that point the
# 16 megabytes / 10 seconds, the client will get disconnected immediately
# disconnected if the client reaches 16 megabytes and continuously overcomes
[root@server2 ~]#

Redis 默认配置文件 redis.conf 中设置了 16 个逻辑数据库(编号 0 至 15)。

它们是 Redis 服务端在单个实例内提供的 数据隔离空间,类似于“命名空间”。通过 SELECT 命令(如 SELECT 1)可在客户端切换当前操作的数据库,默认连接的是 第 0 号库。

不同库之间的 Key 相互独立,但共用同一实例的资源(如内存、CPU、端口),适用于简单场景下的数据分类存储。

 [root@server2 ~]# redis-cli
 
127.0.0.1:6379> select 0
OK
127.0.0.1:6379> select 1
OK
127.0.0.1:6379[1]> select 2
OK
127.0.0.1:6379[2]> select 3
OK
127.0.0.1:6379[3]>

2. Key 操作

NoSQL数据库,存储形式,键值对,键类似身份证号(不能重复,必须唯一)

key的命名规则不同于一般语言,键盘上除了空格、\n换行符外其他的大部分字符都可以使用。

但是像"my key"和"mykey\n"这样包含空格和换行的key是不允许的。

我们在使用的时候可以自己定义一个key的格式,但是要特别注意:

key不要太长,占内存,查询慢。

key不要太短,像u:1000:pwd:123456 就不如 user:1000:password:123456可读性好

非关系型数据库 Redis 基础1.png

2.1. ☆ 显示所有键 keys

# keys *
127.0.0.1:6379> set name ithaung
OK
127.0.0.1:6379> set age 25
OK
127.0.0.1:6379> keys *
1) "age"
2) "name"
127.0.0.1:6379>

2.2. ☆ 判断键是否存在 exists

# exists key 键名
存在1,不存在0
127.0.0.1:6379> exists key name
(integer) 1
127.0.0.1:6379> exists key age
(integer) 1
127.0.0.1:6379> exists key address
(integer) 0
127.0.0.1:6379>

2.3. ☆ 删除键 del

# del 键名
存在1,不存在0
127.0.0.1:6379> set name666 xiaoming
OK
127.0.0.1:6379> set name123 xiaohong
OK
127.0.0.1:6379> keys *
1) "name123"
2) "age"
3) "name666"
4) "name"
127.0.0.1:6379> del name123
(integer) 1
127.0.0.1:6379> del name666
(integer) 1
127.0.0.1:6379> keys *
1) "age"
2) "name"
127.0.0.1:6379>

2.4. ☆ 获取键类型 type

# type 键名
127.0.0.1:6379> keys *
1) "age"
2) "name"
127.0.0.1:6379> type key
none
127.0.0.1:6379> type name
string
127.0.0.1:6379> type age
string
127.0.0.1:6379>

2.5. ☆ 设置过期时间(单位s)expire

# set name test
# expire name 25

2.6. ☆ 查看剩余时间 ttl

# ttl name
127.0.0.1:6379> set name test
OK
127.0.0.1:6379> expire name 25
(integer) 1
127.0.0.1:6379> ttl name
(integer) 18
127.0.0.1:6379> ttl name
(integer) 14
127.0.0.1:6379> ttl name
(integer) 11
127.0.0.1:6379> ttl name
(integer) 9
127.0.0.1:6379> ttl name
(integer) 8
127.0.0.1:6379> ttl name
(integer) 7
127.0.0.1:6379> ttl name
(integer) -2
127.0.0.1:6379> ttl name
(integer) -2
127.0.0.1:6379>
返回值(整数)含义
正整数(如 3600)键存在,并且设置了过期时间,返回的是 剩余的秒数(例如 3600 表示还有 1 小时过期)。
-1键存在,但没有设置过期时间(永久有效)。
-2键不存在(可能从未设置过,或已过期并被自动删除)。

TTL是什么含义?

TTL(Time To Live),在计算机中,指数据(如数据包、缓存、键值对等)在系统或网络中允许存在的 “最大存活时长”。

除此之外,还有 EOL(End of Life,生命周期结束),指软件或硬件产品的 “官方支持的终止时间”,厂商不再提供更新、维护或技术支持。

3. 库操作

3.1. ☆ 查看当前库key数量 dbsize

# dbsize
127.0.0.1:6379> keys *
(empty array)
127.0.0.1:6379> dbsize
(integer) 0
127.0.0.1:6379> set name ithuang
OK
127.0.0.1:6379> dbsize
(integer) 1
127.0.0.1:6379> keys *
1) "name"
127.0.0.1:6379> set age 25
OK
127.0.0.1:6379> keys *
1) "age"
2) "name"
127.0.0.1:6379> dbsize
(integer) 2
127.0.0.1:6379>

3.2. ☆ 切换数据库 select

Redis 共16个库,index = number - 1

# select 0到15    # 下标从0到15
127.0.0.1:6379> client info
id=4 addr=127.0.0.1:42570 laddr=127.0.0.1:6379 fd=12 name= age=679 idle=0 flags=N db=0 sub=0 psub=0 ssub=0 multi=-1 watch=0 qbuf=26 qbuf-free=20448 argv-mem=10 multi-mem=0 rbs=1024 rbp=5 obl=0 oll=0 omem=0 tot-mem=22426 events=r cmd=client|info user=default redir=-1 resp=2 lib-name= lib-ver=
127.0.0.1:6379> select 1
OK
127.0.0.1:6379[1]> client info
id=4 addr=127.0.0.1:42570 laddr=127.0.0.1:6379 fd=12 name= age=683 idle=0 flags=N db=1 sub=0 psub=0 ssub=0 multi=-1 watch=0 qbuf=26 qbuf-free=20448 argv-mem=10 multi-mem=0 rbs=1024 rbp=5 obl=0 oll=0 omem=0 tot-mem=22426 events=r cmd=client|info user=default redir=-1 resp=2 lib-name= lib-ver=
127.0.0.1:6379[1]> select 2
OK
127.0.0.1:6379[2]> client info
id=4 addr=127.0.0.1:42570 laddr=127.0.0.1:6379 fd=12 name= age=687 idle=0 flags=N db=2 sub=0 psub=0 ssub=0 multi=-1 watch=0 qbuf=26 qbuf-free=20448 argv-mem=10 multi-mem=0 rbs=1024 rbp=5 obl=0 oll=0 omem=0 tot-mem=22426 events=r cmd=client|info user=default redir=-1 resp=2 lib-name= lib-ver=
127.0.0.1:6379[2]>

3.3. ☆ 清空当前库 flushdb

# flushdb
127.0.0.1:6379[2]> select 0
OK
127.0.0.1:6379> dbsize
(integer) 2
127.0.0.1:6379> keys *
1) "age"
2) "name"
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> keys *
(empty array)
127.0.0.1:6379>

3.4. ☆ 清空所有库 flushall

127.0.0.1:6379> flushall
OK
[root@redis ~]# redis-cli
127.0.0.1:6379> select 6
OK
127.0.0.1:6379[6]> keys *
1) "redis1"
127.0.0.1:6379[6]> select 7
OK
127.0.0.1:6379[7]> keys *
1) "test2"
2) "test"
127.0.0.1:6379[7]> select 0
OK
127.0.0.1:6379> flushall
OK
127.0.0.1:6379> select 6
OK
127.0.0.1:6379[6]> keys *
(empty array)
127.0.0.1:6379[6]> select 7
OK
127.0.0.1:6379[7]> keys *
(empty array)
127.0.0.1:6379[7]>

五、Redis 五大核心数据结构

1. string

string是redis最基本的类型

redis的string可以包含任何数据,包括jpg图片、base64编码或者序列化的对象

单个value值最大上限是512MB

如果只用string类型,redis就可以被看作加上持久化特性的memcached

非关系型数据库 Redis 基础2.png

☆ 设置与批量设置 set|mset

127.0.0.1:6379> set name itheima
OK
127.0.0.1:6379> get name
"itheima"
127.0.0.1:6379> mset name redis age 25 address canton
OK
127.0.0.1:6379> get name
"redis"
127.0.0.1:6379> keys *
1) "age"
2) "name"
3) "address"
127.0.0.1:6379> get age
"25"
127.0.0.1:6379> get address
"canton"
127.0.0.1:6379>

☆ 获取与批量获取 get|mget

127.0.0.1:6379> get name
"redis"
127.0.0.1:6379> mget name age address
1) "redis"
2) "25"
3) "canton"

☆ 增加与减少 incr|decr

# incr age        增加1
# decr age        减少1
    
# incrby age 2    增加2
# decrby age 3    减少3
127.0.0.1:6379> get age
"25"
127.0.0.1:6379> incr age
(integer) 26
127.0.0.1:6379> incr age
(integer) 27
127.0.0.1:6379> decr age
(integer) 26
127.0.0.1:6379> decr age
(integer) 25
127.0.0.1:6379> incrby age 2
(integer) 27
127.0.0.1:6379> decrby age 3
(integer) 24
127.0.0.1:6379> get age
"24"
127.0.0.1:6379>

☆ 追加 append

127.0.0.1:6379> append name 666
(integer) 8
127.0.0.1:6379> get name
"redis666"

☆ 截取 substr

# substr name start end

start:从哪里开始截取,默认从0,第一个字符开始
end:到哪里截取结束,必须要添加结束字符的索引号
127.0.0.1:6379> get name
"redis666"
127.0.0.1:6379> substr name 0 4
"redis"
127.0.0.1:6379> substr name 0 5
"redis6"
127.0.0.1:6379> substr name 5 7
"666"
127.0.0.1:6379>

小结

String 是 Redis 最基础、最常用的数据类型,本质是一个 binary-safe 的字节序列,可存储文本、数字或任何二进制数据。**单个字符串最大允许 512MB。**该类型性能极高,常用于缓存热点数据、分布式锁、计数器、限流器等高频场景,是 Redis 业务中使用最广泛的结构。

2. list

Redis 的 List 类型本质是一个双向链表,支持通过 LPUSH/RPUSH(头部/尾部插入)和 LPOP/RPOP(头部/尾部删除)操作灵活管理元素。

  • 栈功能:若仅从同一端进出(如都从头部操作:LPUSH+ LPOP),则遵循 后进先出(LIFO) 原则,类似栈结构。
  • 队列功能:若从一端进(如尾部 RPUSH)、另一端出(如头部 LPOP),则符合 先进先出(FIFO) 原则,类似队列(堆通常指优先级队列,此处更准确说是队列)。

这种双向操作特性使 List 既能当栈又能当队列,适用于消息队列、最新动态列表等场景。

非关系型数据库 Redis 基础3.png

首部(左 left) 尾部(右right) head tail

需求:显示最后登录的几个用户

设计实现:

① 登录一个用户,把用户名称或者id存储在list中

② 从左侧取第一个元素

特点:同一端进,同一端出(大部分以左为主)

用户:xiaohua xiaoming xiaobai

key名称:lastlogin

案例1:获取最后登录的用户

# 1. 首先清空测试环境(可选)
127.0.0.1:6379> DEL lastlogin
(integer) 1

# 2. 模拟用户登录 - 使用 LPUSH 从左侧添加
# 这样最新登录的用户总是在最前面
127.0.0.1:6379> LPUSH lastlogin xiaohua
(integer) 1
127.0.0.1:6379> LPUSH lastlogin xiaoming  
(integer) 2
127.0.0.1:6379> LPUSH lastlogin xiaobai
(integer) 3

# 3. 查看当前列表中的所有用户
# 语法:LRANGE key start stop
# 0表示第一个元素,-1表示最后一个元素
127.0.0.1:6379> LRANGE lastlogin 0 -1
1) "xiaobai"    # 最新登录的(最后push的)
2) "xiaoming"   # 中间登录的
3) "xiaohua"    # 最早登录的(最先push的)

# 4. 获取最后登录的2个用户
127.0.0.1:6379> LRANGE lastlogin 0 1
1) "xiaobai"
2) "xiaoming"

# 5. 再模拟一个新用户登录
127.0.0.1:6379> LPUSH lastlogin xiaohong
(integer) 4

# 6. 再次查看最新登录的2个用户
127.0.0.1:6379> LRANGE lastlogin 0 1
1) "xiaohong"   # 最新登录的
2) "xiaobai"    # 之前最新登录的

3. set(sadd)

Redis 的 set 是 无序且唯一的字符串集合(元素不重复,最大支持 2³²-1 个元素)。

除基础增删操作外,核心功能是通过 交集(intersection)、并集(union)、差集(difference) 快速处理多集合关系。set 是“数据结构”,而 sadd 是“操作 set 的命令”。

这些操作能高效实现 SNS 好友推荐(如求共同好友、交叉关注推荐),例如通过 SINTER找共同好友,SUNION合并好友圈,SDIFF分析差异好友。

案例1:朋友圈

设计:

key value

xiaomingFR xiaohong xiaoqiang xiaogang xiaobai xiaohei

xiaohongFR xiaoming xiaolv xiaolan xiaobai xiaohei

第一步:使用sadd添加xiaomingFR与xiaohongFR

127.0.0.1:6379> sadd xiaomingFR xiaohong xiaoqiang xiaogang xiaobai xiaohei
(integer) 5
127.0.0.1:6379> sadd xiaohongFR xiaoming xiaolv xiaolan xiaobai xiaohei
(integer) 5
127.0.0.1:6379> SMEMBERS xiaomingFR
1) "xiaohong"
2) "xiaoqiang"
3) "xiaogang"
4) "xiaobai"
5) "xiaohei"
127.0.0.1:6379> SMEMBERS xiaohongFR
1) "xiaoming"
2) "xiaolv"
3) "xiaolan"
4) "xiaobai"
5) "xiaohei"

第二步:求交集(共同好友)

127.0.0.1:6379> sinter xiaomingFR xiaohongFR
1) "xiaobai"
2) "xiaohei"
127.0.0.1:6379>

第三步:求并集(所有好友)

127.0.0.1:6379> sunion xiaomingFR xiaohongFR
1) "xiaohong"
2) "xiaoqiang"
3) "xiaogang"
4) "xiaobai"
5) "xiaohei"
6) "xiaoming"
7) "xiaolv"
8) "xiaolan"
127.0.0.1:6379>

第四步:求差集(互相推荐好友)

127.0.0.1:6379> sdiff xiaomingFR xiaohongFR
1) "xiaohong"
2) "xiaoqiang"
3) "xiaogang"
127.0.0.1:6379> sdiff xiaohongFR xiaomingFR
1) "xiaoming"
2) "xiaolv"
3) "xiaolan"
127.0.0.1:6379>

案例2:IP列表

# 向集合(ips)中添加多个IP地址成员(自动去重,重复IP不会重复存储)
# 命令:sadd 集合名 成员1 成员2 ...
sadd ips 10.1.1.11 10.1.1.12         # 将IP 10.1.1.11和10.1.1.12加入集合ips

# 检查指定IP是否为集合(ips)的成员(返回1表示存在,0表示不存在)
# 命令:sismember 集合名 成员
sismember ips 10.1.1.11              # 检查IP 10.1.1.11是否在集合ips中(预期返回1,存在)

# 检查另一个IP是否为集合(ips)的成员
sismember ips 10.1.1.100             # 检查IP 10.1.1.100是否在集合ips中(预期返回0,不存在)
127.0.0.1:6379> sadd ips 10.1.1.11 10.1.1.12
(integer) 2
127.0.0.1:6379> sismember ips 10.1.1.11
(integer) 1
127.0.0.1:6379> sismember ips 10.1.1.100
(integer) 0
127.0.0.1:6379> smembers ips
1) "10.1.1.11"
2) "10.1.1.12"

小结

set:无序,且天生去重

核心:求交集、并集、差集

4. zset(zadd)

Redis 的 Sorted Set(简称 ZSet)是一种 可排序的集合类型。 与普通 Set 一样,它保证 成员唯一性;与列表不同,它通过为每个成员关联一个 Score(浮点数权重) 来实现排序功能。 ZSet 内部使用 哈希表 + 跳表 组合结构,因此可以高效支持按 Score 排序、按范围查询、按排名称查询等操作。

核心特点:

  • **元素唯一性:**与 Set 相同,ZSet 中的每个 元素(成员/Value)必须唯一(例如不能同时存在两个相同的用户ID)。
  • **权重排序:**每个元素会关联一个 浮点数权值(Score),Redis 根据 Score 自动对所有元素排序(默认升序,也可降序查询)。
  • **灵活排序:**支持按 Score 范围、排名称范围查询,满足“有序且去重”的复杂需求。

ZSet 与 Set 的区别:

Set:仅存储唯一元素,无排序功能(如标签集合)。

ZSet:存储唯一元素 + 权重 Score,元素按 Score 自动排序(如排行榜)。

Score(分数/权重) 是 ZSet(有序集合)中用于排序的数值,它决定了元素在集合中的位置顺序。

案例1:实现手机APP市场的软件排名

key : hotTop

id score name

1 2 qq

2 3 wechat

3 5 alipay

4 7 taobao

5 10 mi

6 8 jd

第一步:插入数据

# zadd  hotTop  2 qq    3 wechat   5 alipay    7 taobao    10 mi   8 jd

第二步:排序,从小到大,qq -> wechat -> alipay -> taobao -> jd -> mi

# zrange hotTop 0 5

第三步:排序,从大到小,mi -> jd -> taobao -> alipay -> wecaht -> qq

# zrevrange hotTop 0 5

扩展:获取某个软件的score值

# zscore hotTop  jd
# zscore hotTop  taobao

扩展:更新某个软件的score值

# zincrby hotTop -2 jd #分数减2

# 从新打印前 5 名
# zrange hotTop 0 5
127.0.0.1:6379> zadd  hotTop  2 qq    3 wechat   5 alipay    7 taobao    10 mi   8 jd
(integer) 6
127.0.0.1:6379> zrange hotTop 0 5
1) "qq"
2) "wechat"
3) "alipay"
4) "taobao"
5) "jd"
6) "mi"
127.0.0.1:6379> zrevrange hotTop 0 5
1) "mi"
2) "jd"
3) "taobao"
4) "alipay"
5) "wechat"
6) "qq"
127.0.0.1:6379> zscore hotTop  jd
"8"
127.0.0.1:6379> zscore hotTop  taobao
"7"
127.0.0.1:6379> zscore hotTop  wechat
"3"
127.0.0.1:6379> zincrby hotTop -2 jd
"6"
127.0.0.1:6379> zscore hotTop  jd
"6"
127.0.0.1:6379> zrange hotTop 0 5
1) "qq"
2) "wechat"
3) "alipay"
4) "jd"
5) "taobao"
6) "mi"
127.0.0.1:6379>

小结

和set一样,zset也是集合的一种;不同点在于zset是有序集合,set是无序集合;共同点是元素唯一。

5. hash(哈希)

作用:使用Redis做缓存,除了可以使用string,还可以使用hash结构,比string压缩效率和使用效率更高。

hash存储数据和关系型数据库(mysql)存储的一条数据的结构极为相似

key:value(field:value)

insert into table(id,name,sex,address) values (1,'王维','男',18,'北京市昌平区')

id:1

name:'王维'

sex:'男'

age:18

address:'北京市昌平区'

☆ 设置hash数据

127.0.0.1:6379> hmset devops username redis age 18 email redis@itcast.cn

HMSET:一次性设置多个字段-值对
结构:
键:devops
字段:username → 值:redis
字段:age → 值:18
字段:email → 值:redis@itcast.cn

127.0.0.1:6379> hgetall devops # 查看多个值
127.0.0.1:6379> hmset devops username redis age 18 email redis@itcast.cn
OK
127.0.0.1:6379> hgetall devops
1) "username"
2) "redis"
3) "age"
4) "18"
5) "email"
6) "redis@itcast.cn"

☆ 获取的指定的field字段信息

127.0.0.1:6379> hget devops username
127.0.0.1:6379> hmget devops username age email
127.0.0.1:6379> hget devops username
"redis"
127.0.0.1:6379> hmget devops username age email
1) "redis"
2) "18"
3) "redis@itcast.cn"

☆ 更新devops的age字段信息

127.0.0.1:6379> hincrby devops age 1
127.0.0.1:6379> hmget devops username age
1) "redis"
2) "18"
127.0.0.1:6379> hincrby devops age 1
(integer) 19
127.0.0.1:6379> hget devops age
"19"

☆ 获取某个key的所有field信息

127.0.0.1:6379> hkeys devops
127.0.0.1:6379> hkeys devops
1) "username"
2) "age"
3) "email"

☆ 获取某个key的所有field的所有value信息

127.0.0.1:6379> hvals devops
127.0.0.1:6379> hkeys devops
1) "username"
2) "age"
3) "email"
127.0.0.1:6379> hvals devops
1) "redis"
2) "19"
3) "redis@itcast.cn"

☆ 删除指定的field字段

127.0.0.1:6379> hdel devops age
127.0.0.1:6379> hget devops age
"19"
127.0.0.1:6379> hdel devops age
(integer) 1
127.0.0.1:6379> hget devops age
(nil)

☆ 获取devops的key数量

127.0.0.1:6379> hlen devops
127.0.0.1:6379> hlen devops
(integer) 2
127.0.0.1:6379> hkeys devops
1) "username"
2) "email"

☆ 查询指定field是否存在

127.0.0.1:6379> hexists devops name
127.0.0.1:6379> hexists devops username
127.0.0.1:6379> hkeys devops
1) "username"
2) "email"
127.0.0.1:6379> hexists devops name
(integer) 0
127.0.0.1:6379> hexists devops username
(integer) 1

小结

Redis既可以作为存储也可以作为NoSQL数据库使用

存储数据时,如果数据与数据之间关联度不太大 => string

存储数据时,如果数据与数据之间关联度还比较大=> hash

相对而言,hash拥有更高的压缩比

总结

Redis 常说的 5 大数据类型:String、List、Set、ZSet、Hash 加上 3 个高级类型:Bitmap、HyperLogLog、Stream 总计:8 种常用数据结构

Redis 基础 5 大数据类型

类型说明常用场景
String字符串(包括数字、文本、二进制),最大 512MB缓存、计数器、分布式锁
Hash键值对集合,类似小型对象用户信息、商品信息
List双向链表队列、栈、消息流
Set无序集合、元素唯一去重、共同好友、标签
ZSet(Sorted Set)带分数的有序集合,按 score 排序排行榜、延时队列

Redis 扩展 3 大高级数据类型

类型说明常用场景
Bitmap位图,基于 String 的位操作签到、用户活跃统计
HyperLogLog基于概率的基数统计结构UV 统计(去重人数)
Stream消息流(类似 Kafka)消息队列、事件系统(Redis 5.0+)

六、Redis数据持久化操作(重点)

1. 基本概念

数据持久化(数据在服务或者软件重启之后不丢失)

如果数据只存在内存中,肯定会丢失,实现持久化,就需要把数据存储到磁盘中

非关系型数据库 Redis 基础4.png

2. RDB 持久化 - Snappshoting 快照

RDB(Redis DataBase)是 Redis 默认开启的持久化方式,通过 定时生成内存数据的二进制快照(snapshot) 将当前数据保存到磁盘文件(默认 dump.rdb),重启时直接加载快照恢复数据。

其特点是 恢复速度快、文件体积小,但可能存在最后一次快照之后的数据有丢失风险。

默认Snappshoting是开启的,而且有一个"拍照"的频率。

自动备份机制

触发条件:只有满足触发条件(指定时间内至少发生指定次数的增删改操作),才会自动备份!

优化Redis配置文件

[root@redis ~]# mv /usr/local/redis/conf/redis.conf /usr/local/redis/conf/redis.conf.bak
[root@redis ~]# grep -Ev "^$|#" /usr/local/redis/conf/redis.conf.bak > /usr/local/redis/conf/redis.conf
[root@redis ~]# cat /usr/local/redis/conf/redis.conf
bind 0.0.0.0
protected-mode yes
port 6379
tcp-backlog 511
timeout 0
tcp-keepalive 300
daemonize yes
pidfile /var/run/redis_6379.pid
loglevel notice
logfile ""
databases 16
always-show-logo no
set-proc-title yes
proc-title-template "{title} {listen-addr} {server-mode}"
locale-collate ""
stop-writes-on-bgsave-error yes
rdbcompression yes
rdbchecksum yes
dbfilename dump.rdb
rdb-del-sync-files no
dir /usr/local/redis
replica-serve-stale-data yes
replica-read-only yes
repl-diskless-sync yes
repl-diskless-sync-delay 5
repl-diskless-sync-max-replicas 0
repl-diskless-load disabled
repl-disable-tcp-nodelay no
replica-priority 100
acllog-max-len 128
lazyfree-lazy-eviction no
lazyfree-lazy-expire no
lazyfree-lazy-server-del no
replica-lazy-flush no
lazyfree-lazy-user-del no
lazyfree-lazy-user-flush no
oom-score-adj no
oom-score-adj-values 0 200 800
disable-thp yes
appendonly no
appendfilename "appendonly.aof"
appenddirname "appendonlydir"
appendfsync everysec
no-appendfsync-on-rewrite no
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
aof-load-truncated yes
aof-use-rdb-preamble yes
aof-timestamp-enabled no

slowlog-log-slower-than 10000
slowlog-max-len 128
latency-monitor-threshold 0
notify-keyspace-events ""
hash-max-listpack-entries 512
hash-max-listpack-value 64
list-max-listpack-size -2
list-compress-depth 0
set-max-intset-entries 512
set-max-listpack-entries 128
set-max-listpack-value 64
zset-max-listpack-entries 128
zset-max-listpack-value 64
hll-sparse-max-bytes 3000
stream-node-max-bytes 4096
stream-node-max-entries 100
activerehashing yes
client-output-buffer-limit normal 0 0 0
client-output-buffer-limit replica 256mb 64mb 60
client-output-buffer-limit pubsub 32mb 8mb 60
hz 10
dynamic-hz yes
aof-rewrite-incremental-fsync yes
rdb-save-incremental-fsync yes
jemalloc-bg-thread yes
[root@redis ~]#

配置文件/usr/local/redis/conf/redis.conf save配置说明

save 900 1       # 15分钟内(900秒)至少有1个key被修改 → 触发快照  
save 300 100     # 5分钟内(300秒)至少有100个key被修改 → 触发快照  
save 60 10000    # 1分钟内(60秒)至少有10000个key被修改 → 触发快照

三个条件满足任意一个即可触发快照(“或”关系),例如若 1 分钟内修改了 10001 个 key,则立即生成快照。
触发后,计时会重置。

下面的
    save 3600 1 300 100 60 10000
等价的分行写法
    save 3600   1
    save 300    100
    save 60     10000

可以自定义配置

# 语法:save  秒数  redis事务型操作(增删改)次数
save 10 2    # 10秒内至少2个写操作触发快照(原配置可注释或删除)
[root@redis ~]# echo 'save 10 2' >> /usr/local/redis/conf/redis.conf
[root@redis ~]# systemctl restart redis
[root@redis ~]# systemctl status redis

快照文件:

[root@redis ~]# grep dump.rdb /usr/local/redis/conf/redis.conf
dbfilename dump.rdb

从Redis6版本开始,引入了一个dir参数 => 代表默认保存路径,之前版本默认安装路径 => /usr/local/redis

[root@redis ~]# redis-cli
127.0.0.1:6379> config get dir
1) "dir"
2) "/"

可以查看默认保存路径

建议直接更改配置518行,数据存放路径
[root@redis ~]# vim /usr/local/redis/conf/redis.conf
dir /usr/local/redis

配置完成后,一定要记得重启redis

[root@redis ~]# systemctl restart redis
[root@redis ~]# systemctl status redis
[root@redis ~]# redis-cli
127.0.0.1:6379> config get dir
1) "dir"
2) "/usr/local/redis"
127.0.0.1:6379>

测试备份频率

在10s内,进行两个key的改变,查看备份效果:

[root@redis ~]# redis-cli
127.0.0.1:6379> set num1 1
OK
127.0.0.1:6379> set num2 2
OK
127.0.0.1:6379>

测试结果:查看dump.rdb文件大小的变化

[root@redis ~]# ll /usr/local/redis/
total 4
drwxr-xr-x. 2 root root 134 Nov 28 20:56 bin
drwxr-xr-x. 2 root root  46 Nov 29 16:17 conf
-rw-r--r--. 1 root root 109 Nov 29 16:18 dump.rdb

如果 11s 内只有1个key改变,会触发条件吗?

不会触发,仍使用上一次的快照文件。

如果这时,第 11s 时 Redis 宕机,从上次快照到宕机前的数据会丢失吗?

会丢失,这是 RDB 的固有缺点 —— 依赖 “触发条件 + 时间窗口”,可能丢失两次快照之间的增量数据。

总结

RDB 因 “条件触发” 特性,在未满足快照条件时发生宕机,会丢失上次快照后的所有数据。

即使只有 1 个 key 变更也不例外。

手动快照备份

127.0.0.1:6379> save 或 bgsave

127.0.0.1:6379> save
OK
127.0.0.1:6379> bgsave
Background saving started
127.0.0.1:6379>

☆ save

使用 save 的时候,在主程序中执行会 阻塞 当前 Redis 服务器,直到持久化工作完成。执行 save 命令期间, Redis 不能处理其他命令,所以生产上禁止使用!

☆ bgsave(Background Save)

Redis 会在后台(Background)异步对所有数据进行快照操作, 不阻塞快照同时还可以响应客户端请求。

该触发方式会 fork 一个子进程,由子进程复制持久化过程。

这个操作是子进程在后台完成的,这就允许父进程同时还可以修改数据。

☆ 总结

命令行为是否阻塞适用场景
save在主线程中同步执行快照,期间 Redis 完全阻塞(无法处理其他请求)✅ 阻塞禁止线上使用(仅测试或紧急情况)
bgsave在后台异步执行快照(fork 子进程处理),主线程 继续响应客户端请求❌ 不阻塞生产环境推荐(安全且高效)

lastsave

lastsave

可以通过lastsave命令获取最后一次成功的执行快照时间。

运行效果:

[root@redis ~]# redis-cli
127.0.0.1:6379> set num1 1
OK
127.0.0.1:6379> set num2 2
OK
127.0.0.1:6379> save
OK
127.0.0.1:6379> bgsave
Background saving started
127.0.0.1:6379> lastsave
(integer) 1764404398

获取一个看不懂的时间戳,需要转换一下

date -d @[redis内获取的时间戳]

比如
date -d @1764404398
[root@redis ~]# redis-cli
127.0.0.1:6379> set num1 1
OK
127.0.0.1:6379> set num2 2
OK
127.0.0.1:6379> save
OK
127.0.0.1:6379> bgsave
Background saving started
127.0.0.1:6379> lastsave
(integer) 1764404398
127.0.0.1:6379>
[root@redis ~]# date -d @1764404398
Sat Nov 29 04:19:58 PM CST 2025
[root@redis ~]# date -R
Sat, 29 Nov 2025 16:21:22 +0800
[root@redis ~]#

CST,China Standard Time 中国标准时间。

数据还原

[root@redis ~]# rm -rf * # 这个操作可选
[root@redis ~]# ll /usr/local/redis/
total 4
drwxr-xr-x. 2 root root 134 Nov 28 20:56 bin
drwxr-xr-x. 2 root root  46 Nov 29 16:17 conf
-rw-r--r--. 1 root root 109 Nov 29 16:39 dump.rdb
[root@redis ~]# redis-cli
127.0.0.1:6379> set test1 2025
OK
127.0.0.1:6379> set test2 2026
OK
127.0.0.1:6379> exit

触发自动备份机制

[root@redis ~]# ll /usr/local/redis/
total 4
drwxr-xr-x. 2 root root 134 Nov 28 20:56 bin
drwxr-xr-x. 2 root root  46 Nov 29 16:17 conf
-rw-r--r--. 1 root root 129 Nov 29 16:40 dump.rdb
[root@redis ~]# cp /usr/local/redis/dump.rdb ./
[root@redis ~]# ll
total 4
-rw-r--r--. 1 root root 129 Nov 29 16:40 dump.rdb
[root@redis ~]# redis-cli
127.0.0.1:6379> get test1
"2025"
127.0.0.1:6379> get test2
"2026"
127.0.0.1:6379> del test1 test2
(integer) 2
127.0.0.1:6379> get test1
(nil)
127.0.0.1:6379> get test2
(nil)
127.0.0.1:6379> exit
[root@redis ~]# ll /usr/local/redis/
total 4
drwxr-xr-x. 2 root root 134 Nov 28 20:56 bin
drwxr-xr-x. 2 root root  46 Nov 29 16:17 conf
-rw-r--r--. 1 root root 109 Nov 29 16:41 dump.rdb
[root@redis ~]# systemctl stop redis
[root@redis ~]# cp dump.rdb /usr/local/redis/
cp: overwrite '/usr/local/redis/dump.rdb'? y
[root@redis ~]# systemctl start redis
[root@redis ~]# redis-cli
127.0.0.1:6379> get test1
"2025"
127.0.0.1:6379> get test2
"2026"
127.0.0.1:6379>

RDB高级配置

☆ 关键配置参数 (redis.conf)

a) stop-writes-on-bgsave-error

默认:yes 如果配置成 no,表示不停止写,也就是允许在后台快照写入失败时,Redis 仍接受新写请求,不阻断服务。

b) rdbcompression

默认:yes

对于存储到磁盘中的快照,可以进行压缩存储。

如果是的话,redis会采用LZF算法进行压缩。

如果你不想消耗CPU来进行压缩的话,可以设置为关闭此功能。

c) rdbchecksum

默认:yes

在存储快照后,还可以让redis使用CRC64算法来进行数据校验。

但是这样做会增加大约10%的性能消耗,如果希望获取到最大的性能提升,可以关闭此功能。

d) rdb-del-sync-files

默认:no

默认情况下,保留那些没持久化的 RDB 文件,以防后面需要用。

☆ RDB快照触发场景 (了解)

除配置的 save 规则外,以下操作也会触发 RDB 快照:

  • 手动执行 save/bgsave 命令;
  • 执行 flushall 清空数据库并持久化(生成空 RDB 文件,无实际意义);
  • 主从复制时,主节点自动触发快照发送给从节点;
  • 执行 shutdown 且未开启 AOF 持久化时,自动保存最后一次快照。

☆ 禁用RDB快照

若需完全关闭 RDB,可通过以下方式:

# 方式1:命令行临时禁用(重启后恢复原配置)  
127.0.0.1:6379> config set save ""  

# 方式2:修改配置文件永久禁用  
vim /usr/local/redis/conf/redis.conf  
# 添加或修改:save ""  # 空引号表示禁用所有自动快照规则
127.0.0.1:6379> config get save
1) "save"
2) "10 2"
127.0.0.1:6379> config set save ""
OK
127.0.0.1:6379> config get save
1) "save"
2) ""

或者

总结

优点缺点
1. 恢复速度快:单文件加载比 AOF 日志重放更快(适合大规模数据恢复)。1. 可能丢数据:若 Redis 宕机发生在两次快照之间,会丢失最后一次快照后的所有变更(如 10 秒内 1 个 key 变更未触发快照)。
2. 文件紧凑:二进制格式存储,适合备份和传输(可按业务定时归档不同版本的 RDB)。2. 全量同步压力:大数据量时 fork 子进程可能导致瞬时 I/O 和 CPU 飙升(如 1 天修改 1000 次,同步数据量大)。
3. 适合容灾备份:可按小时/天备份多份 RDB 文件,支持恢复到不同时间点。3. fork 耗时风险:数据集极大时,fork 子进程可能阻塞主线程几毫秒至秒级(影响服务响应)。

3. AOF持久化(日志追加)

AOF(Append Only File) 是 Redis 的另一种持久化方式。

它不记录数据快照,而是记录所有写操作命令(读操作不记录),就像数据库的“操作日志”。

Redis 重启时,通过重新执行这些命令恢复数据。

类似 MySQL 的 BinLog 重放

核心特点:

  • 只追加不修改:AOF 文件仅允许在末尾追加新命令(增删改内容),不允许直接编辑或删除已有内容。
  • 数据恢复可靠:通过重放日志,能精确还原到崩溃前的状态(丢失数据量可控)。
  • 文件可读性强:命令以 Redis 协议格式存储,人类可读(如 SET key value)。

工作流程 (从命令到恢复)

步骤详解
①命令接收客户端发送的写操作(如 SET name "redis")先到达 Redis 服务端。
②缓存写入命令不会直接写入磁盘,而是先存入 AOF 缓存区(内存区域),避免频繁磁盘 I/O。
③策略同步AOF缓存会根据AOF缓存区 同步文件的三种写回策略 将命令写入磁盘上的AOF文件。
④文件优化随着 AOF 文件变大,Redis 会根据规则进行命令的合并( 又称AOF重写 ),从而起到AOF文件压缩的目的。
⑤重启恢复Redis 重启时,读取 AOF 文件 并 按顺序执行所有写命令,重建原始数据集。

Redis 当中,只要备份做的好,重启能解决 90% 以上的问题;

核心配置

☆ 开启 AOF

默认情况下,Redis 未开启 AOF,需手动修改配置文件(redis.conf):

vim /usr/local/redis/conf/redis.conf
appendonly yes  # 开启 AOF(原默认值为 no)

☆ 三种写回策略

三种写回策略小结:

☆ 文件位置与命名

Redis 6或之前版本: AOF 保存文件的位置和 RDB 保存文件的位置一样,都是通过 redis.conf 配置文件中的 dir 配置的。

Redis 7: redis.conf 配置文件中除了要设置dir以外,还新增加了一个属性appenddirname 。

AOF保存文件的位置:dir + appenddirname

[root@redis ~]# vim /usr/local/redis/conf/redis.conf

... 

515 # The Append Only File will also be created inside this directory.
516 #
517 # Note that you must specify a directory here, not a file name.
518 dir /usr/local/redis

...

1416 # Append-only file names are created by Redis following a specific pattern.
1417 # The file name's prefix is based on the 'appendfilename' configuration
1418 # parameter, followed by additional information about the sequence and type.
1419 #
1420 # For example, if appendfilename is set to appendonly.aof, the following file
1421 # names could be derived:
1422 #
1423 # - appendonly.aof.1.base.rdb as a base file.
1424 # - appendonly.aof.1.incr.aof, appendonly.aof.2.incr.aof as incremental files.
1425 # - appendonly.aof.manifest as a manifest file.
1426
1427 appendfilename "appendonly.aof"
1428
1429 # For convenience, Redis stores all persistent append-only files in a dedicated
1430 # directory. The name of the directory is determined by the appenddirname
1431 # configuration parameter.
1432
1433 appenddirname "appendonlydir"

# 记得重启redis
[root@redis ~]# systemctl restart redis 
[root@redis ~]# systemctl status redis

# 查看目录
[root@redis ~]# ll /usr/local/redis/appendonlydir
[root@redis ~]# ll /usr/local/redis/
total 4
drwxr-xr-x. 2 root root 134 Nov 28 20:56 bin
drwxr-xr-x. 2 root root  46 Nov 29 16:52 conf
-rw-r--r--. 1 root root 129 Nov 29 16:39 dump.rdb
[root@redis ~]# systemctl restart redis
[root@redis ~]# systemctl status redis
● redis.service - redis-server
     Loaded: loaded (/etc/systemd/system/redis.service; enabled; preset: disabl>
     Active: active (running) since Fri 2026-03-13 09:48:51 CST; 5s ago
    Process: 2029 ExecStart=/usr/local/redis/bin/redis-server /usr/local/redis/>
   Main PID: 2030 (redis-server)
      Tasks: 6 (limit: 22927)
     Memory: 7.3M
        CPU: 57ms
     CGroup: /system.slice/redis.service
             └─2030 "/usr/local/redis/bin/redis-server 0.0.0.0:6379"

Mar 13 09:48:51 redis systemd[1]: Starting redis-server...
Mar 13 09:48:51 redis systemd[1]: Started redis-server.
[root@redis ~]# ll /usr/local/redis/
total 4
drwxr-xr-x. 2 root root 103 Nov 29 16:52 appendonlydir
drwxr-xr-x. 2 root root 134 Nov 28 20:56 bin
drwxr-xr-x. 2 root root  46 Nov 29 16:52 conf
-rw-r--r--. 1 root root 129 Nov 29 16:39 dump.rdb

验证测试

[root@redis ~]# ll /usr/local/redis/
total 4
drwxr-xr-x. 2 root root 103 Nov 29 16:52 appendonlydir
drwxr-xr-x. 2 root root 134 Nov 28 20:56 bin
drwxr-xr-x. 2 root root  46 Nov 29 16:52 conf
-rw-r--r--. 1 root root 129 Nov 29 16:39 dump.rdb
[root@redis ~]# ll /usr/local/redis/appendonlydir/
total 8
-rw-r--r--. 1 root root 88 Nov 29 16:52 appendonly.aof.1.base.rdb
-rw-r--r--. 1 root root  0 Nov 29 16:52 appendonly.aof.1.incr.aof
-rw-r--r--. 1 root root 88 Nov 29 16:52 appendonly.aof.manifest

添加一些数据

redis-cli
127.0.0.1:6379 > set name devops
127.0.0.1:6379 > set age 25
127.0.0.1:6379 > set address canton

查看AOF文件

[root@redis ~]# ll /usr/local/redis/appendonlydir/
total 8
-rw-r--r--. 1 root root 88 Nov 29 16:52 appendonly.aof.1.base.rdb
-rw-r--r--. 1 root root  0 Nov 29 16:52 appendonly.aof.1.incr.aof
-rw-r--r--. 1 root root 88 Nov 29 16:52 appendonly.aof.manifest
[root@redis ~]# redis-cli
127.0.0.1:6379> set name devops
OK
127.0.0.1:6379> set age 25
OK
127.0.0.1:6379> set address canton
OK
127.0.0.1:6379> keys *
1) "address"
2) "age"
3) "name"
127.0.0.1:6379> exit
[root@redis ~]# ll /usr/local/redis/appendonlydir/
total 12
-rw-r--r--. 1 root root  88 Nov 29 16:52 appendonly.aof.1.base.rdb
-rw-r--r--. 1 root root 126 Nov 29 16:54 appendonly.aof.1.incr.aof
-rw-r--r--. 1 root root  88 Nov 29 16:52 appendonly.aof.manifest
[root@redis ~]#

数据恢复

[root@redis ~]# redis-cli
127.0.0.1:6379> set name devops
OK
127.0.0.1:6379> set age 25
OK
127.0.0.1:6379> set address canton
OK
127.0.0.1:6379> exit
[root@redis ~]# ll /usr/local/redis/appendonlydir/
total 12
-rw-r--r--. 1 root root  88 Nov 29 16:52 appendonly.aof.1.base.rdb
-rw-r--r--. 1 root root 126 Nov 29 16:54 appendonly.aof.1.incr.aof
-rw-r--r--. 1 root root  88 Nov 29 16:52 appendonly.aof.manifest
[root@redis ~]# cp /usr/local/redis/appendonlydir/appendonly.aof.1.incr.aof ./
[root@redis ~]# systemctl restart redis
[root@redis ~]# redis-cli
127.0.0.1:6379> get name
"devops"
127.0.0.1:6379> get age
"25"
127.0.0.1:6379> get address
"canton"
127.0.0.1:6379> del name age address
(integer) 3
127.0.0.1:6379> get name
(nil)
127.0.0.1:6379> get age
(nil)
127.0.0.1:6379> get address
(nil)
127.0.0.1:6379> exit
[root@redis ~]# systemctl stop redis
[root@redis ~]# ls
appendonly.aof.1.incr.aof  dump.rdb
[root@redis ~]# ll /usr/local/redis/appendonlydir/
total 12
-rw-r--r--. 1 root root  88 Nov 29 16:52 appendonly.aof.1.base.rdb
-rw-r--r--. 1 root root 194 Nov 29 16:58 appendonly.aof.1.incr.aof
-rw-r--r--. 1 root root  88 Nov 29 16:52 appendonly.aof.manifest
[root@redis ~]# cp appendonly.aof.1.incr.aof /usr/local/redis/appendonlydir/
cp: overwrite '/usr/local/redis/appendonlydir/appendonly.aof.1.incr.aof'? y
[root@redis ~]# systemctl start redis
[root@redis ~]# redis-cli
127.0.0.1:6379> keys *
1) "address"
2) "age"
3) "name"
127.0.0.1:6379> get name
"devops"
127.0.0.1:6379> get age
"25"
127.0.0.1:6379> get address
"canton"
127.0.0.1:6379> exit
[root@redis ~]#

appendonlydir目录下三个文件的作用对比
appendonly.aof.1.base.rdbRDB(二进制)基础数据快照包含某个时间点的完整数据镜像
appendonly.aof.1.incr.aofAOF(文本)增量操作日志记录 BASE 文件之后的写命令
appendonly.aof.manifestJSON(文本)文件清单和版本管理管理文件序列和加载顺序

总结

4. RDB vs AOF

特性RDB(快照)AOF(Append Only File)
数据形式内存数据的二进制快照(时间点备份)记录所有写操作命令(日志)
恢复方式直接加载快照文件重启时重放命令
数据安全性可能丢失最后一次快照后的所有数据可控丢失(默认每秒同步,最多丢 1 秒数据)
文件大小更紧凑(仅存数据状态)通常比 RDB 大(记录详细命令)
性能影响全量同步可能阻塞主线程(fork 压力)写操作需同步到磁盘(依赖策略,默认每秒同步性能较好)
适用场景对恢复速度要求高(如大数据量备份)对数据安全性要求高(如金融业务)

生产建议:AOF + RDB 混合使用(Redis 4.0+ 支持),兼顾数据安全与恢复效率!

5. RDB + AOF 混合模式

**RDB + AOF 混合模式,**指在同一 Redis 实例中既开启 RDB 快照,又开启 AOF 日志,结合两者优势:

  • RDB:提供 快速恢复(单文件加载效率高)和 全量数据备份(时间点快照)。
  • AOF:提供 数据高可靠性(记录所有写操作,丢失数据量更小)。

核心价值:

通过混合模式,既保留 RDB 的快速加载能力,又弥补 AOF 文件体积大、恢复慢的缺点,实现 “又快又安全” 的数据持久化。

☆ 共存优先级:AOF 优先于 RDB

翻译如下:

AOF 和 RDB 持久化可以同时启用,不会有问题。

如果启动时启用了 AOF,Redis 将加载 AOF ,即文件具有更好的持久性保证。则 AOF 的优先级高于 RDB。

☆ 数据恢复顺序及加载流程

同时开启 RDB 和 AOF 时, Redis 默认先优加载 AOF ,则不去加载 RDB (可以说和 RDB 毫无关系)。

如果 AOF 文件不存在,会刷新一个空的 AOF 配置,而忽略 RDB 文件。

☆ 开启 RDB 和 AOF 混合模式

结合了 RDB 和 AOF 的优点,既能快速加载又能避免丢失过多的数据。江湖人称“ 鸳鸯锅 ”

配置文件中 aof-use-rdb-preamble 的默认值为 yes 。 yes 表示开启,设置为 no 表示禁用。

用 RDB 快照做 “基础数据”,用 AOF 日志补 “最新操作”,既解决了纯 RDB 丢数据的问题,又解决了纯 AOF 恢复慢、文件大的问题,是现在 Redis 推荐的持久化搭配(默认开启)。

RDB+AOF 的混合方式

  1. RDB 镜像做全量持久化,AOF 做增量持久化。
  2. 先使用 RDB 进行快照存储,然后使用 AOF 持久化记录所有的写操作。
  3. 当重写策略满足或手动触发重写的时候,将最新的数据存储为新的 RDB 记录。

这样的话,重启服务的时候会从 RDB 和 AOF 两部分恢复数据,既保证了数据完整性,又提高了恢复数据的性能。

简单来说:

混合持久化方式产生的文件一部分是 RDB 格式,一部分是 AOF 格式。即 AOF 包括了 RDB 头部 + AOF 混写。

时间点恢复(了解)

https://www.percona.com/blog/perform-point-in-time-recovery-pitr-in-valkey-redis/

总结

掌握:学会开启与恢复 RDB、学会开启与恢复 AOF 即可!

  • RDB 适用于一般的数据持久化使用,效率高,数据迁移方便
  • AOF 适合于增量备份、数据实时性备份要求更高的情况

RDB 和 AOF 同时开启,Redis听谁的?

① 听 AOF 的,RDB 与 AOF 同时开启默认加载 AOF 的配置文件

② 相同数据集,AOF 文件要远大于 RDB 文件,恢复速度慢于 RDB

③ AOF 运行效率慢于 RDB,但是同步策略效率好,异步效率和 RDB 相同

概括:

生产环境都是混合模式居多,RDB 负责全量备份,AOF 适合增量备份,RDB 恢复数据快。

如果只能选一个:一般场景用 RDB,实时要求高场景用 AOF。

七、Redis 企业实战

1. Redis 主从复制

Redis 主从复制原理

1)第一次同步时,从库会把主库的数据整体拉取一份(全量同步)

主库会生成一个数据快照,并把这份快照发给从库,从库加载后与主库保持一致。

2)全量同步完成后,主库会把这期间发生的写操作再补发给从库

这样从库不会漏掉主库在同步过程中产生的新数据。

3)进入正常工作后,主库的每一条写操作都会实时推送给从库(增量同步)

从库按顺序执行这些写命令,从而与主库持续保持数据一致。

4)如果从库短暂断线,重新连接后会尽量只同步缺失的部分命令(部分同步)

避免每次都全量同步,提高效率。

5)主从之间维持长连接,通过心跳与偏移量确保同步状态正常

这样主库可以知道从库是否在线、是否落后。


注意:

主从复制使用的是“快照 + 写命令流”的方式,与 RDB/AOF 是否开启持久化无关,即使持久化关闭也不影响主从复制正常工作。

主从环境

主机名IPRedis版本角色
redis01192.168.88.101redis-7.4.0master
redis02192.168.88.102redis-7.4.0slave

优化Linux环境

sed -i -r 's/SELINUX=[ep].*/SELINUX=disabled/g' /etc/selinux/config
setenforce 0
systemctl stop firewalld &> /dev/null
systemctl disable firewalld &> /dev/null
iptables -F

改主机名

hostnamectl set-hostname redis01 && bash
hostnamectl set-hostname redis02 && bash
[root@redis01 ~]# redis-cli --version
redis-cli 7.4.0

准备redis02,需要提前安装Redis

第一步:找到对应的安装包资源,使用wget命令下载,这里安装的7.4.0版本。
安装包资源地址:https://download.redis.io/releases/

第二步:上传或者下载Redis到Linux系统中

wget https://download.redis.io/releases/redis-7.4.0.tar.gz

第三步:配置=>编译=>安装
tar -zxvf redis-7.4.0.tar.gz
cd redis-7.4.0
yum install epel-release -y
yum install make gcc jemalloc jemalloc-devel -y
make
make PREFIX=/usr/local/redis install

第四步:配置环境变量
echo 'PATH=$PATH:/usr/local/redis/bin/' >> /etc/profile && source /etc/profile

优化配置文件
mkdir /usr/local/redis/conf -p
cp redis.conf /usr/local/redis/conf

修改配置文件redis.conf
bind 0.0.0.0                                 # 允许所有IP连接
daemonize yes                                # 允许后台运行

创建 redis.service
cat >/etc/systemd/system/redis.service<<EOF
[Unit]
Description=redis-server
After=network.target
 
[Service]
Type=forking
ExecStart=/usr/local/redis/bin/redis-server /usr/local/redis/conf/redis.conf
PrivateTmp=true
 
[Install]
WantedBy=multi-user.target
EOF

过量使用内存设置为1
echo 'vm.overcommit_memory = 1' >> /etc/sysctl.conf
sysctl -p

systemctl daemon-reload
systemctl start redis
systemctl enable redis
systemctl status redis --no-pager
[root@redis02 ~]# redis-cli --version
redis-cli 7.4.0

建议清理一下数据,redis01和redis02都要执行flushall

redis-cli
flushall

操作之前,可以把redis01和redis02拍摄一个快照

第一步:配置 master 192.168.88.101

vim /usr/local/redis/conf/redis.conf

开启监听,在网络中与其他服务器进行网络交互的网卡

bind的ip指的是其他的主机需要和这个主机通讯的ip

bind 0.0.0.0           # 允许所有IP连接
protected-mode no      # 关闭redis安全保护机制,允许主从、哨兵、集群中各节点之间的相互访问,没有安全限制
logfile "/usr/local/redis/redis.log"     #日志文件存放路径

replica-read-only no

删除注释行和空行(可选)
[root@web01 conf]# vim redis.conf
:g/#/d   删除注释行
:g/^$/d  删除空行
[root@redis01 ~]# vim /usr/local/redis/conf/redis.conf
[root@redis01 ~]# cat /usr/local/redis/conf/redis.conf
bind 0.0.0.0
protected-mode no
port 6379
tcp-backlog 511
timeout 0
tcp-keepalive 300
daemonize yes
pidfile /var/run/redis_6379.pid
loglevel notice
logfile "/usr/local/redis/redis.log"
databases 16
always-show-logo no
set-proc-title yes
proc-title-template "{title} {listen-addr} {server-mode}"
locale-collate ""
stop-writes-on-bgsave-error yes
rdbcompression yes
rdbchecksum yes
dbfilename dump.rdb
rdb-del-sync-files no
dir /usr/local/redis
replica-serve-stale-data yes
replica-read-only no
repl-diskless-sync yes
repl-diskless-sync-delay 5
repl-diskless-sync-max-replicas 0
repl-diskless-load disabled
repl-disable-tcp-nodelay no
replica-priority 100
acllog-max-len 128
lazyfree-lazy-eviction no
lazyfree-lazy-expire no
lazyfree-lazy-server-del no
replica-lazy-flush no
lazyfree-lazy-user-del no
lazyfree-lazy-user-flush no
oom-score-adj no
oom-score-adj-values 0 200 800
disable-thp yes
appendonly yes
appendfilename "appendonly.aof"
appenddirname "appendonlydir"
appendfsync everysec
no-appendfsync-on-rewrite no
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
aof-load-truncated yes
aof-use-rdb-preamble yes
aof-timestamp-enabled no

slowlog-log-slower-than 10000
slowlog-max-len 128
latency-monitor-threshold 0
notify-keyspace-events ""
hash-max-listpack-entries 512
hash-max-listpack-value 64
list-max-listpack-size -2
list-compress-depth 0
set-max-intset-entries 512
set-max-listpack-entries 128
set-max-listpack-value 64
zset-max-listpack-entries 128
zset-max-listpack-value 64
hll-sparse-max-bytes 3000
stream-node-max-bytes 4096
stream-node-max-entries 100
activerehashing yes
client-output-buffer-limit normal 0 0 0
client-output-buffer-limit replica 256mb 64mb 60
client-output-buffer-limit pubsub 32mb 8mb 60
hz 10
dynamic-hz yes
aof-rewrite-incremental-fsync yes
rdb-save-incremental-fsync yes
jemalloc-bg-thread yes
save 10 2
[root@redis01 ~]# systemctl restart redis
[root@redis01 ~]# systemctl status redis --no-pager
● redis.service - redis-server
     Loaded: loaded (/etc/systemd/system/redis.service; enabled; preset: disabled)
     Active: active (running) since Sat 2025-11-29 17:52:57 CST; 17s ago
    Process: 3387 ExecStart=/usr/local/redis/bin/redis-server /usr/local/redis/conf/redis.conf (code=exited, status=0/SUCCESS)
   Main PID: 3388 (redis-server)
      Tasks: 6 (limit: 22927)
     Memory: 7.3M
        CPU: 91ms
     CGroup: /system.slice/redis.service
             └─3388 "/usr/local/redis/bin/redis-server 0.0.0.0:6379"

Nov 29 17:52:57 redis01 systemd[1]: Starting redis-server...
Nov 29 17:52:57 redis01 systemd[1]: Started redis-server.
[root@redis01 ~]#

第二步:配置 slave 192.168.88.102

vim /usr/local/redis/conf/redis.conf

主从配置:设置master redis信息

bind 0.0.0.0
protected-mode no
logfile "/usr/local/redis/redis.log"
replicaof 192.168.88.101 6379    # redis5/6/7 版本
[root@redis02 ~]# vim /usr/local/redis/conf/redis.conf
[root@redis02 ~]# cat /usr/local/redis/conf/redis.conf
bind 0.0.0.0
protected-mode no
port 6379
tcp-backlog 511
timeout 0
tcp-keepalive 300
daemonize yes
pidfile /var/run/redis_6379.pid
loglevel notice
logfile "/usr/local/redis/redis.log"
databases 16
always-show-logo no
set-proc-title yes
proc-title-template "{title} {listen-addr} {server-mode}"
locale-collate ""
stop-writes-on-bgsave-error yes
rdbcompression yes
rdbchecksum yes
dbfilename dump.rdb
rdb-del-sync-files no
dir ./
replica-serve-stale-data yes
replica-read-only yes
repl-diskless-sync yes
repl-diskless-sync-delay 5
repl-diskless-sync-max-replicas 0
repl-diskless-load disabled
repl-disable-tcp-nodelay no
replica-priority 100
replicaof 192.168.88.101 6379
acllog-max-len 128
lazyfree-lazy-eviction no
lazyfree-lazy-expire no
lazyfree-lazy-server-del no
replica-lazy-flush no
lazyfree-lazy-user-del no
lazyfree-lazy-user-flush no
oom-score-adj no
oom-score-adj-values 0 200 800
disable-thp yes
appendonly no
appendfilename "appendonly.aof"
appenddirname "appendonlydir"
appendfsync everysec
no-appendfsync-on-rewrite no
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
aof-load-truncated yes
aof-use-rdb-preamble yes
aof-timestamp-enabled no

slowlog-log-slower-than 10000
slowlog-max-len 128
latency-monitor-threshold 0
notify-keyspace-events ""
hash-max-listpack-entries 512
hash-max-listpack-value 64
list-max-listpack-size -2
list-compress-depth 0
set-max-intset-entries 512
set-max-listpack-entries 128
set-max-listpack-value 64
zset-max-listpack-entries 128
zset-max-listpack-value 64
hll-sparse-max-bytes 3000
stream-node-max-bytes 4096
stream-node-max-entries 100
activerehashing yes
client-output-buffer-limit normal 0 0 0
client-output-buffer-limit replica 256mb 64mb 60
client-output-buffer-limit pubsub 32mb 8mb 60
hz 10
dynamic-hz yes
aof-rewrite-incremental-fsync yes
rdb-save-incremental-fsync yes
jemalloc-bg-thread yes
[root@redis02 ~]# systemctl restart redis
[root@redis02 ~]# systemctl status redis --no-pager
● redis.service - redis-server
     Loaded: loaded (/etc/systemd/system/redis.service; enabled; preset: disab>
     Active: active (running) since Sat 2025-11-29 17:57:13 CST; 5s ago
    Process: 2591 ExecStart=/usr/local/redis/bin/redis-server /usr/local/redis>
   Main PID: 2593 (redis-server)
      Tasks: 6 (limit: 22927)
     Memory: 7.4M
        CPU: 48ms
     CGroup: /system.slice/redis.service
             └─2593 "/usr/local/redis/bin/redis-server 0.0.0.0:6379"

Nov 29 17:57:13 redis02 systemd[1]: Starting redis-server...
Nov 29 17:57:13 redis02 systemd[1]: Started redis-server.

第三步:重启Redis服务器,查看是否成功

redis-cli
127.0.0.1:6379> info replication
[root@redis01 ~]# redis-cli
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:1
slave0:ip=192.168.88.102,port=6379,state=online,offset=28,lag=1
master_failover_state:no-failover
master_replid:455f2585d7338f0828b40e38c9405ef57061d64a
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:28
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:28
127.0.0.1:6379>

[root@redis02 ~]# redis-cli
127.0.0.1:6379> info replication
# Replication
role:slave
master_host:192.168.88.101
master_port:6379
master_link_status:up
master_last_io_seconds_ago:0
master_sync_in_progress:0
slave_read_repl_offset:84
slave_repl_offset:84
slave_priority:100
slave_read_only:1
replica_announced:1
connected_slaves:0
master_failover_state:no-failover
master_replid:455f2585d7338f0828b40e38c9405ef57061d64a
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:84
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:84
127.0.0.1:6379>

第四步:测试Redis主从,主写从读

[root@redis01 ~]# redis-cli
127.0.0.1:6379> set title itheima
OK
127.0.0.1:6379> keys *
1) "title"
127.0.0.1:6379> get title
"itheima"
127.0.0.1:6379>

[root@redis02 ~]# redis-cli
127.0.0.1:6379> keys *
1) "title"
127.0.0.1:6379> get title
"itheima"
127.0.0.1:6379>

注:slave不允许写操作,因为从服务器Redis配置文件里进行了replica-read-only设置,也是符合业务的使用需求

replica-read-only yes

2. 安全限制

目前redis已经完成相关配置,缺少安全配置,如IP限制,密码限制。

==IP限制登录==

① master打开配置IP限制

注意如果有防火墙,先关闭防火墙或者开启端口放行。

bind ip 监听绑定网卡的IP

# vim /usr/local/redis/conf/redis.conf
bind 127.0.0.1 本机的网卡IP地址
bind 192.168.88.101 具体某个IP
bind 0.0.0.0 适合公网环境,没有限制,任意主机均可访问

重启Redis服务

② slave服务器进行远程连接测试

# redis-cli -h 远程IP地址
如果设置了密码也可以通过-a指定密码,密码会保留终端
[root@redis02 ~]# redis-cli -h 192.168.88.101
192.168.88.101:6379> info
# Server
redis_version:7.4.0
redis_git_sha1:00000000
redis_git_dirty:0
redis_build_id:1f85b0fd6901cd00
redis_mode:standalone
os:Linux 5.14.0-511.el9.x86_64 x86_64
arch_bits:64
monotonic_clock:POSIX clock_gettime
multiplexing_api:epoll
atomicvar_api:c11-builtin
gcc_version:11.5.0
process_id:8979
process_supervised:no
run_id:02bc1933766a3a0338274eb04b0a03f4530eab08
tcp_port:6379
server_time_usec:1764411380991945
uptime_in_seconds:37
uptime_in_days:0
hz:10
configured_hz:10
lru_clock:2803700
executable:/usr/local/redis/bin/redis-server
config_file:/usr/local/redis/conf/redis.conf
io_threads_active:0
listener0:name=tcp,bind=0.0.0.0,port=6379
...省略剩余输出信息...

③ master进行密码限制

1051行 # requirepass 密码
[root@redis01 ~]# vim /usr/local/redis/conf/redis.conf
[root@redis01 ~]# cat /usr/local/redis/conf/redis.conf
bind 0.0.0.0
protected-mode no
port 6379
tcp-backlog 511
timeout 0
tcp-keepalive 300
daemonize yes
pidfile /var/run/redis_6379.pid
loglevel notice
logfile "/usr/local/redis/redis.log"
databases 16
always-show-logo no
set-proc-title yes
proc-title-template "{title} {listen-addr} {server-mode}"
locale-collate ""
stop-writes-on-bgsave-error yes
rdbcompression yes
rdbchecksum yes
dbfilename dump.rdb
rdb-del-sync-files no
dir /usr/local/redis
replica-serve-stale-data yes
replica-read-only no
repl-diskless-sync yes
repl-diskless-sync-delay 5
repl-diskless-sync-max-replicas 0
repl-diskless-load disabled
repl-disable-tcp-nodelay no
replica-priority 100
acllog-max-len 128
lazyfree-lazy-eviction no
lazyfree-lazy-expire no
lazyfree-lazy-server-del no
replica-lazy-flush no
lazyfree-lazy-user-del no
lazyfree-lazy-user-flush no
oom-score-adj no
oom-score-adj-values 0 200 800
disable-thp yes
appendonly yes
appendfilename "appendonly.aof"
appenddirname "appendonlydir"
appendfsync everysec
no-appendfsync-on-rewrite no
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
aof-load-truncated yes
aof-use-rdb-preamble yes
aof-timestamp-enabled no

slowlog-log-slower-than 10000
slowlog-max-len 128
latency-monitor-threshold 0
notify-keyspace-events ""
hash-max-listpack-entries 512
hash-max-listpack-value 64
list-max-listpack-size -2
list-compress-depth 0
set-max-intset-entries 512
set-max-listpack-entries 128
set-max-listpack-value 64
zset-max-listpack-entries 128
zset-max-listpack-value 64
hll-sparse-max-bytes 3000
stream-node-max-bytes 4096
stream-node-max-entries 100
activerehashing yes
client-output-buffer-limit normal 0 0 0
client-output-buffer-limit replica 256mb 64mb 60
client-output-buffer-limit pubsub 32mb 8mb 60
hz 10
dynamic-hz yes
aof-rewrite-incremental-fsync yes
rdb-save-incremental-fsync yes
jemalloc-bg-thread yes
save 10 2
requirepass redis666
[root@redis01 ~]# systemctl restart redis
[root@redis01 ~]# systemctl status redis
● redis.service - redis-server
     Loaded: loaded (/etc/systemd/system/redis.service; enabled; preset: disab>
     Active: active (running) since Sat 2025-11-29 18:19:11 CST; 4s ago
    Process: 9567 ExecStart=/usr/local/redis/bin/redis-server /usr/local/redis>
   Main PID: 9569 (redis-server)
      Tasks: 6 (limit: 22927)
     Memory: 7.3M
        CPU: 870ms
     CGroup: /system.slice/redis.service
             └─9569 "/usr/local/redis/bin/redis-server 0.0.0.0:6379"

Nov 29 18:19:11 redis01 systemd[1]: Starting redis-server...
Nov 29 18:19:11 redis01 systemd[1]: Started redis-server.
[root@redis01 ~]#

重启redis服务,在slave服务器测试密码是否可用

[root@redis02 ~]# redis-cli -h 192.168.88.101
192.168.88.101:6379> info
NOAUTH Authentication required.
192.168.88.101:6379> auth redis666
OK

或者

[root@redis02 ~]# redis-cli -h 192.168.88.101 -a redis666
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
192.168.88.101:6379> info
# Server
redis_version:7.4.0
redis_git_sha1:00000000
redis_git_dirty:0
redis_build_id:1f85b0fd6901cd00
redis_mode:standalone
os:Linux 5.14.0-511.el9.x86_64 x86_64
arch_bits:64
...省略部分输出信息...
[root@redis02 ~]# redis-cli -h 192.168.88.101
192.168.88.101:6379> info
NOAUTH Authentication required.
192.168.88.101:6379> auth redis666
OK
192.168.88.101:6379> info
# Server
redis_version:7.4.0
redis_git_sha1:00000000
redis_git_dirty:0
redis_build_id:1f85b0fd6901cd00
redis_mode:standalone
os:Linux 5.14.0-511.el9.x86_64 x86_64
arch_bits:64
monotonic_clock:POSIX clock_gettime
multiplexing_api:epoll
atomicvar_api:c11-builtin
gcc_version:11.5.0
process_id:9569
process_supervised:no
run_id:35ba011debf3d9676b3497239481e5d199c1da06
tcp_port:6379
server_time_usec:1764411590255650
uptime_in_seconds:39
uptime_in_days:0
hz:10
configured_hz:10
lru_clock:2803910
executable:/usr/local/redis/bin/redis-server
config_file:/usr/local/redis/conf/redis.conf
io_threads_active:0
listener0:name=tcp,bind=0.0.0.0,port=6379
...省略剩余输出信息...

[root@redis02 ~]# redis-cli -h 192.168.88.101 -a redis666
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
192.168.88.101:6379> info
# Server
redis_version:7.4.0
redis_git_sha1:00000000
redis_git_dirty:0
redis_build_id:1f85b0fd6901cd00
redis_mode:standalone
os:Linux 5.14.0-511.el9.x86_64 x86_64
arch_bits:64
monotonic_clock:POSIX clock_gettime
multiplexing_api:epoll
atomicvar_api:c11-builtin
gcc_version:11.5.0
process_id:9569
process_supervised:no
run_id:35ba011debf3d9676b3497239481e5d199c1da06
tcp_port:6379
server_time_usec:1764411651816602
uptime_in_seconds:100
uptime_in_days:0
hz:10
configured_hz:10
lru_clock:2803971
executable:/usr/local/redis/bin/redis-server
config_file:/usr/local/redis/conf/redis.conf
io_threads_active:0
listener0:name=tcp,bind=0.0.0.0,port=6379
...省略剩余输出信息...

注意:如果开启了密码限制,搭建主从需要在slave配置中填写master密码

# vim /usr/local/redis/conf/redis.conf
547行:masterauth 密码
[root@redis02 ~]# vim /usr/local/redis/conf/redis.conf
[root@redis02 ~]# cat /usr/local/redis/conf/redis.conf
bind 0.0.0.0
protected-mode no
port 6379
tcp-backlog 511
timeout 0
tcp-keepalive 300
daemonize yes
pidfile /var/run/redis_6379.pid
loglevel notice
logfile ""
databases 16
always-show-logo no
set-proc-title yes
proc-title-template "{title} {listen-addr} {server-mode}"
locale-collate ""
stop-writes-on-bgsave-error yes
rdbcompression yes
rdbchecksum yes
dbfilename dump.rdb
rdb-del-sync-files no
dir ./
replica-serve-stale-data yes
replica-read-only yes
repl-diskless-sync yes
repl-diskless-sync-delay 5
repl-diskless-sync-max-replicas 0
repl-diskless-load disabled
repl-disable-tcp-nodelay no
replica-priority 100
replicaof 192.168.88.101 6379
acllog-max-len 128
lazyfree-lazy-eviction no
lazyfree-lazy-expire no
lazyfree-lazy-server-del no
replica-lazy-flush no
lazyfree-lazy-user-del no
lazyfree-lazy-user-flush no
oom-score-adj no
oom-score-adj-values 0 200 800
disable-thp yes
appendonly no
appendfilename "appendonly.aof"
appenddirname "appendonlydir"
appendfsync everysec
no-appendfsync-on-rewrite no
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
aof-load-truncated yes
aof-use-rdb-preamble yes
aof-timestamp-enabled no

slowlog-log-slower-than 10000
slowlog-max-len 128
latency-monitor-threshold 0
notify-keyspace-events ""
hash-max-listpack-entries 512
hash-max-listpack-value 64
list-max-listpack-size -2
list-compress-depth 0
set-max-intset-entries 512
set-max-listpack-entries 128
set-max-listpack-value 64
zset-max-listpack-entries 128
zset-max-listpack-value 64
hll-sparse-max-bytes 3000
stream-node-max-bytes 4096
stream-node-max-entries 100
activerehashing yes
client-output-buffer-limit normal 0 0 0
client-output-buffer-limit replica 256mb 64mb 60
client-output-buffer-limit pubsub 32mb 8mb 60
hz 10
dynamic-hz yes
aof-rewrite-incremental-fsync yes
rdb-save-incremental-fsync yes
jemalloc-bg-thread yes
masterauth redis666
[root@redis02 ~]# systemctl restart redis
[root@redis02 ~]# systemctl status redis
● redis.service - redis-server
     Loaded: loaded (/etc/systemd/system/redis.service; enabled; preset: disab>
     Active: active (running) since Sat 2025-11-29 18:21:59 CST; 4s ago
    Process: 6672 ExecStart=/usr/local/redis/bin/redis-server /usr/local/redis>
   Main PID: 6674 (redis-server)
      Tasks: 6 (limit: 22927)
     Memory: 7.4M
        CPU: 37ms
     CGroup: /system.slice/redis.service
             └─6674 "/usr/local/redis/bin/redis-server 0.0.0.0:6379"

Nov 29 18:21:59 redis02 systemd[1]: Starting redis-server...
Nov 29 18:21:59 redis02 systemd[1]: Started redis-server.
[root@redis02 ~]#

3. PHP Redis(拓展)

项目:LNMP(PHP) => .so扩展文件 => Redis

源码安装LNMP,要参考下面的代码,手工编译安装redis.so扩展

第一步:安装redis.so扩展

https://pecl.php.net/package/redis

[root@server3 ~]# wget https://pecl.php.net/get/redis-5.3.3.tgz
--2025-11-29 18:29:29--  https://pecl.php.net/get/redis-5.3.3.tgz
Resolving pecl.php.net (pecl.php.net)... 185.85.0.29, 2a02:cb40:200::1ad
Connecting to pecl.php.net (pecl.php.net)|185.85.0.29|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 266990 (261K) [application/octet-stream]
Saving to: ‘redis-5.3.3.tgz’

redis-5.3.3.tgz     100%[==================>] 260.73K   337KB/s    in 0.8s

2025-11-29 18:29:31 (337 KB/s) - ‘redis-5.3.3.tgz’ saved [266990/266990]

[root@server3 ~]# ls -hl
total 268K
-rw-------. 1 root root  984 Jul 18 15:57 anaconda-ks.cfg
-rw-r--r--. 1 root root 261K Feb  2  2021 redis-5.3.3.tgz
[root@server3 ~]# tar xf redis-5.3.3.tgz
[root@server3 ~]# ls
anaconda-ks.cfg  package.xml  redis-5.3.3  redis-5.3.3.tgz
[root@server3 ~]# cd redis-5.3.3/
[root@server3 redis-5.3.3]# ls
arrays.markdown    CREDITS           redis_array_impl.c  redis_session.c
cluster_library.c  INSTALL.markdown  redis_array_impl.h  redis_session.h
cluster_library.h  liblzf            redis.c             sentinel_library.c
cluster.markdown   library.c         redis_cluster.c     sentinel_library.h
common.h           library.h         redis_cluster.h     sentinel.markdown
config.m4          php_redis.h       redis_commands.c    tests
config.w32         README.markdown   redis_commands.h
COPYING            redis_array.c     redis_sentinel.c
crc16.h            redis_array.h     redis_sentinel.h
[root@server3 redis-5.3.3]# yum install -y gcc make autoconf php-devel php-pear php-fpm
[root@server3 redis-5.3.3]# which phpize
/usr/bin/phpize
[root@server3 redis-5.3.3]# /usr/bin/phpize
Configuring for:
PHP Api Version:         20200930
Zend Module Api No:      20200930
Zend Extension Api No:   420200930
[root@server3 redis-5.3.3]# ./configure --with-php-config=/usr/bin/php-config --enable-fpm
[root@server3 redis-5.3.3]# make
...省略部分输出信息...
If you ever happen to want to link against installed libraries
in a given directory, LIBDIR, you must either use libtool, and
specify the full pathname of the library, or use the '-LLIBDIR'
flag during linking and do at least one of the following:
   - add LIBDIR to the 'LD_LIBRARY_PATH' environment variable
     during execution
   - add LIBDIR to the 'LD_RUN_PATH' environment variable
     during linking
   - use the '-Wl,-rpath -Wl,LIBDIR' linker flag
   - have your system administrator add LIBDIR to '/etc/ld.so.conf'

See any operating system documentation about shared libraries for
more information, such as the ld(1) and ld.so(8) manual pages.
----------------------------------------------------------------------

Build complete.
Don't forget to run 'make test'.

[root@server3 redis-5.3.3]# make install
Installing shared extensions:     /usr/lib64/php/modules/
[root@server3 redis-5.3.3]# php -v
PHP 8.0.30 (cli) (built: Oct  3 2025 08:25:46) ( NTS gcc x86_64 )
Copyright (c) The PHP Group
Zend Engine v4.0.30, Copyright (c) Zend Technologies
[root@server3 redis-5.3.3]# systemctl start php-fpm
[root@server3 redis-5.3.3]# systemctl status php-fpm
● php-fpm.service - The PHP FastCGI Process Manager
     Loaded: loaded (/usr/lib/systemd/system/php-fpm.service; disabled; preset>
     Active: active (running) since Sat 2025-11-29 19:11:24 CST; 1s ago
   Main PID: 38930 (php-fpm)
     Status: "Ready to handle connections"
      Tasks: 6 (limit: 22927)
     Memory: 9.2M
        CPU: 57ms
     CGroup: /system.slice/php-fpm.service
             ├─38930 "php-fpm: master process (/etc/php-fpm.conf)"
             ├─38931 "php-fpm: pool www"
             ├─38932 "php-fpm: pool www"
             ├─38933 "php-fpm: pool www"
             ├─38934 "php-fpm: pool www"
             └─38935 "php-fpm: pool www"

Nov 29 19:11:24 server3 systemd[1]: Starting The PHP FastCGI Process Manager...
Nov 29 19:11:24 server3 systemd[1]: Started The PHP FastCGI Process Manager.
[root@server3 redis-5.3.3]# php -m|grep redis
[root@server3 redis-5.3.3]#

第二步:配置php.ini

在最后一行添加如下内容
vim /etc/php.ini  
extension = redis.so
[root@server3 redis-5.3.3]# vim /etc/php.ini
[root@server3 redis-5.3.3]# tail -n 1 /etc/php.ini
extension = redis.so
[root@server3 redis-5.3.3]#

优化php.ini配置文件

[root@server3 redis-5.3.3]# cp /etc/php.ini /etc/php.ini.bak
[root@server3 redis-5.3.3]# sed -i '/^\s*;/d; /^$/d' /etc/php.ini
[root@server3 redis-5.3.3]# cat /etc/php.ini
[PHP]
engine = On
short_open_tag = Off
precision = 14
output_buffering = 4096
zlib.output_compression = Off
implicit_flush = Off
unserialize_callback_func =
serialize_precision = -1
disable_functions =
disable_classes =
zend.enable_gc = On
zend.exception_ignore_args = On
zend.exception_string_param_max_len = 0
expose_php = On
max_execution_time = 30
max_input_time = 60
memory_limit = 128M
error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT
display_errors = Off
display_startup_errors = Off
log_errors = On
log_errors_max_len = 1024
ignore_repeated_errors = Off
ignore_repeated_source = Off
report_memleaks = On
variables_order = "GPCS"
request_order = "GP"
register_argc_argv = Off
auto_globals_jit = On
post_max_size = 8M
auto_prepend_file =
auto_append_file =
default_mimetype = "text/html"
default_charset = "UTF-8"
doc_root =
user_dir =
enable_dl = Off
file_uploads = On
upload_max_filesize = 2M
max_file_uploads = 20
allow_url_fopen = On
allow_url_include = Off
default_socket_timeout = 60
[CLI Server]
cli_server.color = On
[Date]
[filter]
[iconv]
[imap]
[intl]
[sqlite3]
[Pcre]
pcre.jit=0
[Pdo]
[Pdo_mysql]
pdo_mysql.default_socket=
[Phar]
[mail function]
sendmail_path = /usr/sbin/sendmail -t -i
mail.add_x_header = Off
[ODBC]
odbc.allow_persistent = On
odbc.check_persistent = On
odbc.max_persistent = -1
odbc.max_links = -1
odbc.defaultlrl = 4096
odbc.defaultbinmode = 1
[MySQLi]
mysqli.max_persistent = -1
mysqli.allow_persistent = On
mysqli.max_links = -1
mysqli.default_port = 3306
mysqli.default_socket =
mysqli.default_host =
mysqli.default_user =
mysqli.default_pw =
mysqli.reconnect = Off
[mysqlnd]
mysqlnd.collect_statistics = On
mysqlnd.collect_memory_statistics = Off
[PostgreSQL]
pgsql.allow_persistent = On
pgsql.auto_reset_persistent = Off
pgsql.max_persistent = -1
pgsql.max_links = -1
pgsql.ignore_notice = 0
pgsql.log_notice = 0
[bcmath]
bcmath.scale = 0
[browscap]
[Session]
session.save_handler = files
session.use_strict_mode = 0
session.use_cookies = 1
session.use_only_cookies = 1
session.name = PHPSESSID
session.auto_start = 0
session.cookie_lifetime = 0
session.cookie_path = /
session.cookie_domain =
session.cookie_httponly =
session.cookie_samesite =
session.serialize_handler = php
session.gc_probability = 1
session.gc_divisor = 1000
session.gc_maxlifetime = 1440
session.referer_check =
session.cache_limiter = nocache
session.cache_expire = 180
session.use_trans_sid = 0
session.sid_length = 26
session.trans_sid_tags = "a=href,area=href,frame=src,form="
session.sid_bits_per_character = 5
[Assertion]
zend.assertions = -1
[mbstring]
[gd]
[exif]
[Tidy]
tidy.clean_output = Off
[soap]
soap.wsdl_cache_enabled=1
soap.wsdl_cache_dir="/tmp"
soap.wsdl_cache_ttl=86400
soap.wsdl_cache_limit = 5
[sysvshm]
[ldap]
ldap.max_links = -1
[dba]
[opcache]
[curl]
[openssl]
[ffi]
extension = redis.so
[root@server3 redis-5.3.3]#

sed -i '/^\s*;/d; /^$/d' /etc/php.ini 命令解析

  • sed -i:-i 选项表示 "in-place"(原地修改),直接对 /etc/php.ini 文件进行修改。

  • /^\s*;/d:

    • ^: 匹配行首。
    • \s*: 匹配零个或多个空格或制表符(处理行首有空格的注释)。
    • ;: 匹配分号。
    • d: 删除匹配到的行。
    • 作用: 删除所有以分号开头的注释行,包括前面有空格的注释。
  • /^$/d:

    • ^$: 匹配空行(行首紧跟行尾)。
    • d: 删除匹配到的行。
    • 作用: 删除所有空行。

第三步:重启php-fpm

# systemctl restart php-fpm
# php -m #使用命令查看是否有对应的redis模块

第四步:测试php redis是否安装成功

[root@server3 redis-5.3.3]# systemctl restart php-fpm
[root@server3 redis-5.3.3]# php -m|grep redis
redis

4. Redis 应用场景:缓存与 Session 存入 Redis(了解)

缓存设计:减少Web对MySQL的访问,减轻数据库压力

与之前session存储到file文件的方式不同,将session存储到redis中,可以实现session的共享以及单点登录(sso)的操作。

www.baidu.com

pan.baidu.com

image.baidu.com

music.baidu.com

① 更改缓存保存方式

vim /data/CRMEB/crmeb/config/cache.php
return [
    // 默认缓存驱动
    'default' => Env::get('cache.driver', 'redis'),  //修改这里

    // 缓存连接方式配置
    'stores'  => [
        'file' => [
            // 驱动方式
            'type'       => 'File',
            // 缓存保存目录
            'path'       => app()->getRuntimePath() . 'cache' . DIRECTORY_SEPARATOR,
            // 缓存前缀
            'prefix'     => '',
            // 缓存有效期 0表示永久缓存
            'expire'     => 0,
            // 缓存标签前缀
            'tag_prefix' => 'tag:',
            // 序列化机制 例如 ['serialize', 'unserialize']
            'serialize'  => [],
        ],
        // 更多的缓存连接
        // redis缓存
        'redis'   =>  [
            // 驱动方式
            'type'          => 'redis',
            // 服务器地址
            'host'          => Env::get('redis.redis_hostname', '192.168.88.132'),  //缓存服务器地址
            // 端口
            'port'          => Env::get('redis.port', '6379'),  //缓存服务器地址端口
            // 密码
            'password'      => Env::get('redis.redis_password', '123456'), //缓存服务器密码
            // 缓存有效期 0表示永久缓存
            'expire'        => 0 ,
            // 缓存前缀
            'prefix'     => Env::get('cache.cache_prefix', 'c:'),
            // 缓存标签前缀
            'tag_prefix'    => Env::get('cache.cache_tag_prefix', 'CRMEB:'),
            // 数据库 0号数据库
            'select'        => intval(Env::get('redis.select', 0)),
            // 序列化机制 例如 ['serialize', 'unserialize']
            'serialize'     => [],
            // 服务端主动关闭
            'timeout'       => 0
        ],
    ],
];

② 更改Session存储位置

https://doc.thinkphp.cn/v6_1/Session.html

vim /data/CRMEB/crmeb/config/session.php

web01 session文件

web02 session文件

web01/web02 => session => redis

<?php
// +----------------------------------------------------------------------
// | 会话设置
// +----------------------------------------------------------------------

return [
    // session name
    'name'           => 'PHPSESSID',
    // SESSION_ID的提交变量,解决flash上传跨域
    'var_session_id' => '',
    // 驱动方式 支持file cache
    'type'           => 'cache',                // 修改这里
    // 存储连接标识 当type使用cache的时候有效
    // 过期时间
    'expire'         => 1440*1000,
    // 前缀
    'prefix'         => 'think',      // 修改这里
];

5. Nginx+Lua+Redis 实现访问攻击黑名单 WAF(了解)

WAF:(Web Application Firewalld),根据nginx访问流量判断是否为恶意攻击,如果1s内超过200个请求,就认为这是恶意攻击。

① redis01,安装openresty 和之前安装一致

#!/bin/bash
dnf -y install pcre-devel zlib-devel openssl-devel
tar -zxf openresty-1.25.3.2.tar.gz
cd openresty-1.25.3.2
./configure && make && make install

② 编写lua脚本

# mkdir -p /usr/local/lua
# vim /usr/local/lua/access_limit.lua
-- access_by_lua_file '/usr/local/lua/access_limit.lua'
local function close_redis(red)
    if not red then
        return
    end
    --释放连接(连接池实现)
    local pool_max_idle_time = 10000 --毫秒
    local pool_size = 100 --连接池大小
    local ok, err = red:set_keepalive(pool_max_idle_time, pool_size)
 
    if not ok then
        ngx_log(ngx_ERR, "set redis keepalive error : ", err)
    end
end
 
local redis = require "resty.redis"
local red = redis:new()
red:set_timeout(1000)
local ip = "192.168.88.132" -- redis ip
local port = 6379 -- redis port
local ok, err = red:connect(ip,port)
red:auth("123456") -- redis auth password
if not ok then
    return close_redis(red)
end
 
local clientIP = ngx.req.get_headers()["X-Real-IP"]
if clientIP == nil then
   clientIP = ngx.req.get_headers()["x_forwarded_for"]
end
if clientIP == nil then
   clientIP = ngx.var.remote_addr
end
 
local incrKey = "user:"..clientIP..":freq"
local blockKey = "user:"..clientIP..":block"
 
local is_block,err = red:get(blockKey) -- check if ip is blocked
if tonumber(is_block) == 1 then
   ngx.exit(ngx.HTTP_FORBIDDEN) -- return 403
   return close_redis(red)
end
 
res, err = red:incr(incrKey)
 
if res == 1 then
   res, err = red:expire(incrKey,1)
end
 
if res > 5 then        -- block 频率,测试环境可以考虑设置为3或5
    res, err = red:set(blockKey,1)
    res, err = red:expire(blockKey,600)
end
 
close_redis(red)

在Nginx中添加Lua脚本:

# vim /usr/local/openresty/nginx/conf/nginx.conf

#user  nobody;
worker_processes  1;

#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;

#pid        logs/nginx.pid;


events {
    worker_connections  1024;
}


http {
    include       mime.types;
    default_type  application/octet-stream;

    #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
    #                  '$status $body_bytes_sent "$http_referer" '
    #                  '"$http_user_agent" "$http_x_forwarded_for"';

    #access_log  logs/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    #keepalive_timeout  0;
    keepalive_timeout  65;

    #gzip  on;

    server {
        listen       80;
        server_name  localhost;

        #charset koi8-r;

        #access_log  logs/host.access.log  main;
        root html;
        location / {
            index  index.html index.htm;
            access_by_lua_file /usr/local/lua/access_limit.lua;
        }

        #error_page  404              /404.html;

        # redirect server error pages to the static page /50x.html
        #
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
        }

        # proxy the PHP scripts to Apache listening on 127.0.0.1:80
        #
        #location ~ \.php$ {
        #    proxy_pass   http://127.0.0.1;
        #}

        # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
        #
        #location ~ \.php$ {
        #    root           html;
        #    fastcgi_pass   127.0.0.1:9000;
        #    fastcgi_index  index.php;
        #    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
        #    include        fastcgi_params;
        #}

        # deny access to .htaccess files, if Apache's document root
        # concurs with nginx's one
        #
        #location ~ /\.ht {
        #    deny  all;
        #}
    }


    # another virtual host using mix of IP-, name-, and port-based configuration
    #
    #server {
    #    listen       8000;
    #    listen       somename:8080;
    #    server_name  somename  alias  another.alias;

    #    location / {
    #        root   html;
    #        index  index.html index.htm;
    #    }
    #}


    # HTTPS server
    #
    #server {
    #    listen       443 ssl;
    #    server_name  localhost;

    #    ssl_certificate      cert.pem;
    #    ssl_certificate_key  cert.key;

    #    ssl_session_cache    shared:SSL:1m;
    #    ssl_session_timeout  5m;

    #    ssl_ciphers  HIGH:!aNULL:!MD5;
    #    ssl_prefer_server_ciphers  on;

    #    location / {
    #        root   html;
    #        index  index.html index.htm;
    #    }
    #}

}

# 启动openresty
cd /usr/local/openresty
bin/openresty

启动openresty,在lb01上面可以先停旧的nginx(如何存在了nginx),然后再启动,也可以通过修改/usr/local/openresty/nginx/conf里面nginx.conf监听端口号

停掉旧版本nginx再启动
[root@node1 conf]# pkill nginx 
[root@node1 conf]# cd /usr/local/openresty
[root@node1 conf]# bin/openresty


或者修改/usr/local/openresty/nginx/conf/nginx.conf端口
[root@node1 conf] vim /usr/local/openresty/nginx/conf/nginx.conf
 server {
        listen       8080;    //修改端口号
        server_name  localhost;

        #charset koi8-r;

        #access_log  logs/host.access.log  main;

        location / {
            root   html;
            index  index.html index.htm;
            access_by_lua_file /usr/local/lua/access_limit.lua;
        }
 }
 启动服务
 [root@node1 conf]# /usr/local/openresty/bin/openresty

通过浏览器进行访问

③ 测试验证黑名单效果

6. 实现商城前端恶意攻击IP加入黑名单(了解)

停掉旧版nginx 服务器并禁止开机自启动

复制配置文件
[root@lb01 conf]# systemc disable nginx --now
[root@lb01 conf]# cp -a /etc/nginx/conf.d  /usr/local/openresty/nginx/conf/

修改配置文件,
[root@lb01 conf]# cd /usr/local/openresty/nginx/conf/conf.d
[root@lb01 conf]# vim my_web_service.conf
upstream my_web_service {
        #ip_hash;
        server 192.168.88.130:80;
        server 192.168.88.131:80;
    }
server {
        listen       80;  
        ####################  配置开始  ######################
        server_name  www.itheimashop.com;        # 替换为实际域名或 IP
        location / {
            proxy_pass http://my_web_service;    # 将请求转发到负载均衡组
            proxy_set_header HOST $host;
            proxy_set_header X-Real-IP $remote_addr;       # 添加这两行
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            access_by_lua_file /usr/local/lua/access_limit.lua; 
        }
        ####################  配置结束  ######################
    }
    
 修改lua脚本访问频率
[root@lb01 lua]# vim /usr/local/lua/access_limit.lua
    -- access_by_lua_file '/usr/local/lua/access_limit.lua'
local function close_redis(red)
    if not red then
        return
    end
    --释放连接(连接池实现)
    local pool_max_idle_time = 10000 --毫秒
    local pool_size = 100 --连接池大小
    local ok, err = red:set_keepalive(pool_max_idle_time, pool_size)
 
    if not ok then
        ngx_log(ngx_ERR, "set redis keepalive error : ", err)
    end
end
 
local redis = require "resty.redis"
local red = redis:new()
red:set_timeout(1000)
local ip = "192.168.88.132" -- redis ip
local port = 6379 -- redis port
local ok, err = red:connect(ip,port)
red:auth("123456") -- redis auth password
if not ok then
    return close_redis(red)
end
 
local clientIP = ngx.req.get_headers()["X-Real-IP"]
if clientIP == nil then
   clientIP = ngx.req.get_headers()["x_forwarded_for"]
end
if clientIP == nil then
   clientIP = ngx.var.remote_addr
end
 
local incrKey = "user:"..clientIP..":freq"
local blockKey = "user:"..clientIP..":block"
 
local is_block,err = red:get(blockKey) -- check if ip is blocked
if tonumber(is_block) == 1 then
   ngx.exit(ngx.HTTP_FORBIDDEN) -- return 403
   return close_redis(red)
end
 
res, err = red:incr(incrKey)
 
if res == 1 then
   res, err = red:expire(incrKey,1)
end
 
if res > 100 then        -- block 频率,网站前端改成100
    res, err = red:set(blockKey,1)
    res, err = red:expire(blockKey,600)
end
 
close_redis(red)

启动服务
[root@lb01 lua]# /usr/local/openresty/bin/openresty

浏览器http://www.itheimashop.com访问

总结

一、Redis 核心价值与应用场景

业务背景匹配

  • 用户规模:6万用户,日活跃2万,PV 20-30万

  • 适用场景:

    • 活跃用户统计:使用 Set 集合统计日活用户
    • Session 共享:存储用户会话,支持分布式架构
    • 安全防护:配合 OpenResty+Lua 实现 WAF 防火墙

Redis 五大核心优势

  1. 内存级性能:读写速度极快,适合高并发场景
  2. 丰富数据结构:5种基础+3种高级类型,满足复杂业务需求
  3. 持久化保障:RDB+AOF 保证数据安全不丢失
  4. 高可用方案:主从复制、哨兵、集群多种选择
  5. 分布式支持:支持水平扩展,满足大数据量需求

二、Redis 核心数据结构与业务映射

数据类型典型业务场景关键操作
String计数器、验证码、简单缓存SET/GET/INCR/EXPIRE
List消息队列、最新动态、登录历史LPUSH/RPOP/LRANGE
Set去重统计、共同好友、黑白名单SADD/SMEMBERS/SINTER
ZSet排行榜、热度排序、优先级队列ZADD/ZRANGE/ZSCORE
Hash用户信息、商品详情、对象缓存HSET/HGET/HGETALL

三、持久化策略选择指南

RDB 快照模式

# 触发条件配置
save 900 1    # 15分钟至少1个键改变
save 300 10   # 5分钟至少10个键改变
save 60 10000 # 1分钟至少10000个键改变
  • 优点:恢复速度快,文件小,适合备份
  • 缺点:可能丢失最后一次快照后的数据
  • 适用:对数据完整性要求不苛刻,追求快速恢复的场景

AOF 日志模式

# 写回策略
appendfsync always    # 每个命令都同步,最安全
appendfsync everysec  # 每秒同步,推荐使用
appendfsync no        # 由系统决定,性能最好但风险最大
  • 优点:数据丢失少,可读性强
  • 缺点:文件大,恢复速度慢
  • 适用:对数据安全要求极高的场景

混合模式(生产推荐)

aof-use-rdb-preamble yes  # Redis 4.0+ 默认开启

效果:RDB 做基础快照 + AOF 记录增量操作,兼顾速度与安全

四、高可用架构对比

特性主从复制哨兵模式集群模式
架构1主多从主从+哨兵监控多主多从分片
故障转移手动自动(10-30秒)自动(秒级)
扩展性垂直扩展读写分离水平扩展
数据量单机容量单机容量海量数据
适用场景简单备份中小规模高可用大规模高并发

五、企业实战应用总结

1. 主从复制配置要点

# Master 配置
bind 0.0.0.0
protected-mode no
requirepass redis666

# Slave 配置  
replicaof 192.168.88.101 6379
masterauth redis666
replica-read-only yes

2. 安全加固措施

  1. 密码认证:所有节点配置 requirepass
  2. 访问控制:结合防火墙限制访问IP
  3. 权限分离:主节点读写,从节点只读
  4. 网络隔离:生产环境使用内网通信

3. 性能优化建议

  1. 内存优化:设置 vm.overcommit_memory = 1
  2. 连接池:客户端使用连接池减少连接开销
  3. 键值设计:避免大Key、热Key,合理使用数据结构
  4. 持久化权衡:根据业务容忍度选择合适策略

六、典型解决方案

方案一:会话共享

// PHP 配置 session 存储到 Redis
session.save_handler = redis
session.save_path = "tcp://192.168.88.101:6379?auth=redis666"

优势:多台Web服务器共享Session,支持负载均衡

方案二:访问频率控制

-- OpenResty + Lua 实现 IP 黑名单
local incrKey = "user:"..clientIP..":freq"
if res > 100 then  -- 1秒内超过100次请求
    red:set(blockKey, 1)
    red:expire(blockKey, 600)  -- 封禁10分钟
end

方案三:排行榜实现

# 使用 ZSet 实现热门软件排行
ZADD hotTop 2 qq 3 wechat 5 alipay
ZREVRANGE hotTop 0 10 WITHSCORES  # 获取前10名

七、运维最佳实践

监控指标

  1. 内存使用率:避免超过物理内存
  2. 连接数:监控 connected_clients
  3. 命中率:关注 keyspace_hits/keyspace_misses
  4. 持久化状态:定期检查 RDB/AOF 文件状态
  5. 主从延迟:监控 master_repl_offset

故障处理

  1. 数据恢复:优先使用AOF,其次RDB
  2. 主从中断:检查网络、密码配置、复制状态
  3. 内存溢出:分析内存使用,优化数据结构
  4. 性能下降:排查慢查询、连接数激增

备份策略

  1. 定期备份:RDB快照 + AOF日志双重备份
  2. 异地备份:重要数据跨机房备份
  3. 恢复演练:定期测试备份文件可恢复性

八、技术选型建议

选型决策树

是否需要数据分片?
├── 否 → 数据量是否超过单机内存?
│   ├── 否 → 是否需要自动故障转移?
│   │   ├── 否 → 主从复制
│   │   └── 是 → 哨兵模式
│   └── 是 → 集群模式
└── 是 → 集群模式

容量规划

  • 内存需求:预估数据量 × 1.5(预留增长空间)
  • CPU需求:4核起步,高并发场景8核以上
  • 网络需求:千兆网络,集群节点间低延迟

九、核心要点

  1. 核心定位:Redis是企业级高性能缓存和数据结构存储解决方案
  2. 数据结构:5大基础类型应对90%业务场景,合理选择是关键
  3. 持久化:生产环境推荐RDB+AOF混合模式,平衡性能与安全
  4. 高可用:根据业务规模和重要性选择主从、哨兵或集群
  5. 安全运维:密码、防火墙、监控、备份四维一体保障稳定运行
  6. 扩展性:设计之初考虑分片策略,为业务增长预留空间

Redis在企业应用中不仅是缓存工具,更是支撑高并发、高性能业务的核心组件。正确选型、合理配置、有效监控是保障Redis稳定运行的关键。