跳到主要内容

第1章 Redis介绍及安装

1. 数据库基础概念

关系型与非关系型数据库对比

数据库类型代表产品特点
关系型数据库MySQL, Oracle, PostgreSQL结构化存储,ACID特性
非关系型数据库Redis, MongoDB灵活存储,高性能

NoSQL概念理解

NoSQL = Not Only SQL,指的是非关系型的数据库。

NoSQL有时也称作Not Only SQL的缩写,是对不同于传统的关系型数据库的数据库管理系统的统称。

对NoSQL最普遍的解释是"非关联型的",强调Key-Value Stores和文档数据库的优点,而不是单纯的RDBMS。

NoSQL的应用场景:

  • 超大规模数据的存储
  • 不需要固定的模式,无需多余操作就可以横向扩展
  • 用户的个人信息,社交网络,地理位置,用户生成的数据和用户操作日志的处理
  • 海量用户数据的挖掘和分析

2. Redis核心特性

Redis六大特性(AK47级别)

1. 速度快

  • C语言编写
  • 代码优雅简洁
  • 单线程架构

2. 支持多种数据结构

  • 字符串(String)
  • 哈希(Hash)
  • 列表(List)
  • 集合(Set)
  • 有序集合(Sorted Set)

3. 丰富的功能

  • 天然计数器
  • 键过期功能
  • 消息队列
  • 发布订阅

4. 支持客户端语言多

  • PHP, Java, Go, Python等主流语言

5. 支持数据持久化

  • 所有运行数据都存放在内存中
  • 支持多种持久化格式:RDB、AOF、混合持久化

6. 自带多种高可用架构

  • 主从复制
  • 哨兵模式
  • 集群模式

3. Redis应用场景(面试重点)

1. 缓存系统 - 利用键过期特性

  • Session数据缓存,过期自动删除
  • 用户信息缓存,先访问Redis,未命中再访问MySQL,然后回写Redis
  • 商城优惠券过期时间控制
  • 短信验证码有效期管理

2. 排行榜系统 - 利用有序集合特性

  • 热度/点击量排行
  • 直播间礼物打赏排行榜
  • 商品销量排行
  • 游戏积分排名

3. 计数器应用 - 利用原子操作特性

  • 帖子浏览数统计
  • 视频播放次数记录
  • 评论次数累计
  • 点赞/点踩计数

4. 社交网络功能 - 利用集合运算特性

  • 粉丝关系存储(关注/被关注)
  • 共同好友计算(交集运算)
  • 兴趣标签管理
  • 用户去重(注册名检查)
  • 抽奖系统(随机抽取)

5. 消息队列 - 利用列表数据结构

  • ELK日志缓冲
  • 异步任务队列
  • 聊天记录暂存

6. 发布订阅系统

  • 实时消息推送
  • 粉丝关注通知
  • 系统广播消息

7. 地理位置服务 - 利用GEO特性

  • 附近的人/商家/车辆查询
  • 外卖配送位置跟踪
  • 基于位置的推荐服务

4. 版本选择与规划

Redis官网

https://redis.io/download

版本选择对比

版本特性推荐场景
2.x非常老旧不推荐使用
3.x主流版本,支持redis-cluster集群部署
4.x混合持久化生产环境
5.x上一个稳定版,新增流处理类型消息队列场景
6.x最新稳定版追求最新特性

Redis版本选择

目录规划

/data/soft                        # 下载目录
/opt/redis_6379/{conf,logs,pid} # 安装目录,日志目录,pid目录,配置目录
/data/redis_6379/ # 数据目录

彩蛋:为什么Redis的端口号是6379?

Redis端口号由来

5. 源码编译安装

安装命令

# 创建下载目录
mkdir /data/soft -p
cd /data/soft/

# 下载Redis源码
wget http://download.redis.io/releases/redis-5.0.7.tar.gz

# 解压到/opt目录
tar zxf redis-5.0.7.tar.gz -C /opt/
cd /opt

# 创建软链接便于管理
ln -s /opt/redis-5.0.7 /opt/redis
cd /opt/redis

# 编译安装
make # 如果报错可以执行 make MALLOC=libc
make install

make和make install功能说明

步骤作用比喻
./configure环境检查洗菜和切菜
make编译源码炒菜
make install安装到系统装盘

6. 配置文件管理

编写配置文件

# 创建配置目录
mkdir -p /opt/redis_6379/{conf,logs,pid}
mkdir -p /data/redis_6379

