NoSQL之 Redis配置与优化
1.关系数据库和非关系数据库
关系数据库SQL | 非关系数据库NoSQL | |
---|---|---|
存储结构 | 二维表格结构 | 非二维表格结构,不同的NoSQL采用不同的存储方式(比如键值对、文档、索引、图形结构、时间序列等) |
扩展方式 | 纵向扩展(提升单机的硬件性能) | 横向扩展(增加服务器节点节点数量) |
事务支持 | 基于ACID原则,对事物控制更稳定,细粒度更高 | 基于BASE原则,对事物控制的稳定性和细粒度不如SQL |
典型代表 | MySQL、Oracle、PostgreSQL、SQL Server、Microsoft Access、DB2 | Redis、MongBD、Hbase、Memcached、ElasticSearch、TSDB |
2.Redis安装部署
(1)Redis简介
用C语言开发的,开源的,基于内存运行的NoSQL并支持持久化。
存储结构:键值对(Key/Value KV)
数据类型:
五大基础类型:string(字符串)、list(列表)、hash(哈希/散列)、set(集合/无序集合)、zset/sorted set(有序集合)
三种特殊的数据类型:HyperLogLogs(基数统计) Bitmaps(位图) geospatial(地理位置)
端口号:TCP/6379
(2)Redis 为什么那么快?
1)redis是基于内存运行,数据的读写都是在内存中完成的
2)数据结构简单,可以直接使用 键值对 的方式存储数据
3)数据读写采用单线程模型,避免了多线程切换带来的CPU性能损耗,同时也不用考虑各种锁的影响
4)采用IO多路复用模型,非阻塞IO可以使网络线程处理更多的网络连接请求,提高了网络并发能力
(3)Redis安装部署
(1)环境准备
systemctl stop firewalld
systemctl disable firewalld
setenforce 0
vim /etc/selinux/config
disabled
#修改内核参数
vim /etc/sysctl.conf
vm.overcommit_memory = 1 #内核允许超量使用内存直到用完为止,防止OOM杀死进程
net.core.somaxconn = 2048 #指定处于监听状态的连接请求队列的最大长度
sysctl -p
(2)安装redis
yum install -y gcc gcc-c++ make
cd /opt/
ls
rz -E 上传redis-7.0.13.tar.gz
tar xf redis-7.0.13.tar.gz
cd redis-7.0.13/
make
make PREFIX=/usr/local/redis install
cd /usr/local/redis/
ls
cd bin/
ls
#由于Redis源码包中直接提供了 Makefile 文件,所以在解压完软件包后,不用先执行 ./configure 进行配置,可直接执行 make 与 make install 命令进行安装。
#创建redis工作目录
mkdir /usr/local/redis/{conf,log,data}
ls
cd /opt/redis-7.0.13/
ls
cp redis.conf /usr/local/redis/conf/
useradd -M -s /sbin/nologin redis
chown -R redis:redis /usr/local/redis/
#环境变量
cd /usr/local/redis/bin/
ls
vim /etc/profile
export PATH=$PATH:/usr/local/redis/bin
source /etc/profile
(3)修改配置文件
(4)定义systemd服务管理脚本
[Unit]
Description=Redis Server
After=network.target
[Service]
User=redis
Group=redis
Type=forking
TimeoutSec=0
PIDFile=/usr/local/redis/log/redis_6379.pid
ExecStart=/usr/local/redis/bin/redis-server /usr/local/redis/conf/redis.conf
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s QUIT $MAINPID
PrivateTmp=true
[Install]
WantedBy=multi-user.target
-h :指定远程主机
-p :指定 Redis 服务的端口号
-a :指定密码,未设置数据库密码可以省略-a 选项
redis-server:Redis 服务器启动命令
redis-benchmark:性能测试工具,用于检测 Redis 在本机的运行效率
redis-check-aof:修复有问题的 AOF 持久化文件
redis-check-rdb:修复有问题的 RDB 持久化文件
redis-cli:Redis 客户端命令行工具
redis-sentinel:Redis 哨兵集群使用
(4)redis-benchmark 测试工具
redis-benchmark [选项] [选项值]
常见选项 | |
---|---|
-h | 指定服务器主机名 |
-p | 指定服务器端口 |
-s | 指定服务器 socket |
-c | 指定并发连接数 |
-n | 指定请求数 |
-d | 以字节的形式指定 SET/GET 值的数据大小 |
-k | 1=keep alive 0=reconnect |
-r | SET/GET/INCR 使用随机 key, SADD 使用随机值 |
-P | 通过管道传输 |
-q | 强制退出 redis。仅显示 query/sec 值 |
--csv | 以 CSV 格式输出 |
-l | 生成循环,永久执行测试 |
-t | 仅运行以逗号分隔的测试命令列表 |
-I | Idle 模式。仅打开 N 个 idle 连接并等待 |
redis性能压测攻击
redis-benchmark -h <redis服务器地址> -p <redis端口> -a <redis密码> -c <并发连接数> -n <总请求数> -d <请求的数据大小> -t <测试的命令列表> -q
redis命令行客户端工具
redis-cli -h <redis服务器地址> -p <redis端口> -a <redis密码> [命令]
3.Redis数据库常用命令
set:存放数据,命令格式为 set key value
get:获取数据,命令格式为 get key
通用操作 | |
---|---|
del 键 | 删除键 |
type 键 | 查看键的数据类型 |
keys 键 | 查询键,支持通配符 * ? |
exists 键 | 判断键是否存在 |
expire 键 秒数 | 为键设置过期时间 |
ttl 键 | 查看键当前的过期时间,-1 永不过期,-2 已过期 |
rename 键 新键 | 重命名键,会覆盖已存在的键 |
renamenx 键 新键 | 重命名键,不会覆盖已存在的键 |
dbsize | 统计当前库中键的总数 |
config set requirepass '密码' | 设置redis密码 |
config get requirepass | 查看redis密码 |
select 库ID | 切换库,默认库ID为 0~15 |
move 键 库ID | 移动键到指定的库 |
flushdb | 清空当前库所有键(慎用) |
flushall | 清空所有库所有键(慎用) |
Redis 支持多数据库,Redis 默认情况下包含 16 个数据库,数据库名称是用数字 0-15 来依次命名的。
多数据库相互独立,互不干扰。
select 序号 ##多数据库间切换
注:使用 redis-cli 连接 Redis 数据库后,默认使用的是序号为 0 的数据库。
move 键名 序号 ##多数据库间移动数据
rename 源key 目标key
renamenx 源key 目标key
config set requirepass 密码 ##设置密码
config get requirepass ##查看密码(一旦设置密码,必须先验证通过密码,否则所有操作不可用)
(1)Redis 数据类型
string类型操作:(计数器)
set 键 值
get 键
setex 键 秒数 值 #创建键并设置过期时间
incr 键 #数字递增+1
decr 键 #数字递减-1
STRLEN 键 #统计键值的字符长度
mset 键1 值1 键2 值2
mget 键1 键2
list类型操作:(消息队列)
lpush 键 值1 值2 .... #从左边开始插入元素
rpush 键 值1 值2 .... #从右边开始插入元素
lrange 键 起始位置 终止位置 #起始位置 0表示左边开始的第一个元素,终止位置 -1表示到最后一个元素
lpop 键 #显示并删除左边第一个元素
rpop 键 #显示并删除右边第一个元素
lrem 键 N 值 #从左边开始删除N个指定元素
llen 键 #统计元素的数量
lindex 键 元素位置 #查看第N个元素的值
linsert 键 before|after 元素 值 #在指定元素前|后插入
hash类型操作:(存储对象描述)
hset 键 字段 值
hmset 键 字段1 值1 字段2 值2 ....
hget 键 字段
hkeys 键 #查看所有的字段
hvals 键 #查看所有字段的值
hdel 键 字段 #删除指定字段
set类型操作:(抽奖,求交集、差集、并集)
sadd 键 值1 值2 .... #元素不能重复
smembers 键 #查看元素,无序的
srem 键 值1 值2 ... #删除指定的元素
srandmember 键 N #随便显示N个元素
scard 键 #统计元素的数量
spop 键 #随机显示并删除一个元素
smove 键1 键2 值 #将键1的元素移动到键2中
sinter 键1 键2 #求键1与键2的交集
sdiff 键1 键2 #求键1与键2的差集
sunion 键1 键2 #求键1与键2的并集
zset类型操作:(排行榜,热搜)
zadd 键 权重1 值1 权重2 值2 .... #score权重是可以重复的,元素值是不可以重复的
zrange 键 起始位置 终止位置 [withscores] #起始位置 0表示左边开始的第一个元素,终止位置 -1表示到最后一个元素
zrangebyscore 键 起始权重 终止权重 [limit N M] #查看指定权重范围的元素,按score从小到大,limit N M 表示只显示第N个之后的M个元素(不包括第N个元素)
zrevrange 键 起始位置 终止位置 #降序查看
zrevrangebyscore 键 起始权重 终止权重 [limit N M] #按score从大到小查看
zcard 键 #统计元素的数量
zcount 键 起始权重 终止权重 #统计指定权重范围的元素数量
zrem 键 值1 值2 ... #删除指定的元素
zincrby 键 权重 值 #添加新的元素或增加指定元素的权重
4.Redis 高可用
在Redis中,实现高可用的技术主要包括持久化、主从复制、哨兵和 Cluster集群
●持久化:持久化是最简单的高可用方法(有时甚至不被归为高可用的手段),主要作用是数据备份,即将数据存储在硬盘,保证数据不会因进程退出而丢失。
●主从复制:主从复制是高可用Redis的基础,哨兵和集群都是在主从复制基础上实现高可用的。主从复制主要实现了数据的多机备份,以及对于读操作的负载均衡和简单的故障恢复。缺陷:故障恢复无法自动化;写操作无法负载均衡;存储能力受到单机的限制。
●哨兵:在主从复制的基础上,哨兵实现了自动化的故障恢复。缺陷:写操作无法负载均衡;存储能力受到单机的限制。
●Cluster集群:通过集群,Redis解决了写操作无法负载均衡,以及存储能力受到单机限制的问题,实现了较为完善的高可用方案。
(1)Redis持久化
(1)RDB持久化:定时的将redis在内存中的数据进行快照并压缩保存到硬盘里
手动触发:bgsave命令
自动触发:满足配置文件中 save n m 的规则(在n秒内发生了m次数据更新就会自动触发);主从复制在做全量复制时;执行shutdown命令关闭数据库时
工作流程:redis父进程会fork子进程来进行RDB持久化快照保存内存数据到硬盘里,文件名:dump.rdb
优缺点:
RDB持久化保存的文件占用空间较小,网络传输快,恢复速度比AOF更快,性能影响比AOF更小;
实时性不如AOF,兼容性较差,持久化期间在fork子进程时会阻塞redis父进程
(2)AOF持久化:实时的以追加的方式将redis写操作的命令记录到aof文件中
工作流程:
命令追加(将写操作命令追到aof_buf缓冲区)
文件写入和同步(文件名:appendonly.aof
,同步策略:appendfsync everysec|always|no)
文件重写(减少aof文件占用空间的大小和加快恢复速度,定期执行bgrewriteaof命令触发)
优缺点:
实时性比RDB更好,支持秒级持久化,兼容性较好;
持久化保存的文件占用空间更大,恢复速度更慢,性能影响更大,AOF文件重写期间在fork子进程时也会阻塞redis父进程,且IO压力更大
AOF缓存区的同步文件策略存在三种同步方式
5.Redis性能管理
(1)内存碎片
通过 info memory 命令查看内存的使用情况。
mem_fragmentation_ratio的值如果超过了1.5,建议可以考虑进行内存碎片的清理了。
mem_fragmentation_ratio的值如果小于1,说明物理内存不够真实数据的保存了,此时已经开始使用swap交换空间了,会导致redis性能的严重下降。应该考虑增加物理内存或减少redis内存占用。
config set activedefrag yes #开启自动内存碎片清理
memory purge #手动内存碎片清理
注:由于内存碎片清理是redis主线程执行的,会发生阻塞。因此需要合理配置对应的参数和方式,保证redis的高性能。
内回收key
内存数据淘汰策略,保证合理分配redis有限的内存资源。
当达到设置的最大阀值时,需选择一种key的回收策略,默认情况下回收策略是禁止删除。
6.redis优化
1)开启AOF持久化,设置AOF刷盘策略为everysec(每隔1秒执行一次刷盘操作),只在业务低峰期执行AOF文件重写,减少磁盘的开销
2)缩短键值对存储长度,避免存储bigkey导致操作耗时
3)给key设置合理的过期时间,尽量避免大量key集中过期
4)设置内存上限(maxmemory),并设置内存数据淘汰策略(maxmemory-policy),一般最常使用的是volatile-lru(只删除最近最少访问并设置了过期时间的键)或 allkeys-lru
5)开启自动内存碎片清理(activedefrag yes)
6)开启lazy-free机制(lazyfree-lazy-eviction yes、lazyfree-lazy-expire yes、lazyfree-lazy-server-del yes),将删除过期key的操作放到后台线程执行,以减少删除对Redis主线程的阻塞
7)使用物理机而非虚拟机部署Redis服务,使用高速固态盘作为AOF日志的写入盘。
8)使用分布式架构(主从复制、哨兵模式、集群)增加读写速度,并实现高可用
9)禁用内存大页(echo never > /sys/kernel/mm/transparent_hugepage/enabled),因开启内存大页会导致fork的速度变慢,也会拖慢写操作的执行时间
(1)redis的三大缓存问题
正常情况下,大部分的访问请求应该是先被redis响应的,在redis那里得不到响应的小部分访问请求才会去请求MySQL数据库获取数据,这样MySQL数据库的负载压力是非常小的,且可以正常工作。
缓存雪崩/穿透/击穿问题的根本原因在于redis缓存命中率下降,大量请求会直接发给MySQL数据库,导致MySQL数据库压力过大而崩溃。
缓存雪崩:redis中大量不同的缓存key集体过期
缓存穿透:大量请求访问redis和MySQL数据库都不存在的资源
缓存击穿:redis中一个热点key过期,此时又有大量请求访问这个热点key
(1)缓存雪崩解决方案
使用随机数设置key的过期时间,防止集群过期
设置二级缓存
数据库使用排他锁,实现加锁等待
(2)缓存穿透解决方案
对空值也进行缓存
使用布隆过滤器进行判断拦截一定不存在的无效请求
使用脚本实时监控,进行黑名单限制
(3)缓存击穿解决方案
设置永不过期
预先对热点数据进行缓存预热
数据库使用排他锁,实现加锁等待
(2)如何保证MySQL和redis的数据一致性?
读取数据时,先从redis读取数据,如果redis没有,再从MySQL读取,并将读取到的数据同步到redis缓存中。
更新数据时,先更新MySQL数据,再更新redis缓存
删除数据时,先删除redis缓存,再删除MySQL数据
对于一些关键数据,可以使用定时任务,定时自动进行缓存预热,或使用MySQL触发器来实现同步redis缓存