目录
1. 内存淘汰
1.1 主动过期
- 通过EXPIRE/EXPIREAT等命令主动设置key的过期时间。
1.2 内存策略
- volatile-lru->removethekeywithanexpiresetusinganLRUalgorithm
- volatile-random->removearandomkeywithanexpireset
- volatile-ttl->removethekeywiththenearestexpiretime(minorTTL)
- allkeys-random->removearandomkey,anykey
- allkeys-lru->removeanykeyaccordingtotheLRUalgorithm
- noeviction->don'texpireatall,justreturnanerroronwriteoperations
127.0.0.1:60000> set jingbotest
OK
127.0.0.1:60000> get jingbo
"test"
127.0.0.1:60000> expirejingbo30
(integer) 1
127.0.0.1:60000> ttljingbo
(integer) 27
127.0.0.1:60000> ttljingbo
(integer) 26
127.0.0.1:60000> ttljingbo
(integer) 25
127.0.0.1:60000> ttljingbo
(integer) 24
redis> SET mykey"Hello"
"OK"
redis> EXISTS mykey
(integer) 1
redis> EXPIREATmykey1293840000
(integer) 1
redis> EXISTS mykey
(integer) 0
redis>
1.3 Redis淘汰机制
1. 主动淘汰
- 在beforeSleep方法中执行key快速过期检查
- 在serverCron方法中执行key过期检查,serverCron方法执行周期为1秒钟执行server.hz次,hz默认为10,所以约100ms执行一次。hz设置越大过期键删除就越精准,但是cpu使用率会越高,这里建议线上redis采用的默认值。redis主要是在这个方法里删除大部分的过期键。
2. 被动淘汰
- 使用内存超过最大内存被迫根据相应的内存策略删除符合条件的key。(触发时影响相应时间)
- 在key写入之前进行被动检查,检查key是否过期,过期就进行删除。
- randomkey命令,该命令随机从redis获取键,每次获取到键的时候会检查该键是否过期。
2. Redis事物
2.1 事物简介
数据库事务(Database Transaction) ,是指作为单个逻辑工作单元执行的一系列操作,要么完全地执行,要么完全地不执行。事务处理可以确保除非事务性单元内的所有操作都成功完成,否则不会永久更新面向数据的资源。通过将一组相关操作组合为一个要么全部成功要么全部失败的单元,可以简化错误恢复并使应用程序更加可靠。一个逻辑工作单元要成为事务,必须满足所谓的ACID(原子性、一致性、隔离性和持久性)属性。事务是数据库运行中的逻辑工作单位,由RDBMS中的事务管理子系统负责事务的处理。
2.2 Redis事物使用
1.开始事物
redis127.0.0.1:6379> MULTI
2.命令入队
redis127.0.0.1:6379> set jingbotest
redis127.0.0.1:6379> set zbdba test
3.执行事物
redis127.0.0.1:6379> EXEC
2.3 Redis事物原理
2.4 Redis事物踩坑
1.业务反映Redis夯住
2.查看slow log
3.发现MULTI
注意:合理使用事务,避免长事务对线上造成影响。
3. Redis持久化
Redis是缓存数据库,但是也提供了如下数据落盘的方式:
- RDB // 做一份内存快照
- AOF // 将Redis执行的命令追加到AOF文件中
- MIX // 在内存快照的基础上追加AOF文件
3.1 RDB
3.1.1 RDB 配置使用
# save <seconds> <changes>
# after 900 sec (15 min) if at least 1 key changed
# after 300 sec (5 min) if at least 10 keys changed
# after 60 sec if at least 10000 keys changed
# Note: you can disable saving completely by commenting out all "save" lines.
# It is also possible to remove all the previously configured save
# points by adding a save directive with a single empty string argument
# like in the following example:
save 900 1
save 300 10
save 60 10000
save “” 关闭bgsave
- stop-writes-on-bgsave-error yes // bgsave失败停止写入
- rdbcompressionyes // 是否压缩RDB
- rdbchecksumyes// 是否对RDB做校验
- dbfilenamedump.rdb// RDB 命名
3.1.2 RDB 内部流程
3.1.3 RDB 内部结构
https://github.com/sripathikrishnan/redis-rdb-tools/wiki/Redis-RDB-Dump-File-Format
https://github.com/sripathikrishnan/redis-rdb-tools
3.1.4 RDB 配置建议
- Fork 代价较高
- 建议设置频率尽量低
- 采用调度程序定时调度。
3.2 AOF
3.2.1 AOF 配置使用
- appendonly no // 是否开启AOF
- appendfilename “appendonly.aof“ // AOF文件命名
- appendfsync everysec/always // AOF 刷盘频率
- no-appendfsync-on-rewrite no // 做bgaofrewrite时是否允许继续追加
- auto-aof-rewrite-percentage 100
- auto-aof-rewrite-min-size 64mb
3.2.2 AOF 内部流程
3.2.3 AOF 内部结构
*2 // 两个命令参数
$6 // 6个元素
SELECT // select 命令
$1 // 1个元素
0 // 0 号数据库
*3 // 三个命令参数
$3 // 三个元素
Set // set 命令
$10 // 10个元素
2604057111 // key
$10 // 10个元素
1415540069 // value
3.2.4 AOF 配置建议
- appendfsync需要根据磁盘性能进行设置
- no-appendfsync-on-rewrite 需要根据磁盘性能进行设置
- auto-aof-rewrite-percentage 100
- auto-aof-rewrite-min-size 64mb
总结:
尽量避免做bgaofrewrite,在写入量较大磁盘性能较差的情况下设置appendfsynceverysec/no-appendfsync-on-rewrite yes
3.3 Redis混合持久化
AT Redis4.0 Version:
# When rewriting the AOF file, Redisis able to use an RDB preamble in the
# AOF file for faster rewrites and recoveries. When this option is turned
# on the rewritten AOF file is composed of two different stanzas:
# [RDB file][AOF tail]
# When loading Redisrecognizes that the AOF file starts with the "REDIS"
# string and loads the prefixed RDB file, and continues loading the AOF
# tail.
# This is currently turned off by default in order to avoid the surprise
# of a format change, but will at some point be used as the default.
aof-use-rdb-preamble no
4. Redis安全
4.1 Redis安全问题
- 使用redis单独用户和组进行安全部署,并且在OS层面禁止此用户ssh登陆。
- 修改默认端口,降低网络简单扫描危害。
- 修改绑定地址,如果是本地访问要求绑定本地回环。
- 要求设置密码,并对配置文件访问权限进行控制,因为密码在其中是明文。
- HA环境下主从均要求设置密码。
- 在网络防火墙层面进行保护,杜绝任何部署在外网直接可以访问的redis的出现。
4.2 Redis被攻击案例
Redis实例宕机查看日志:
最终原因:
Redis被攻击,采用configset 命令修改数据目录到root用户下,但是由于redis启动用户没有root权限,所以造成aof写入失败,最终造成redis进程挂掉
4.3 Redis高危命令
- save // 阻塞redis做rdb快照
- bgsave // 非阻塞redis做rdb快照
- config // 修改及获取配置
- keys * // 查看所有该实例所有key
- shutdown // 关闭redis
- flushdb // 清除某个db的数据
- flushall // 清楚所有db的数据
- monitor // 实时查看redis正在执行的命令
- bgrewriteaof // 重写AOF文件
- slaveof // 操作Redis主从角色
可通过rename command 禁用
- rename-command save savexss
- rename-command bgsavebgsavexss
- rename-command bgrewriteaofbgrewriteaofxss
- rename-command flushallflushallxss
- rename-command keys keysxss
5. Redis开发
5.1 常用语言客户端
JAVA:
Jedisor Redisson
PHP:
predisor phpredis
Python:
pyredis
C:
hiredis
……
官方推荐:https://redis.io/clients
5.1.1 Jredis
- 轻量,简洁,便于集成和改造
- 支持连接池
- 支持pipelining、事务、LUA Scripting、RedisSentinel、RedisCluster
- 不支持读写分离,需要自己实现
- 文档差
5.1.2 Redisson
- 基于Netty实现,采用非阻塞IO,性能高
- 支持异步请求
- 支持连接池
- 支持pipelining、LUA Scripting、RedisSentinel、RedisCluster
- 不支持事务,官方建议以LUA Scripting代替事务
- 支持在RedisCluster架构下使用pipelining
- 支持读写分离,支持读负载均衡,在主从复制和RedisCluster架构下都可以使用
- 内建Tomcat Session Manager,为Tomcat 6/7/8提供了会话共享功能
- 可以与Spring Session集成,实现基于Redis的会话共享
- 文档较丰富,有中文文档
5.2 LUA的使用
LUA 代码:
local link_id= redis.call("INCR", KEY[1])
redis.call("HSET", KEYS[2], link_id, ARGV[1])
return link_id
Redis执行:
redis-cli EVAL "$(cat incr-and-stor.lua)" 2 links:counterlinks:urlshttp://malcolmgladwellbookgenerator.com/
在EVAL语句中,2指出需要传入的KEY的个数,后面跟着需要传入的两个KEY,最后传入是ARGV的值。在Redis中执行Lua脚本时,Redis-cli会检查传入KEY的个数,除非传入的完全是命令。
https://redis.io/commands/eval
LUA 使用建议:
- LUA 支持一组命令原子执行
- LUA后面的操作能依赖前面操作的执行结果
- LUA 中处理较多也会造成Redis夯住
- LUA排查问题较难
5.3 pipeline使用
5.3.1 pipeline简介
Pipeline 是Redis一种批量执行命令的方式,该方式性能相对于正常要高出很多。
正常命令执行流程VS pipeline:
5.3.2 pipeline使用建议
- 单实例性能无法满足要求
- 需要大批量写入并且
- 允许一定比例的写入失败
5.4 Redis分布式锁
SET resource_namemy_random_valueNX PX 15000
注意,在上面的SET命令中:
my_random_value是由客户端生成的一个随机字符串,它要保证在足够长的一段时间内在所有客户端的所有获取锁的请求中都是唯一的。
NX表示只有当resource_name对应的key值不存在的时候才能SET成功。这保证了只有第一个请求的客户端才能获得锁,而其它客户端在锁被释放之前都无法获得锁。
PX 15000表示这个锁有一个15秒的自动过期时间。当然,这里15秒只是一个例子,客户端可以选择合适的过期时间。
5.4.1 Redis Redlock
- 解决单点故障问题
- 通过多个独立的实例保证可用性
- 每次获取锁需要成功(n+1)/2 个节点
具体参考:https://redis.io/topics/distlock
5.4.2 分布式锁的选择
根据自身业务场景进行选择:
效率型:
允许锁偶尔失效,redis单机锁即可。
正确型:
基于paxos,例如zookeeper
5.5 Redis开发建议
- 容量规划
- 性能规划
- 数据类型选择
- key设计
- 过期设置
- 数据异常处理
- 读写分离
- 主从延迟考虑
- 冷热分离
- 业务独立
- 避免大key
- 避免大事物