redis学习笔记
远程字典服务 Remote Dictionary Server
1.特性及应用场景
--特性:单线程,纯内存访问,支持数据持久化,非阻塞IO,C/C++便捷,键值存储,支持主从、集群,支持事务,支持消息队列(一般用activeMq),读写速度快(读11w/s,写8w/s),官方不支持window(Linux版本已经相当稳定,用户量很大,无需开发windows版本,反而会带来兼容性等问题,但是有第三方版本,客户端管理工具常用Redis Desktop Manager)。
--应用场景:缓存,队列,网站访问计数统计,购物车,分布式session,应用排行版,社交关系图。
2.主要数据类型及部分常识
--基本数据类型5种:字符串String、列表List、哈希Hash、集合Set、有序集合Zset,每个类型语法不一样,比如塞值分set、lpush、hset、sadd、zadd。
--默认配置数据库16个,可以通过redis.conf修改数量,客户端默认指定0号DB,dbsize查看当前数据库key的数量,flusbdb清空当前库,flushall清空全部库。
--16个数据库统一密码,可config设置数量和密码,默认端口6379.。
--常用命令
keys * 获取所有的key
select 0 选择第一个库
flush db 清除指定库
type key 类型
set key1 value1 设置key
get key1 获取key
mset key1 value1 key2 value2 key3 value3
mget key1 key2 key3
del key1 删除key
exists key 判断是否存在key
expire key 10 10过期
pexpire key 1000 毫秒
persist key 删除过期时间
incr age 递增
incrby age 10 步递增
decr age 递减
decrby age 10 步递减
--常见面试题
3.持久化方式
--RDB内存快照(默认开启):恢复速度快,持久化性能高,但是存在数据丢失风险。适合大规模并且一致性要求不高的数据恢复。
dump.rdb是由Redis服务器自动生成的,默认情况下,每隔一段时间redis服务器程序会自动对数据库做一次遍历,把内存快照写在一个叫做“dump.rdb”的文件里,这个持久化机制叫做SNAPSHOT。有了SNAPSHOT后,如果服务器宕机,重新启动redis服务器程序时redis会自动加载dump.rdb,将数据库状态恢复到上一次做SNAPSHOT时的状态。至于多久做一次SNAPSHOT,SNAPSHOT文件的路径和文件名,你可以在redis的conf文件里指定。恢复过程redis会复制(fork)一个相同的进程将数据写入到一个临时文件,持久化过程结束再将这个临时文件替换上次持久化好的文件。redis.conf配置文件开启save <指定时间间隔> <执行指定次数更新操作>,满足条件就将内存中的数据同步到硬盘中。 指定本地数据库文件名dbfilename dump.rdb,默认开启数据压缩rdbcompression yes
--AOF日志文件追加纪录:实时持久化,数据安全性更高,持久化效率低。
日志的形式来记录每个写操作,并追加到文件中。Redis 重启的会根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作。redis.conf配置文件开启appendonly yes,指定本地数据库文件名,默认值为 appendonly.aof,指定更新日志条件默认每秒appendfsync everysec,数据还原失败,可以通过命令redis-check-aof --fix appendonly.aof 进行修复。
如果同时开启了2种持久化方式,系统会默认选择AOP恢复数据(数据更完整)。
4.主从复制
--原理:复制RDB文件,复制缓冲区
--拓扑:主从(单节点),主从(多节点),主从从(树形结构)
--手动版:主从复制是全量复制,从机只能读取不能写入,主机宕掉后需要运维手动修改从机属性,主机回来后仍然是主机,从机宕掉后回来变主机,除非配置文件指定当前redis的主从属性。
--哨兵模式:实现对Redis的主从及运行情况监控、故障检测通知、主从切换(从机配置好后,Sentinel.conf中指定哨兵sentinel monitor 主机名 主机地址 主机端口 票数,从机票数符合后转为主机,原主机启动后转为从机)。
--主从缺点:所有写操作在主机,当系统繁忙时,同步会有延迟,从机数量增加后问题会更严重。
5.分布式
--高可用:24小时对外提供服务,高并发:同一时间段能处理的请求数。
--哨兵模式会有中心化的问题,所有节点都要有一个主节点,中心挂了服务就挂了,且中心处理数据能力有限,各个节点数据重复,不能把节点性能发挥到最大,就是一个路由作用。
--去中心化:去掉路由,客户端自己来路由选择读写。
--redis集群的数据分片:redis集群有16384个hash槽,每个key通过crc16算法检验后对16384取模来决定放置哪个槽,集群的每个节点负责一部分hash槽。添加删除或者改变某个节点hash槽数量不会造成集群不可用。
--redis集群的主从复制:防止部分节点无法通讯情况下集群仍然可用,可以使用主从复制。比如有A,B,C三个节点的集群,在没有复制模型的情况下,如果节点B失败了,那么整个集群就会以为缺少5501-11000这个范围的槽而不可用。
--redis的一致性保证
6.内存
--内存消耗
对象内存:存储所有数据
缓冲内存:客户端缓冲(通过client-output-buffer-limit控制),复制积压缓冲区(根据repl-backlog-size控制),AOF缓冲区(用于在redis重写AOF文件期间保存最近写入的命令)
内存碎片:可采用数据对齐和安全重启等方式规避内存碎片的问题
--内存回收策略:
方式1:删除过期健对象
惰性删除:客户端读取带有超时属性的键时,如果已经超过键设置的过期时间,会执行删除操作并返回空,这种策略是出于节省CPU成本考虑,不需要单独维护TTL链表来处理过期键的删除。但是单独用这种方式存在内存泄露的问题,当过期键一直没有访问将无法得到及时删除,从而导致内存不能及时释放。
定时任务删除:Redis内部维护一个定时任务,默认每秒运行10次(通过配置hz控制)。
方式2:内存溢出控制策略
当Redis所用内存达到maxmemory上限时会触发相应的溢出控制策略,具体策略受maxmemory-policy参数控制,Redis支持8种策略。
noeviction:默认策略,当内存不足以容纳新写入数据时,新写入操作会报错。应该没人用吧。
allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的 Key。推荐使用,目前项目在用这种。
allkeys-random:当内存不足以容纳新写入数据时,在键空间中,随机移除某个 Key。应该也没人用吧,你不删最少使用 Key,去随机删。
volatile-lru:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的 Key。这种情况一般是把 Redis 既当缓存,又做持久化存储的时候才用。不推荐。
volatile-random:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,随机移除某个 Key。依然不推荐。
volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,有更早过期时间的 Key 优先移除。不推荐。如果没有对应的键,则回退到noeviction策略。
--内存优化:
缩短键值对象的长度,共享对象池,字符串优化,编码优化,控制key的数量
7.缓存问题隐患
--缓存无底洞:为了满足业务大量加节点,但是性能没提升反而下降。
--缓存穿透:频繁查询一个不存在的数据,由于缓存不命中,每次都要查询持久层。
解决方案:1.布隆过滤器(是一种二进制组成的数据结构),2.缓存空对象
--缓存击穿:key在redis中过期,大量并发请求过来可能会瞬间把后端DB压垮。
解决方案(都不是万能的):1.synchronized锁(单机可用),2.分布式锁setnx(setnx:key值存在不做任何动作,key值不存在才和set key value相同动作)/redission(底层核心是一个lua脚本,执行一个原子操作,默认锁超时时间30s,如果redis设置成主从/哨兵,分布式锁可能会出现失效问题,比如主redis设置锁后,客户端获得锁true后执行业务,但是主redis在同步从redis前挂了,从redis变主redis,但是锁已经不存在了,可以考虑配合Zookeeper实现分布式锁)/redlock(损失部分可用性用来实现一致性,实现原理和zk差不多,半数以上从redis同步数据后反馈客户端true)
--缓存雪崩:缓存大量失效或者缓存层出现了错误的时候,引发大量查询数据库。
解决方案:1.redis高可用(多增设几台redis),2.限流降级(缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其他线程等待)
--热点key:流量过于集中,达到物理网卡上限,从而导致这台redis的服务器宕机。
解决方案:1.服务端JVM缓存:即将热点数据缓存至服务端的内存中,2.备份热点Key:即将热点Key+随机数,随机分配至Redis其他节点中。这样访问热点key的时候就不会全部命中到一台机器上了。
8.redis事务(了解)
multi : 开启事务,exec : 提交事务,discard : 取消事务,watch 监控key( 类似乐观锁 ),unwatch : 取消监控key。
watch指令类似于乐观锁,在事务提交时,如果watch监控的多个KEY中任何KEY的值已经被其他客户端更改,则使用EXEC执行事务时,事务队列将不会被执行,同时返回Nullmulti-bulk应答以通知调用者事务执行失败。
事务的错误模式:全体连坐(命令性错误(类似于java编译性错误),所有命令都不会执行),冤头债主(语法性错误(类似于java的1/0的运行时异常),其他正确命令会被执行,错误命令抛出异常)。
单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
没有隔离级别的概念:队列中的命令没有提交之前都不会实际的被执行,因为事务提交前任何指令都不会被实际执行。
不保证原子性:redis同一个事务中如果有一条命令执行失败,其后的命令仍然会被执行,没有回滚。