# 创建基础配置文件
cat >/opt/redis_6379/conf/redis_6379.conf<<EOF
daemonize yes
bind 127.0.0.1 10.0.0.51
port 6379
pidfile /opt/redis_6379/pid/redis_6379.pid
logfile /opt/redis_6379/logs/redis_6379.log
EOF

7. 服务启动与验证

启动命令

redis-server /opt/redis_6379/conf/redis_6379.conf

服务检查

ps -ef|grep redis
netstat -lntup|grep 6379

连接测试

[root@db-51 ~]# redis-cli
127.0.0.1:6379> set k1 v1
OK
127.0.0.1:6379> get k1
"v1"
127.0.0.1:6379>

服务关闭命令

方式命令特点
第一种redis-cliSHUTDOWN客户端内关闭
第二种redis-cli shutdown命令行直接关闭
第三种使用管理脚本推荐方式,更安全
第四种kill / pkill进程强制终止,不推荐
# 第一种:客户端内关闭
[root@db-51 ~]# redis-cli
127.0.0.1:6379> SHUTDOWN

# 第二种:命令行直接关闭
[root@db-51 ~]# redis-cli shutdown

# 第三种:使用管理脚本(推荐)
# 使用服务管理脚本
./scripts/redis-service.sh stop

# 或使用专门的关闭脚本
./scripts/redis-shutdown.sh

# 第四种:进程终止(不推荐,可能丢失数据)
kill <pid>
pkill redis

注意:推荐使用脚本关闭服务,脚本会确保数据保存后再关闭Redis,避免数据丢失。

8. 系统服务配置

systemd启动配置

# 关闭现有Redis服务
redis-cli shutdown

# 创建Redis用户
groupadd redis -g 1000
useradd redis -u 1000 -g 1000 -M -s /sbin/nologin

# 修改目录权限
chown -R redis:redis /opt/redis*
chown -R redis:redis /data/redis*

# 创建systemd服务文件
cat >/usr/lib/systemd/system/redis.service<<EOF
[Unit]
Description=Redis persistent key-value database
After=network.target
After=network-online.target
Wants=network-online.target

[Service]
ExecStart=/usr/local/bin/redis-server /opt/redis_6379/conf/redis_6379.conf --supervised systemd
ExecStop=/usr/local/bin/redis-cli shutdown
Type=notify
User=redis
Group=redis
RuntimeDirectory=redis
RuntimeDirectoryMode=0755

[Install]
WantedBy=multi-user.target
EOF

# 重载systemd配置并启动服务
systemctl daemon-reload
systemctl start redis
systemctl enable redis

9. 系统优化配置

警告1:maximum open files过低

问题现象

17068:M 23 Jun 2020 10:23:55.707 # You requested maxclients of 10000 requiring at least 10032 max file descriptors.
17068:M 23 Jun 2020 10:23:55.707 # Server can't set maximum open files to 10032 because of OS error: Operation not permitted.
17068:M 23 Jun 2020 10:23:55.707 # Current maximum open files is 4096. maxclients has been reduced to 4064 to compensate for low ulimit.

解决方案

# 编辑systemd服务文件
vim /usr/lib/systemd/system/redis.service

# 在[Service]部分添加
[Service]
...
LimitNOFILE=65536

警告2: overcommit_memory设置

问题现象

WARNING overcommit_memory is set to 0! Background save may fail under low memory condition.

解决方案

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

警告3: 关闭THP大内存页

问题现象

WARNING you have Transparent Huge Pages (THP) support enabled in your kernel.

解决方案

echo never > /sys/kernel/mm/transparent_hugepage/enabled
echo "echo never > /sys/kernel/mm/transparent_hugepage/enabled" >> /etc/rc.local

警告4: TCP backlog太小

问题现象

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

解决方案

echo 511 > /proc/sys/net/core/somaxconn
sysctl net.core.somaxconn=511
echo "net.core.somaxconn=511" >> /etc/sysctl.conf

10. Redis配置文件详解

基础运行配置

后台运行设置

# 默认情况下,redis不是在后台运行的,如果需要在后台运行,把该项的值更改为yes
daemonize yes

进程和端口配置

# 当Redis在后台运行时,Redis默认会把pid文件放在/var/run/redis.pid
# 运行多个redis服务时,需要指定不同的pid文件和端口
pidfile /opt/redis_6379/pid/redis_6379.pid

# 指定redis运行的端口,默认是6379
port 6379

网络配置

# 在高并发的环境中,为避免慢客户端的连接问题,需要设置一个高速后台日志
tcp-backlog 511

# 指定redis只接收来自于该IP地址的请求,如果不进行设置,那么将处理所有请求
# bind 192.168.1.100 10.0.0.1
# bind 127.0.0.1

