写在前面
- 其他内容请看 JAVA-第十一部分-Redis
redis启动
- 服务端更换端口
redis-server --port 6380 - 客户端连接
redis-cli -p 6380 - 客户端指定ip
-h
查看配置文件
- 目录
/usr/local/ext - 配置文件
redis.conf - 通过配置文件启动
//复制一份
cat redis.conf | grep -v "#" | grep -v "^$" > redis-6379.conf
//配置内容,修改配置文件
port 6379 //端口
daemonize no //后台启动,
logfile "" //日志文件,设置之后,在日志中显示,不显示在控制台
dir /usr/local/etc/data //存储的文件夹
//启动
redis-server redis-6379.conf
//复制一份 修改配置内容
cp redis-6379.conf redis-6380.conf
服务器基础配置
- 设置服务器以守护进程的方式运行
daemoniz yes|no - 绑定主机地址
bind 127.0.0.1 - 设置数据库数量
databases 16 - 设置端口
port 6370
日志配置
- 设置服务器以指定日志记录级别
loglevel debug(过程信息)|verbose(默认)|notice|warning - 日志记录文件名
logfile port.log
客户端配置
- 设置最大客户端连接数
maxclients 00为不限制 - 客户端限制最大时长,达到最大值后关闭连接
timeout 300(秒)0关闭该功能
多服务器快捷配置
- 导入并加载指定配置文件信息
include /path/server-port.conf,类似继承
持久化
- 利用永久性存储介质将数据保存,例如硬盘,在特定的时间将保存的数据进行恢复的工作机制
- 数据快照 RDB/过程日志 AOF
RDB
- 存储效率高,存储的是某个时间点的数据,速度快,通常用于灾难恢复
- 会丢数据,后台执行时,消耗子进程;redis版本rdb格式版本没有进行统一
save- 生成文件
dump.rdb - 文件保存在
/usr/local/etc/data - 配置
//文件名
dbfilename dump-6379.rdb
//是否压缩
rdbcompression yes
//rdb格式校验
rdbchecksum yes
- save会阻塞当前服务器,不推荐线上使用
后台执行
bgsave,推荐使用- 调用
fork函数生成子进程去保存 - 保存结果会返回到控制台/日志文件
Background saving terminated with success
- 配置,如果后台存储过程中出现错误,是否停止,默认开启
stop-writes-on-bgsave-error yes
自动执行
- 对数据产生影响;真正产生了影响;不进行数据比对,产生一个影响量
- 底层调用
bgsave - 通过配置设置
//second 监控的时间范围;changes 监控key的变化量
save second changes
- 日志信息
2 changes in 10 seconds. Saving...
Background saving started by pid 20409
DB saved on disk
Background saving terminated with success
特殊启动
- 全量复制,主从复制
- 服务器运行过程中重启
debug reload - 关闭服务器时指定保存数据
shutdown save
AOF
- 记录操作过程,排除丢失数据的风险
- append only file,以独立日志的方式记录
- 先将操作命令放在
AOP写命令刷新缓存区,在一定阶段后将命令同步到AOP文件中 - 三种策略
appendfsync
always 每次写入操作都同步到AOF文件中,数据零误差,性能差
everysec 每秒,数据准确定较高,性能高,但是突然宕机丢失一秒的数据
no 系统控制 由操作系统控制
- 原理
使用
- 配置
//是否开启AOF持久化功能,默认不开启
appendonly yes|no
//AOF写数据策略
appendfsync always | everysec | no
//改文件名
appendfilename filename
//文件夹
dir
重写
- 整理操作,提高磁盘利用率,降低持久化时间
- 超时数据不写,忽略不对结果产生影响的指令,多条指令合并为一条指令
- 手动重写
bgrewriteaof,重写后,就跟rdb形式一样的存储方式 - 自动重写,配置
// 最小尺寸
auto-aof-rewrite-min-size size
// 自动重写的百分比
auto-aof-rewrite-precentage precentage
//重写条件
aof_current_size > auto-aof-rewrite-min-size
aof_cuuent_size - aof_base_size / aof_base_size >= auto-aof-rewrite-precentage
RDB和AOF的区别
事务
- 开启事务
multi,之后的指令返回QUEUED - 执行事务
exec,执行所有指令 - multi之后,后续所有的指令均加入到事务中,遇到exec之后,执行事务
- 取消事务
discard - 如果后续输入的命令有语法错误,其余的命令都将不存在
- 只要是语法正确,但是无法正确地执行,运行错误的命令将不会被执行
锁
- 监控数据
watch key1 key2... - 取消监控
unwatch - watch监控的数据,在开启事务后,如果操作被监控的数据,事务将会被打断,对于这个数据的操作将无法执行
127.0.0.1:6379> watch name
OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> set name 123
QUEUED
127.0.0.1:6379(TX)> exec
(nil)
分布式锁
- 公共锁
setnx lock-key value,利用setnx命令的返回值特征,有值则返回设置失败,无值才能设置成功;操作完数据之后,del lock-key - 通过协议的方式实现,在操作数据前,先操作
lock-key,如果可以操作,那么就可以操作,如果不行,就不操作 - 为锁加上时间,防止忘记开锁
expire lock-key seconds
删除策略
- 过期数据会被保留在redis中
- 在内存中,为过期数据开辟一块hash的空间,管理过期数据
定时删除
- 到期时间到时,cpu就去处理删除,用处理器性能换存储空间(时间换空间)
惰性删除
- 数据到时不做处理,等下次访问该数据时,如果过期了,则清除,并返回不存在
expireIfNeeded()在get key时执行- 节约CPU性能,内存压力大,用存储空间换取处理器性能(空间换时间)
定期删除
- redis启动时,读取配置
server.hz的值,默认为10,每秒钟执行server.hz次serverCron()->databasesCron()->activeExpireCycle() activeExpireCycle()对每个库的expires逐一检测,每次执行250ms/server.hz,对某个expires检测时,随机挑选W个key检测,如果key超时,则删除key;如果一轮中删除的key的数量>W*25%,证明过期数据较多,在检测一次;通过后,检查下一个- W取值 =
ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP属性值,配置文件中设置 current_db专门记录activeExpireCycle()进入到哪个库的expires空间执行- 每秒话费固定的CPU资源维护内存,随机抽查、重点抽查
逐出算法
- redis使用内存存储数据,在执行每一个指令前,会调用
freeMemoryIfNeeded()检测内存是否充足,如果不满足新加入数据的最低存储要求,根据逐出算法,临时删除一些数据 - 最大可使用内存
maxmemory,占物理内存的比例,默认为0,不限制 - 每次选取待删除数据的个数
maxmemory-samples,采用随机选取数据的方式 - 删除策略
maxmemory-policy XXXXXX lru离现在的使用时间,淘汰最长的
主从复制
- 若干个redis连接在一起,保证数据是同步的,实现高可用,同时实现数据冗余备份
- master 提供数据;slave 接收数据
- 核心工作,就是master数据复制到slave
- 一个master负责多个slave,一个slave只对应一个master
- master只写,slave只读
- master宕机,让某个slaver充当master
- 主从复制实现读写分离、负载均衡、故障恢复、数据冗余、高可用
- slave连接master,master将数据反复同步给slave
建立连接阶段
- slave 发送 master的ip和端口号
slaveof ip port //客户端直接发送命令
redis-server -slaveof ip port //服务端启动发送
slaveof ip port //slave机器配置
//6380 连接 6379
slaveof 127.0.0.1 6379
//slave发送断开操作
slaveof no one
- 权限验证
//slave端
auth password
masterauth password //配置文件
redis-cli -a password //启动设置密码
//master端
requirepass password //配置文件设置密码
config set requirepass password //命令设置密码
config get requirepass
- 最后,slave保存了master的地址与端口;master保存了slave的端口;之间建立了连接的socket
数据同步阶段
- 避开流量高峰期进行数据同步
- 请求同步数据
- 创建rdb同步数据
- 恢复rdb同步数据
- 请求部分同步数据,部分数据指的是全量复制过程中所产生的数据,也叫增量复制
- 恢复部分同步数据
- 最后状态,slave具有master端全部数据,包含rdb过程中接收的数据;master保存了slave当前同步的位置,下一次从上一次结尾开始复制
- master 复制缓冲区,避免数据溢出 2平均时长数据总量
repl-backlog-size 1mb
- slave只提供读,关闭写
slave-server-stable-date yes|no
命令传播阶段(不断同步)
- 服务器运行ID,每一台服务器每次运行的身份识别码,40位字符组成,是一个随机的十六进制字符;用于在服务期间进行传输,身份识别,用于处理是否是一台机器,数据是否同步;
info server查看runid;master在首次连接slave时,会将自己的运行id发送给slave,slave保存,并且每次更新都要携带,如果不同,则说明先前的master不是这个master - 复制缓冲区,是一个先进先出的队列,用于存储服务器执行过的命令,每次传播命令,master都会将传播的命令记录下来,存储在复制缓冲区;一条命令,以aof的形式存储,用字符为单位,存储字节值,并且给了一个编号(偏移量),用来识别;通过offset区分不同的slave当前数据传播的差异
- 复制偏移量,一个数字,描述肤质缓冲区中的指令字节位置;master,发送一次记录一次,记录发给所有的slave的指令字节偏移量;slave,接收一次记录一次,记录自己接收master发过来的指令字节偏移量;作用,同步数据,对比差异
心跳机制
- 命令传播阶段,master与slave间需要进行信息交换,使用心跳机制维护,保持双方在线
- master处理策略,通过slave发送的
REPLCONF ACK命令确定
//slave数量少于2个,或者所有slave的延迟都大于等于10秒,强制关闭master写功能,停止数据同步
min-slaves-to-write 2
min-slaves-max-lag 8
全部工作流程
一些问题
- 设置合理的超时时间
repl-timeout
- ping指令发送频率,确定连接
repl-ping-slave-period
- 如果slave延迟过大,多个slave获取相同数据不同步,暂时屏蔽对某个slave的数据访问;开启后仅响应info、slaveof等少数命令
slave-serve-stale-data yes|no
哨兵
- 解决master宕机的问题
- 在slave中选择一个作为master,通知所有的slave连接新的mater,启动新的master和slave
- sentinel,分布式系统,用于对主从结构中的每台服务器进行
监控,当master宕机,向哨兵间、客户端发送通知,选择出新的master,并将所有的slave连接到新的master上,并告诉客户端新的服务器地址,进行自动故障转移 - 哨兵也是一台redis服务器,不提供数据服务,配置数量为单数
- 配置
sentinel.conf配置相同,端口不同
//快速改端口,并复制一份
sed 's/26379/26381/g' sentinel-26379.conf > sentinel-26381.conf
- 启动哨兵
redis-sentinel sentinel-端口号.conf - 整个过程在后台进行
监控
- 同步信息
- 同步各个节点状态信息,获取(ping)sentinel的状态,(info)master状态(runid、role、各个slave信息),(info)slave的信息
通知
- 保持联通
故障转移
- 判断master下线
- 选择出一个sentinel负责选择新的master
- 选择新的master,选择在线的、响应快的、与原master断开时间短的、优先原则(优先级、offset、runid)
- 向新的master发送
slaveof no one断开与原master的连接 - 向其他slave发送
slaveof 新的masterIP和端口,建立新的连接
集群
- 使用网络将若干台计算机联通起来,并提供统一的管理方式,使其对外呈现单机的服务效果
- 分散单台服务器的访问压力,负载均衡;分散单台服务器的存储压力,可扩展性;降低单台服务器宕机带来的业务灾难
redis集群
- 槽,存储key的位置
- 通过算法设计,计算key应该保存的位置;增减服务器,处理槽的数量
- 内部通讯设计,每台服务器互连,互相记录槽的位置;客户端一次命中,直接返回,一次未命中收到具体位置,直接去找
cluster集群
- 配置
//设置是否是cluster
cluster-enabled yes
//配置文件名
cluster-config-file nodes-6379.conf
//超时时长 毫秒
cluster-node-timeout 10000
//master连接的slave最小数量
cluster-migration-barrier count
- 建立,需要
redis-trib.rb,配置ruby和gem环境
//执行前要对每个服务端执行
flushall
cluster reset
//执行命令,1代表一个master连一个slave,按后面添加的服务器自动分,而且服务器里面不允许有任何内容
./redis-trib.rb create --replicas 1 127.0.0.1:6379 127.0.0.1:6380 127.0.0.1:6381 127.0.0.1:6382 127.0.0.1:6383 127.0.0.1:6384
- 使用
//重定向启动,否则要在指定的端口槽中进行操作
redis-cli -c
//客户端查看节点状态
cluster nodes
主从下线和主从切换
- 从下线/重连,直属master节点收到,并通知其他master节点
- master节点下线,直属slave节点不断重连,超过超时时间后,变成mster
- master节点重新连接,作为slave节点连接master
企业级解决方案
缓存数据库双写不一致
- 由于卡顿,数据库的值和
redis中的不一致
实际上,采用
懒加更新缓存,当更新时,删除缓存,只更新数据库,需要读取数据时,才加载进缓存
- 但是,有可能在读取后更新的过程,有卡顿,期间有新的数据被写入数据库,此时缓存中的数据不一致
- 延迟双删,在更新数据库并删除缓存后,延迟一段时间,在删除一次,防止过程中缓存数据不一致,但是不能很好的解决
- 内存队列,将数据缓存的读写操作都用一个队列维护,逐个执行,更新缓存,实现麻烦
- 通过分布式锁,将
写数据库并删缓存,查数据库并更新缓存转换成原子操作,排队执行,并发执行的操作串行化
- 读写锁,读锁不互斥,写锁互斥,可以有多个线程并发读,一个时间只能有一个线程写,解决读多写少
- 读多写多,为缓存设置超时时间,牺牲一致性,因为真正下单中间还有一定过程,在最后真正结算的时候才访问数据库
- 读多写多,直接访问数据库,使用
canal,监控数据库,一有变化,同步更新缓存
缓存预热
- 系统启动前,提前将相关的缓存数据直接加载到缓存系统,避免在用户请求的时候,先查询数据库,然后再缓存,用户应该直接查询到事先准备好的缓存数据
缓存雪崩
- 瞬间过期数据量太大,导致对数据库服务器造成压力,应该避免过期时间集中,监控服务器运行数据
- 一个较短的时间内,缓存中较多的key集中过期,向数据库要
- 数据库收到大量的请求无法及时处理,redis大量请求被积压,开始出现超时
- 流量激增,资源严重被占用
缓存击穿
- 单个key高热或者过期,瞬间访问量大,没有命中redis,对数据库造成大量访问
缓存穿透
- 访问不存在的数据
- 数据库中不存在这个数据,reids中没有持久化;redis中大面积未命中,非正常的url访问
性能指标监控
- 性能指标 Performance
- 内存指标 Memory
- 基本活动指标 Basic activity
- 持久性指标 Persistence
- 错误指标 Error
监控命令
redis-benchmark
//-c 连接数 -n 请求数
redis-benchmark [-h] [-p] [-c] [-n <requests>] [-k]
//不带参数,50个链接 10000次请求对应的性能
//50个连接 100个请求
redis-benchmark -c 50 -n 100
- 打印服务器调试信息
monitor,可以通过一个客户端来执行,看另一个客户端执行的语句 slowlog慢查询日志,记录操作超时的命令
//获取慢查询日志/日志条目数/重置
slowlog get/len/reset
//相关配置
//设置慢查询的时间下线 微秒
slowlog-log-slower than 1000
//设置慢查询命令对应的日志显示长度 命令数
slowlog-max-len 100