Redis
Redis 概念
Redis是基于key-value类型的内存数据库,整个数据库系统加载到内存中进行操作,定期异步操作把数据库数据flush到硬盘上进行保存;因为纯内存操作,redis的的性能非常出色,每秒可以处理10万+的写数据;支持保存多种数据结构的数据结构,单个value的最大限制为1G;
缺点:数据库容量受物理内存限制,不能用作海量的数据的读写,因此Redis适合的场景主要局限在较小的数据量的高性能操作和运算上。
特点:
redis支持持久化,可以将内存中的数据保存在磁盘中,重启的时候将数据再次加载支持数据持久,RDB和AOF
redis不仅支持string类型,还支持list/set/zset/hash数据结构
redis支持数据的备份,主从复制
高可用和分布式:哨兵机制,保障redis节点的故障发现和故障自动转移。
Redis用来干什么
1/用于缓存:
缓存页面数据:大屏查询数据,因为数据需要查询多个表数据计算,很复杂,所以在查询第一次把数据缓存到redis中,下一次就不需要在查询数据库,直接从redis中取。
2/redis分布式锁
3/生成唯一键
4/消息系统
5/计数器的应用
Redis数据结构
1.string
1.1 命令:
set key value :设置键和值 ;批量:mset
get key :获取key的value值;批量:mget
incr key:计数
查看所有键
**keys ***
键总数
dbsize
检查键是否存在
exists key
1.2 内部编码
.int :8个字节的长整型
.embstr:小于等于39个字节的字符串
.raw:大于39个字节的字符串
1.3使用场景
1.缓存
2.计数
3.限速
对于安全考虑,会在每次登陆时让用户进行输入手机验证码,从而确定是否时本人操作,为了不让接口频繁被访问,会限制每分钟获取验证码的频率,例如一分钟不能超过5次;
2. hash
2.1命令
hset key value:设置hash值:hset user:1 name tom
hget key:获取值 :hget user:1 name
hkeys key:获取所有的键:hkeys user:1
hvals key:获取所有值:hvals user:1
2.2 内部编码
.ziplist:当元素可数小于hash-max-ziplist-entries 配置(默认512)同时所有的值都小于hash-max-ziplist-value配置;
.hashtable:当无法满足ziplist时,就会使用hashtable作为内部实现
2.3 使用场景
3.list
列表中的元素是有限的;列表中的元素是可以重复的;
3.1 命令
添加操作从右边插入:rpush listkey a b c
获取从左到右的 所有元素:lrange listkey 0 -1
获取指定下标的数据:lrange listkey 1 3
向某个元素或者后边插入元素: linsert key before|after a java
获取指定下标的元素:lindex listkey b
获取列表长度: llen listkey
删除元素:lpop listkey rpop listkey
阻塞操作:brpop list:test 3 :客户端等到3秒进行返回
3.2 内部编码
.ziplist:当元素可数小于list-max-ziplist-entries 配置(默认512)同时所有的值都小于list-max-ziplist-value配置;
.linkedlist:当无法满足ziplist时,就会使用linkedlist作为内部实现
3.3 使用场景
消息队列:
通过使用lpush+brpop命令可以实现阻塞队列。
通过使用lpush+rpop命令可以实现队列。
通过使用lpush+lpop命令可以实现栈。
文章列表:每个用户有属于自己的文章列表,先需要分页展示文章列表。
4.set
4.1 命令
添加元素:sadd myset a b c
删除元素:srem myset a
计算元素个数:scard myset
判断元素是否在集合中: sismember myset a
获取所有元素: smembers myset
4.2 内部编码
.intset:当元素可数小于set-max-inset-entries 配置(默认512);
.hashtable:当无法满足intset时,当某个元素部位整数时,就会使用hashtable作为内部实现
4.3 使用场景
5.zset
5.1 命令
添加有序集合:zadd user:ranking 1 kris 44 mike 99 tom
计算排名:zrank user:ranking mike
5.2 内部编码
.ziplist:当元素可数小于zset-max-ziplist-entries 配置(默认128)同时所有的值都小于zset-max-ziplist-value配置(64字节);
.skiplist(跳跃表):当无法满足ziplist时,就会使用skiplist作为内部实现;
1.当元素叫少并且每个元素较小;2.当元素个数超过128个时;
.hashtable:当某个元素大于64字节时
5.3 使用场景
排行榜系统
慢查询
1.设置参数
slowlog-log-slower-than:20000
slowlog-max-len:1000
2.命令
slowlog get :获取日志
slowlog-len:获取慢查询日志的长度
redis-cli:
redis-cli -r 3 ping :执行3次ping命令
redis-server:
redis-benchmark:用于性能基准测试
redis-benchmark -c 100 -n 20000:100个客户端同时请求redis,一共执行20000次
3.pipline
发送命令+返回结果称为RTT,往返时间;例如redis客户端部署在北京,redis服务端部署在上海,那一次往返时间:1300x2/(300000x2/3)=13ms,在1s内大约执行80次左右;
pipline只要是减少往返时间,将一组redis命令进行组装成一个命令,在通过一次RTT传给服务端。
4.事务
multi:开始事务;exec:结束事务 ;watch:监听某个值得变化,如果发生变化则不提交事务
5.Bitmaps
本身不是数据结构,实际上是字符串;可以把它想成是以位为单位的数组,数组的每个单元只能存储0或者1;
设置数据:setbit
获取数据:getbit
获取数量:bitcount
6.HyperLogLog
通过它可以利用极小的内存空间完成独立数据的统计;
添加数据:pfadd 2016_01_01:unique:ids "sq1" "sq2"
查看数据个数:pfcount 2016_01_01:unique:ids
只为了计算统计的数量,不需要获取单个数据;可以容忍一定的误差率
持久化
1.RDB
1.1 RDB:是把当前进程数据生成快照保存到硬盘的过程,触发RDB持久化的过程分为手动触发和自动触发;
1.2 触发机制
1.2.1 手动触发:
save 命令:阻塞当前服务器,知道RDB过程完成为止;
bgsave:Redis进程执行fork操作创建子进程,RDB持久化过程由子进程负责,完成后自动结束;
1.2.2 bgsave 流程
1.执行bgsave命令,redis父进程判断当前是否存在正在执行的子进程,如果存在则直接返回;
2.父进程执行fork创建子进程,在该过程中父进程是阻塞的,
3.父进程fork完成后,并不在阻塞父进程,可以继续响应其他命令;
4.子进程创建RDB文件,根据父进程内存生成临时快照文件,完成后对原有文进行替换;
5.子进程发送信号给父进程表示完成,父进程更新系统信息;
1.2.3 文件处理
RDB文件保存dir指定的目录:config set dir{new Dir}
1.2.4 RDB优缺点
优点:紧凑型压缩的二进制文件,代表某个时间点的数据快照;适合备份、全量复制的场景;
缺点:无法做到实时性的持久化、秒级持久化
2.AOF
以独立日志的方式记录每次写命令,重启时在重新执行AOF文件中的命令达到恢复数据的目的;
开启AOF功能需要设置:appendonly yes,默认文件名:appendonly.aof
2.1 AOF的工作流程
1.所有的写命令会追加到aof缓冲区;redis使用单线程响应命令,如果每次写aof文件命令都追加到硬盘,那么完全取决于当前硬盘的负载;多缓存区同步硬盘的策略;
2.aof缓冲区根据对应的策略向硬盘做同步操作;
3.随着aof文件越来越大,需要定期对aof文件进行重写,达到压缩目的;
4.当redis服务器重启时,可以加在aof文件进行数据恢复;
文件同步:
fsync:针对单个文件操作,做强制硬盘同步,fsync将阻塞直到写入硬盘完成后,保证数据的持久化;
write:写入缓冲区后直接返回。
always:每次写入都要同步AOF文件
重写机制:
AOF文件重写是把redis进程内的数据转换为写命令同步到AOF文件的过程
1.执行AOF重写请求
2.父进程执行fork创建子进程;
3.主进程fork操作完成后,继续响应其他命令,所有修改命令依然写入AOF缓冲区并根据appendfsync策略同步到硬盘,保证AOF的正确性;
由于fork操作用于写时复制的技术,子进程只能共享fork操作的内存数据;
4.子进程根据内存快照,快照命令合并规则写入到新AOF文件。
5.新AOF文件写入完成后,子进程发送信号给父进程,父进程更新统计信息;父进程把AOF重写缓冲区的数据写入到新的AOF文件;使用新的AOF文件替换老文件,完成AOF的重写。
2.2 配置文件
#bind 127.0.0.1 #指定redis只接收来自于该IP地址的请求,如果不进行设置,那么将处理所有请求
protected-mode no #不启用保护模式
port 6379 #指定redis运行的端口
tcp-backlog 511
timeout 0 #指定在一个 client 空闲多少秒之后关闭连接(0表示永不关闭)
tcp-keepalive 300 #单位是秒,表示将周期性的使用SO_KEEPALIVE检测客户端是否还处于健康状态,避免服务器一直阻塞,官方给出的建议值是300s,如果设置为0,则不会周期性的检测
daemonize no #默认情况下,redis 不是在后台运行的,如果需要在后台运行,把该项的值更改为yes
supervised no #没有监督互动
pidfile /var/run/redis_6379.pid #当Redis 在后台运行的时候,Redis 默认会把pid 文件放在/var/run/redis.pid,你可以配置到其他地址。当运行多个redis 服务时,需要指定不同的pid 文件和端口
loglevel notice #log 等级分为4 级,debug, verbose, notice, 和warning。生产环境下一般开启notice
logfile "" #配置log 文件地址,默认使用标准输出,即打印在命令行终端的窗口上
databases 16 #设置数据库的个数,可以使用SELECT <dbid>命令来切换数据库。默认使用的数据库是0
#redis数据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快照功能,那么在redis持久化数据到磁盘时如果出现失败,默认情况下,redis会停止接受所有的写请求。此配置可以让用户很明确的知道内存中的数据和磁盘上的数据已经存在不一致了。
rdbcompression yes #存储至本地数据库时(持久化到rdb文件)是否压缩数据,默认为yes
rdbchecksum yes # 在存储快照后,我们还可以让redis使用CRC64算法来进行数据校验,但是这样做会增加大约10%的性能消耗,如果希望获取到最大的性能提升,可以关闭此功能。
dbfilename dump.rdb
dir ./ # 数据库镜像备份的文件放置的路径,AOF文件也会存放在这个目录下面
slave-serve-stale-data yes # 当从库同主机失去连接或者复制正在进行,如果slave-serve-stale-data设置为yes(默认设置),从库会继续相应客户端的请求
slave-read-only yes # 你可以配置一个 slave 实体是否接受写入操作。从 redis 2.6 版起,默认 slaves 都是只读的。
repl-diskless-sync no #主从数据复制是否使用无硬盘复制功能。
repl-diskless-sync-delay 5 #当启用无硬盘备份,服务器等待一段时间后才会通过套接字向从站传送RDB文件,这个等待时间是可配置的。延迟时间以秒为单位,默认为5秒。要关掉这一功能,只需将它设置为0秒,传送会立即启动。
repl-disable-tcp-nodelay no #同步之后是否禁用从站上的TCP_NODELAY,如果选择no,从站的数据延时不会那么多,但备份需要的带宽相对较多。
slave-priority 100 # 从站优先级是可以从redis的INFO命令输出中查到的一个整数。当主站不能正常工作时,redis sentinel使用它来选择一个从站并将它提升为主站。 默认优先级是100
appendonly yes
#默认redis使用的是rdb方式持久化,这种方式在许多应用中已经足够用了。但是redis如果中途宕机,
#会导致可能有几分钟的数据丢失,根据save来策略进行持久化,Append Only File是另一种持久化方式,
#可以提供更好的持久化特性。Redis会把每次写入的数据在接收后都写入appendonly.aof文件,
#每次启动时Redis都会先把这个文件的数据读入内存里,先忽略RDB文件。
appendfilename "appendonly.aof" #aof文件名
appendfsync everysec
#aof持久化策略的配置
# no表示不执行fsync,由操作系统保证数据同步到磁盘,速度最快。
# always表示每次写入都执行fsync,以保证数据同步到磁盘。
# everysec表示每秒执行一次fsync,可能会导致丢失这1s数据
no-appendfsync-on-rewrite no
#在aof重写或者写入rdb文件的时候,会执行大量IO,此时对于everysec和always的aof模式来说,执行fsync会造成阻塞过长时间,no-appendfsync-on-rewrite字段设置为默认设置为no。
#如果对延迟要求很高的应用,这个字段可以设置为yes,否则还是设置为no,这样对持久化特性来说这是更安全的选择。
#设置为yes表示rewrite期间对新写操作不fsync,暂时存在内存中,等rewrite完成后再写入,默认为no,建议yes。
auto-aof-rewrite-percentage 100 #aof自动重写配置,当目前aof文件大小超过上一次重写的aof文件大小的百分之多少进行重写,即当aof文件增长到一定大小的时候,Redis能够调用bgrewriteaof对日志文件进行重写。
#当前AOF文件大小是上次日志重写得到AOF文件大小的二倍(设置为100)时,自动启动新的日志重写过程。
auto-aof-rewrite-min-size 64mb # 设置允许重写的最小aof文件大小,避免了达到约定百分比但尺寸仍然很小的情况还要重写
aof-load-truncated yes
#aof文件可能在尾部是不完整的,当redis启动的时候,aof文件的数据被载入内存。
#重启可能发生在redis所在的主机操作系统宕机后,尤其在ext4文件系统没有加上data=ordered选项,出现这种现象redis宕机或者异常终止不会造成尾部不完整现象,可以选择让redis退出,或者导入尽可能多的数据。
#如果选择的是yes,当截断的aof文件被导入的时候,会自动发布一个log给客户端然后load。
#如果是no,用户必须手动redis-check-aof修复AOF文件才可以。
lua-time-limit 5000
#如果达到最大时间限制(毫秒),redis会记个log,然后返回error。
#当一个脚本超过了最大时限。只有SCRIPT KILL和SHUTDOWN NOSAVE可以用。第一个可以杀没有调write命令的东西。要是已经调用了write,只能用第二个命令杀
slowlog-log-slower-than 10000
# slog log是用来记录redis运行中执行比较慢的命令耗时。
#当命令的执行超过了指定时间,就记录在slow log中,slog log保存在内存中,所以没有IO操作。
#执行时间比slowlog-log-slower-than大的请求记录到slowlog里面,单位是微秒,所以1000000就是1秒。
#注意,负数时间会禁用慢查询日志,而0则会强制记录所有命令。
slowlog-max-len 128
#慢查询日志长度。当一个新的命令被写进日志的时候,最老的那个记录会被删掉,这个长度没有限制。只要有足够的内存就行,你可以通过 SLOWLOG RESET 来释放内存
latency-monitor-threshold 0
#迟监控功能是用来监控redis中执行比较缓慢的一些操作,用LATENCY打印redis实例在跑命令时的耗时图表。
#只记录大于等于下边设置的值的操作,0的话,就是关闭监视。
# 默认延迟监控功能是关闭的,如果你需要打开,也可以通过CONFIG SET命令动态设置。
notify-keyspace-events ""
#键空间通知使得客户端可以通过订阅频道或模式,来接收那些以某种方式改动了 Redis 数据集的事件。因为开启键空间通知功能需要消耗一些 CPU ,所以在默认配置下,该功能处于关闭状态。
hash-max-ziplist-entries 512
#这个参数指的是ziplist中允许存储的最大条目个数,,默认为512,建议为128
hash-max-ziplist-value 64
# ziplist中允许条目value值最大字节数,默认为64,建议为1024
list-max-ziplist-size -2
#当取正值的时候,表示按照数据项个数来限定每个quicklist节点上的ziplist长度。比如,当这个参数配置成5的时候,表示每个quicklist节点的ziplist最多包含5个数据项。
#当取负值的时候,表示按照占用字节数来限定每个quicklist节点上的ziplist长度。这时,它只能取-1到-5这五个值,每个值含义如下:
#2: 每个quicklist节点上的ziplist大小不能超过8 Kb。(-2是Redis给出的默认值)
list-compress-depth 0
#这个参数表示一个quicklist两端不被压缩的节点个数
set-max-intset-entries 512
# 数据量小于等于set-max-intset-entries用intset,大于set-max-intset-entries用set
zset-max-ziplist-entries 128
zset-max-ziplist-value 64
#数据量小于等于zset-max-ziplist-entries用ziplist,大于zset-max-ziplist-entries用zset
hll-sparse-max-bytes 3000
#value大小小于等于hll-sparse-max-bytes使用稀疏数据结构(sparse)
#大于hll-sparse-max-bytes使用稠密的数据结构(dense),一个比16000大的value是几乎没用的,
#建议的value大概为3000。如果对CPU要求不高,对空间要求较高的,建议设置到10000左右
activerehashing yes
#Redis将在每100毫秒时使用1毫秒的CPU时间来对redis的hash表进行重新hash,可以降低内存的使用。
# 如果没有这么严格的实时性要求,可以设置为yes,以便能够尽可能快的释放内存
client-output-buffer-limit normal 0 0 0
#对客户端输出缓冲进行限制可以强迫那些不从服务器读取数据的客户端断开连接,用来强制关闭传输缓慢的客户端。
#对于normal client,第一个0表示取消hard limit,
#第二个0和第三个0表示取消soft limit,normal client默认取消限制,因为如果没有寻问,他们是不会接收数据的
client-output-buffer-limit slave 256mb 64mb 60
#对于pubsub client,如果client-output-buffer一旦超过32mb,又或者超过8mb持续60秒,那么服务器就会立即断开客户端连接。
client-output-buffer-limit pubsub 32mb 8mb 60
# 对于pubsub client,如果client-output-buffer一旦超过32mb,又或者超过8mb持续60秒,那么服务器就会立即断开客户端连接。
hz 10
# redis执行任务的频率为1s除以hz
aof-rewrite-incremental-fsync yes
# 在aof重写的时候,如果打开了aof-rewrite-incremental-fsync开关,系统会每32MB执行一次fsync。
这对于把文件写入磁盘是有帮助的,可以避免过大的延迟峰值
哨兵
1.哨兵概念
1.1 主从复制的问题:
一旦节点出现故障,需要手动将一个节点晋升为主节点,同时需要修改应用方的主节点地址,还需命令将其他节点去复制新的节点,整个过程都是人工干预。
主节点的写能力受到单机能力的限制
主节点的存储能力受到单机的限制
1.2 Redis高可用
1.主节点发生故障,客户端链接主节点失败,两个从节点和主节点链接失败造成复制中断;
2.如果主节点无法正常启动,需要选出一个从节点(slave1),对其执行slaveof no one 命令使其成为新的主节点;
3.原来的从节点(slave2)成为新的主节点后,更新应用方的主节点信息,重新启动应用方;
4.客户端命令另一个从节点(2)去复制新的主节点;
5.待原来的主节点恢复后,让他去复制新的主节点;
Sentinel的功能
1/监控:Sentinel节点会定期检测Redis数据节点,其余Sentinel节点是否可达;
2/通知:Sentinel节点会将故障转移的结果通知给应用方 3/自动故障转移:实现从节点晋升为主节点并维护后续的主从关系;
1.3 Redis Sentinel高可用
每个Sentinel节点会对数据和其余的Sentinel节点进行监控,当发现其他节点不可达时,会对节点做下线标识,如果被表示的是主节点,他还会和其他的Sentinel节点进行协商,当大多数Sentinel节点都认为主节点不可达时,他们会选举出一个Sentinel节点完成自动故障转移,同时将这个变化实时通知Redis应用方。
1.4自动故障转移
主节点发现故障,此时两个从节点与主节点失去链接,主从复制失败;
每个Sentinel节点通过定期监控发现主节点出现的故障;
多个Sentinel节点对主节点的故障达成一致,选举出Sentinel-3节点作为领导者负责故障转移;
1.5 实现原理
监控:
每10s:每个Sentinel节点会向主节点和从节点发送info命令获取最新的拓扑结构;
每2s:每个Sentinel节点会向Redis数据节点的sentinel:hello频道上发送该Sentinel节点对于主节点的判断以及当前Sentinel节点的信息,而且每个Sentinel节点也会订阅该频道,来了解其他的 Sentinel节点以及他们对主节点的判断;发现新的Sentinel节点,如果是加入的Sentinel的节点,将该Sentinel节点信息保存起来,并与该Sentinel的节点创建链接;
每1s:每个Sentinel节点会向主节点、从节点、其余Sentinel节点发送一条ping命令做一次心跳检测;
主观下线和客观下线
每个Sentinel节点会每隔1S对主节点、从节点、其他Sentinel节点发送ping命令做心跳检测,当这些节点超过下线时间并没有进行有效恢复,Sentinel节点会对该节点做失败的判定,叫主观下线;
客观下线:
当Sentinel主观下线的节点是主节点 时,该节点会向其他的节点询问对主节点的判断,当超过半数,Sentinel节点就会认为该主节点确实有问题,这是就会对该节点做出客观下线的判断。
选举:
1.每个在线的Sentinel节点都会有资格称为领导者,当他确认主节点主观下线的时候,会向其他的Sentinel节点发送命令,要求将自己设置为领导者;
2.收到命令的Sentinel节点,同意则发送同意,如果不同意则发送拒绝;
3.如果该Sentinel节点发现自己的票数大于半数,那么它将成为领导者;
4.如果此过程没有选举出领导者,将进入下一次选举。
故障转移:
1.过滤不健康(主观下线)、5s没有回复过Sentinel节点ping命令、与主节点失联超过10s;
2.选举从节点优先级最高的从节点列表,如果存在则返回,不存在则继续;
3.选择复制偏移量最大的从节点;
4选择runid的最小的节点
缓存设计
1.缓存的优缺点
优点:加速读写,优化用户体验、降低后端负载:帮助后端减少访问量和复杂计算,很大程度的降低了后端的负载;
缺点:数据不一致性,代码维护成本、运维成本;
2.缓存更新策略
2.1 LRU/LFU/FIFO算法剔除
剔除算法通常用于缓存使用量超过预设的最大值的时候,设置maxmemory-policy
2.2 超时剔除
超时剔除通过给缓存数据设置过期时间,让其在过期时间后自动剔除;如果业务可以容忍一段时间内,缓存层数据和存储层的数据不一致,那么可以为其设置过期时间。
2.3 主动更新
应用方对于数据的一致性要求高,需要在真实数据更新后, 立即更新缓存数据。
低一致性:建议配置最大内存和淘汰策略的方式使用
高一致性:超时剔除和主动更新
3.缓存优化
缓存穿透:查询一个根本不存在的数据,缓存层和存储层都不会命中,通常处于容错的考虑;
解决办法:
缓存空对象:如果存储层不命中的话,仍然将对象保留到缓存层中,之后在访问这个数据直接从缓冲中获取,这样保护了后端的数据;但是也意味着需要更多的内存空间,可以设置一个较短的过期时间,让其自动剔除;
布隆拦截过滤器:
在访问缓存层和存储层之之间,将存在的key用布隆过滤器提前保存起来,做第一层拦截;
4.雪崩优化
由于缓存层承载着大量请求,有效的保护了存储层,如果由于缓存层由于某些原因不能提供服务,于是所有请求都会到达存储层,存储层调用量大增,造成存储 层也会级联宕机的情况。
1.保证缓存层服务高可用性 :集群
2.依赖隔离组件为后端限流并降级
5.热点KEY重建优化
当前key是热点key失效,并发量非常大,重建缓存不能在短时间完成,在缓存失效的瞬间,有大量线程来重建缓存,造成后端负载加大,甚至可能让应用崩溃;
1.互斥锁:此方法只允许一个线程重建缓存,让其他线程等待重建缓存的线程执行完,重新从缓存中获取数据即可。
2.永远不过期
缓存不过期:没有设置过期时间,所以不会出现热点key过期的产生的问题;
为每个key设置一个逻辑过期时间,当发现超过逻辑过期时间后,会使用单独线程去构建缓存;
内存
redis进程的内存消耗:自身内存+对象内存+缓冲内存+内存碎片
1:对象内存:是redis内存中占用最大的一块,存储着用户的所有数据;对象内存的消耗是sizeof(keys)+sizeof(values);
对象都是字符串,使用redis时很容易忽略键对内存消耗的影响,应避免使用过长的键。
2.缓冲内存:客户端缓冲、复制积压缓冲区、AOF缓冲区
客户端缓冲区:redis服务器TCP连接的输入输出缓冲,输入输出缓冲无法控制,最大为1g;
复制积压缓冲区:可重用的固定大小的缓冲区用于实现部分复制功能,默认大小为1MB;
AOF缓冲区:在重写期间保存最近的写命令。
3.内存碎片:对象的大小小于内存块单位,就会产生内存碎片;
频繁做更新操作:频繁对已存在的键执行append等更新操作;
大量过期键删除:键对象过期删除后,释放的空间无法得到充分的利用,导致碎片率上升;
1.内存回收策略
删除到达过期时间的键对象;内存使用达到maxmemory上限时触发内存溢出控制策略
1.惰性删除
用于当客户端读取的带有超时属性的键时,如果已经超过键设置的过期时间,会执行删除操作并返回空;但是会存在内存泄漏的问题,因为过期键一直没有访问无法得到删除,从而导致内存吧不能及时释放。
2.定时任务删除
redis内部维护一个定时任务,每秒运行10次。
1.定时任务在每个数据库空间随机检查20个键,当发现过期时删除对应的键
2.如果超过25%的键过期,循环执行回收逻辑知道不足25%或运行超时为止,慢模式下超时时间为25ms
3.如果之前回收的键超时,则在redis触发内部事件之前再次以快模式回收过期键任务。
2.内存溢出控制策略
noeviction:默认策略:不会删除任何数据,拒绝所有写入操作并返回客户端错误信息;
volatile-lru:根据LRU算法删除设置了超时属性的键,知道腾出空间为止。
allkeys-lru:根据LRU算法删除键,不管数据有没有设置超时属性,知道腾出足够空间为止;
allkeys-random:随机删除所有的键,直到腾出足够空间为止
volatile-random:随机删除过期键,直到有足够的空间为止
volatile-ttl:删除最近即将要过期的数据。
分布式锁
保证这个任务同一时刻只能被多个进程中的某一个执行,那这样的锁就是分布式锁了
1.数据库
2.Redis
2.1 多进程可见
首先redis基于JVM之外的,因此满足多进程可见的要求;
2.2 互斥
在分布式高并发的条件下,我们最需要保证,同一时刻只能有一个线程获得锁,这是最基本的一点;
2.3 释放锁
为了避免在因服务不可用时造成锁释放不了,应该架一个过期时间,当时间到了,进程会主动释放锁。
2.4 可重入性
可重入锁:同一个线程可以重复拿到同一个资源的锁。
获取锁:首先尝试获取锁,如果获取失败,判断这个锁是否是自己的,如果是则允许再次获取,而且必须记录重复获取锁的次数。
释放锁: 释放锁不能直接删除了,因为锁是可重入的,如果锁进入了多次,在 内层直接删除锁,导致外部的业务在没有锁的情况下执行,会有安全问题。因此必须获取锁时累计重入的次数,释放时则减去重入次数,如果减到*0,则可以删除锁
2.5 锁的性能
1、锁的颗粒度要尽量小。比如你要通过锁来减库存,那这个锁的名称你可以设置成是商品的ID,而不是任取名称。这样这个锁只对当前商品有效,锁的颗粒度小。
2、锁的范围尽量要小。比如只要锁2行代码就可以解决问题的,那就不要去锁10行代码了。
2.6 获取锁和释放锁的基本流程
2.7 Redisson分布式锁
1.加锁机制:
线程去获取锁,获取成功: 执行lua脚本,保存数据到redis数据库。
线程去获取锁,获取失败: 一直通过while循环尝试获取锁,获取成功后,执行lua脚本,保存数据到redis数据库。
2.watch dog自动延期
在一个分布式环境下,假如一个线程获得锁后,突然服务器宕机了,那么这个时候在一定时间后这个锁会自动释放,你也可以设置锁的有效时间(不设置默认30秒),这样的目的主要是防止死锁的发生。
它的作用就是 线程1 业务还没有执行完,时间就过了,线程1 还想持有锁的话,就会启动一个watch dog后台线程,不断的延长锁key的生存时间。
3.Zookeeper
\