# 设置客户端连接时的超时时间,单位为秒。当客户端在这段时间内没有发出任何指令,那么关闭该连接
# 0是关闭此设置
timeout 0

# TCP keepalive
# 在Linux上,指定值(秒)用于发送ACKs的时间。注意关闭连接需要双倍的时间。默认为0
tcp-keepalive 0

日志配置

# 指定日志记录级别,生产环境推荐notice
# Redis总共支持四个级别:debug、verbose、notice、warning,默认为verbose
# debug 记录很多信息,用于开发和测试
# verbose 有用的信息,不像debug会记录那么多
# notice 普通的verbose,常用于生产环境
# warning 只有非常重要或者严重的信息会记录到日志
loglevel notice

# 配置log文件地址
# 默认值为stdout,标准输出,若后台模式会输出到/dev/null
logfile /opt/redis_6379/logs/redis_6379.log

# 可用数据库数
# 默认值为16,默认数据库为0,数据库范围在0-(database-1)之间
databases 16

RDB快照持久化配置

################################ 快照 #################################

# 保存数据到磁盘,格式如下:
# save <seconds> <changes>
# 指出在多长时间内,有多少次更新操作,就将数据同步到数据文件rdb
# 相当于条件触发抓取快照,这个可以多个条件配合
# 比如默认配置文件中的设置,就设置了三个条件
save 900 1 # 900秒内至少有1个key被改变
save 300 10 # 300秒内至少有10个key被改变
save 60 10000 # 60秒内至少有10000个key被改变

# 后台存储错误停止写
stop-writes-on-bgsave-error yes

# 存储至本地数据库时(持久化到rdb文件)是否压缩数据,默认为yes
rdbcompression yes

# RDB文件的是否直接校验checksum
rdbchecksum yes

# 本地持久化数据库文件名,默认值为dump.rdb
dbfilename dump.rdb

# 工作目录
# 数据库镜像备份的文件放置的路径
# 这里的路径跟文件名要分开配置是因为redis在进行备份时,先会将当前数据库的状态写入到一个临时文件中,
# 等备份完成,再把该临时文件替换为上面所指定的文件
# AOF文件也会存放在这个目录下面
# 注意这里必须制定一个目录而不是文件
dir /data/redis_6379/

主从复制配置

################################# 复制 #################################

# 主从复制。设置该数据库为其他数据库的从数据库
# 设置当本机为slave服务时,设置master服务的IP地址及端口,在Redis启动时,它会自动从master进行数据同步
# slaveof <masterip> <masterport>

# 当master服务设置了密码保护时(用requirepass制定的密码)
# slave服务连接master的密码
# masterauth <master-password>

# 当从库同主机失去连接或者复制正在进行,从机库有两种运行方式:
# 1) 如果slave-serve-stale-data设置为yes(默认设置),从库会继续响应客户端的请求
# 2) 如果slave-serve-stale-data是指为no,除了INFO和SLAVOF命令之外的任何请求都会返回一个错误"SYNC with master in progress"
slave-serve-stale-data yes

# 配置slave实例是否接受写。写slave对存储短暂数据(在同master数据同步后可以很容易地被删除)是有用的,
# 但未配置的情况下,客户端写可能会发送问题
# 从Redis2.6后,默认slave为read-only
slave-read-only yes

# 如果master不能再正常工作,那么会在多个slave中,选择优先值最小的一个slave提升为master,优先值为0表示不能提升为master
slave-priority 100

安全配置

################################## 安全 ###################################

# 设置客户端连接后进行任何其他指定前需要使用的密码
# 警告:因为redis速度相当快,所以在一台比较好的服务器下,一个外部的用户可以在一秒钟进行150K次的密码尝试,
# 这意味着你需要指定非常非常强大的密码来防止暴力破解
# requirepass foobared

# 命令重命名
# 在一个共享环境下可以重命名相对危险的命令。比如把CONFIG重名为一个不容易猜测的字符
# rename-command CONFIG b840fc02d524045429941cc15f59e41cb7be6c52
# 如果想删除一个命令,直接把它重命名为一个空字符""即可
# rename-command CONFIG ""

内存管理配置

################################### 约束 ###################################

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

# 指定Redis最大内存限制
# Redis在启动时会把数据加载到内存中,达到最大内存后,Redis会按照清除策略尝试清除已到期的Key
# 如果Redis依照策略清除后无法提供足够空间,或者策略设置为"noeviction",则使用更多空间的命令将会报错
# maxmemory <bytes>

# 当内存达到最大值的时候Redis会选择删除哪些数据?有六种方式可供选择:
# volatile-lru -> 利用LRU算法移除设置过过期时间的key
# allkeys-lru -> 利用LRU算法移除任何key
# volatile-random -> 移除设置过过期时间的随机key
# allkeys-random -> 移除随机key
# volatile-ttl -> 移除即将过期的key
# noeviction -> 不移除任何key,只是返回一个写错误
# 默认是: volatile-lru
# maxmemory-policy volatile-lru

# LRU和minimal TTL算法都不是精准的算法,但是相对精确的算法(为了节省内存),
# 随意你可以选择样本大小进行检测
# Redis默认的会选择3个样本进行检测,你可以通过maxmemory-samples进行设置
# maxmemory-samples 3

AOF持久化配置

############################## AOF ###############################

# 默认情况下,redis会在后台异步的把数据库镜像备份到磁盘,但是该备份是非常耗时的,
# 而且备份也不能很频繁,如果发生诸如拉闸限电、拔插头等状况,那么将造成比较大范围的数据丢失
# 所以redis提供了另外一种更加高效的数据库备份及灾难恢复方式
# 开启append only模式之后,redis会把所接收到的每一次写操作请求都追加到appendonly.aof文件中,
# 当redis重新启动时,会从该文件恢复出之前的状态
# 但是这样会造成appendonly.aof文件过大,所以redis还支持了BGREWRITEAOF指令,对appendonly.aof进行重新整理
# 你可以同时开启asynchronous dumps和AOF
appendonly no

# AOF文件名称 (默认: "appendonly.aof")
# appendfilename appendonly.aof

# Redis支持三种同步AOF文件的策略:
# no: 不进行同步,系统去操作。Faster.
# always: always表示每次有写操作都进行同步。Slow, Safest.
# everysec: 表示对写操作进行累积,每秒同步一次。Compromise.
# 默认是"everysec",按照速度和安全折中这是最好的
# 如果想让Redis能更高效的运行,你也可以设置为"no",让操作系统决定什么时候去执行
# 或者相反想让数据更安全你也可以设置为"always"
# 如果不确定就用"everysec"
# appendfsync always
appendfsync everysec
# appendfsync no

# AOF策略设置为always或者everysec时,后台处理进程(后台保存或者AOF日志重写)会执行大量的I/O操作
# 在某些Linux配置中会阻止过长的fsync()请求。注意现在没有任何修复,即使fsync在另外一个线程进行处理
# 为了减缓这个问题,可以设置下面这个参数no-appendfsync-on-rewrite
no-appendfsync-on-rewrite no

# AOF自动重写
# 当AOF文件增长到一定大小的时候Redis能够调用BGREWRITEAOF对日志文件进行重写
# 它是这样工作的:Redis会记住上次进行些日志后文件的大小(如果从开机以来还没进行过重写,那日子大小在开机的时候确定)
# 基础大小会同现在的大小进行比较。如果现在的大小比基础大小大制定的百分比,重写功能将启动
# 同时需要指定一个最小大小用于AOF重写,这个用于阻止即使文件很小但是增长幅度很大也去重写AOF文件的情况
# 设置percentage为0就关闭这个特性
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb

其他高级配置

################################ LUA脚本 #############################

# 一个Lua脚本最长的执行时间为5000毫秒(5秒),如果为0或负数表示无限执行时间
lua-time-limit 5000

################################ 慢日志 ################################

# Redis Slow Log记录超过特定执行时间的命令。执行时间不包括I/O计算比如连接客户端,返回结果等,只是命令执行时间
# 可以通过两个参数设置slow log:
# 一个是告诉Redis执行超过多少时间被记录的参数slowlog-log-slower-than(微秒)
# 另一个是slow log的长度。当一个新命令被记录的时候最早的命令将被从队列中移除
# 下面的时间以微秒为单位,因此1000000代表一秒
# 注意指定一个负数将关闭慢日志,而设置为0将强制每个命令都会记录
slowlog-log-slower-than 10000

# 对日志长度没有限制,只是要注意它会消耗内存
# 可以通过SLOWLOG RESET回收被慢日志消耗的内存
# 推荐使用默认值128,当慢日志超过128时,最先进入队列的记录会被踢出
slowlog-max-len 128

################################ 事件通知 #############################

# 当事件发生时,Redis可以通知Pub/Sub客户端
# 可以在下表中选择Redis要通知的事件类型。事件类型由单个字符来标识:
# K Keyspace事件,以__keyspace@<db>__的前缀方式发布
# E Keyevent事件,以__keyevent@<db>__的前缀方式发布
# g 通用事件(不指定类型),像DEL, EXPIRE, RENAME, ...
# $ String命令
# l List命令
# s Set命令
# h Hash命令
# z 有序集合命令
# x 过期事件(每次key过期时生成)
# e 清除事件(当key在内存被清除时生成)
# A g$lshzxe的别称,因此"AKE"意味着所有的事件
# notify-keyspace-events带一个由0到多个字符组成的字符串参数。空字符串意思是通知被禁用
# 例子:启用list和通用事件:
# notify-keyspace-events Elg
# 默认所有的通知被禁用,因为用户通常不需要该特性,并且该特性会有性能损耗
# 注意如果你不指定至少K或E之一,不会发送任何事件
notify-keyspace-events ""

############################## 高级配置 ###############################

# 当hash中包含超过指定元素个数并且最大的元素没有超过临界时,
# hash将以一种特殊的编码方式(大大减少内存使用)来存储,这里可以设置这两个临界值
# Redis Hash对应Value内部实际就是一个HashMap,实际这里会有2种不同实现,
# 这个Hash的成员比较少时Redis为了节省内存会采用类似一维数组的方式来紧凑存储,
# 而不会采用真正的HashMap结构,对应的value redisObject的encoding为zipmap,
# 当成员数量增大时会自动转成真正的HashMap,此时encoding为ht
hash-max-ziplist-entries 512
hash-max-ziplist-value 64

# 和Hash一样,多个小的list以特定的方式编码来节省空间
# list数据类型多少节点以下会采用去指针的紧凑存储格式
list-max-ziplist-entries 512
# list数据类型节点值大小小于多少字节会采用紧凑存储格式
list-max-ziplist-value 64

# set数据类型内部数据如果全部是数值型,且包含多少节点以下会采用紧凑格式存储
set-max-intset-entries 512

# 和hash和list一样,排序的set在指定的长度内以指定编码方式存储以节省空间
# zsort数据类型多少节点以下会采用去指针的紧凑存储格式
zset-max-ziplist-entries 128
# zsort数据类型节点值大小小于多少字节会采用紧凑存储格式
zset-max-ziplist-value 64

# Redis将在每100毫秒时使用1毫秒的CPU时间来对redis的hash表进行重新hash,可以降低内存的使用
# 当你的使用场景中,有非常严格的实时性需要,不能够接受Redis时不时的对请求有2毫秒的延迟的话,把这项配置为no
# 如果没有这么严格的实时性要求,可以设置为yes,以便能够尽可能快的释放内存
activerehashing yes

# 客户端的输出缓冲区的限制,因为某种原因客户端从服务器读取数据的速度不够快,
# 可用于强制断开连接(一个常见的原因是一个发布/订阅客户端消费消息的速度无法赶上生产它们的速度)
# 可以三种不同客户端的方式进行设置:
# normal -> 正常客户端
# slave -> slave和MONITOR客户端
# pubsub -> 至少订阅了一个pubsub channel或pattern的客户端
# 每个client-output-buffer-limit语法:
# client-output-buffer-limit <class> <hard limit> <soft limit> <soft seconds>
# 一旦达到硬限制客户端会立即断开,或者达到软限制并保持达成的指定秒数(连续)
# 例如,如果硬限制为32兆字节和软限制为16兆字节/10秒,客户端将会立即断开
# 如果输出缓冲区的大小达到32兆字节,客户端达到16兆字节和连续超过了限制10秒,也将断开连接
# 默认normal客户端不做限制,因为他们在一个请求后未要求时(以推的方式)不接收数据,
# 只有异步客户端可能会出现请求数据的速度比它可以读取的速度快的场景
# 把硬限制和软限制都设置为0来禁用该特性
client-output-buffer-limit normal 0 0 0
client-output-buffer-limit slave 256mb 64mb 60
client-output-buffer-limit pubsub 32mb 8mb 60

# Redis调用内部函数来执行许多后台任务,如关闭客户端超时的连接,清除过期的Key,等等
# 不是所有的任务都以相同的频率执行,但Redis依照指定的"Hz"值来执行检查任务
# 默认情况下,"Hz"的被设定为10
# 提高该值将在Redis空闲时使用更多的CPU时,但同时当有多个key同时到期会使Redis的反应更灵敏,以及超时可以更精确地处理
# 范围是1到500之间,但是值超过100通常不是一个好主意
# 大多数用户应该使用10这个预设值,只有在非常低的延迟的情况下有必要提高最大到100
hz 10

# 当一个子节点重写AOF文件时,如果启用下面的选项,则文件每生成32M数据进行同步
aof-rewrite-incremental-fsync yes

更新: 2024-07-29 08:43:22