redis
NoSql
- 非关系型数据库与关系型数据库的区别
①数据的存储方式不一样。关系型数据库以行列对数据进行存储,并且表与表之间可以关联协作存储。而非关系型数据库中的数据是大块组合在一起,通常存储在数据集中,就像文档、键值对、图结构
②拓展方式不一样。关系型数据库拓展方式为纵向拓展,即提升系统性能、处理能力;非关系型数据库可以横向拓展,增加节点,分担负载。
③对事务的支持不一样。关系型数据库对事务支持较好,并且易于回滚;非关系型数据库对事务的支持没有那么好,稳定性不高。
- 其他NoSql:memcache、mongodb
①redis
优点:数据类型丰富,支持简单事务,支持持久化,支持主从,并发时不需要考虑一致性问题,支持发布/订阅
缺点:单线程执行命令,性能受限于cpu处理能力,支持简单的事务,并不成熟,string类型比较耗内存
②memcache
优点:多核优势明显,但实力吞吐量高
缺点:只支持简单的key/value的数据结构,无法持久化,无法数据同步
③mongodb
优点:插入速度快,支持更高的写负载,查询也很快速
缺点:占用空间大,不支持事务
redis优势
- 读写速度快,读110000次/s 写81000次/s
- 丰富的类型,string、hash、list、set、zset
- 所有单个操作都是原子性的,要么成功要么失败完全不执行;多个操作支持事务但是不保证原子性,即失败了不影响执行成功的,命令MUTIL EXEC
- 丰富的特性:发布订阅、通知、key过期等
redis特性
- 速度快;简单稳定; 支持多种数据结构;支持多种编程语言;功能丰富;持久化;主从复制;高可用和分布式
redis为什么那么快
Redis 使用了单线程架构和 IO 多路复用模型来实现高性能的内存数据库服务。
- redis使用纯内存访问,所有数据都在内存中
- 非阻塞IO,redis使用epoll实现多路IO复用技术
- 单线程避免了线程切换和竞争产生的消耗
redis为什么稳定?是因为单线程吗?
- 单线程模型,使得服务端处理和客户端开发都比较简单
- 不依赖底层操作系统类库,自己实现了事务功能
redis为什么能做分布式锁
- redis命令遵循原子性,保证多个实例竞争一个锁时,只有其中一个实例能获取成功,如提供了setnx命令,如果某个key当前不存在则设置成功并返回true,否则不再重复设置,直接返回false。
方式一:
- ①setnx(key, value) key为锁资源名称,value:当前时间 + 占用锁的时间
- ②setnx成功则说明加锁成功,对key设置过期时间(占用锁的时间),处理业务逻辑,处理完删除key(释放锁)
- ③setnx失败,则说明已经存在该key,说明锁可能已经被占用,利用get取出其value,对比当前时间,如果大于当前时间,说明锁已经被占用,获取锁资源失败;如果小于当前时间,说明锁已经过期却没有被删除,直接getset(key, value)一次,成功获取锁资源,处理业务逻辑,处理完毕删除key,释放锁。
以上方式存在问题:
- 怎么保证A加的锁不被B释放?
- 怎么保证在业务逻辑执行完成之前,key不过期导致锁被释放?
方式二:
- ①利用多参数set方法,set成功则说明key在set前不存在,意味着加锁成功。
key:锁资源名称、value:唯一请求id,标识客户端、nxxx:NX -- Only set the key if it does not already exist、expx:expire time units: EX = seconds; PX = milliseconds、time:过期时间
- ②利用Redisson 的watch dog机制,不断刷新key的过期时间,保证key在业务代码执行完成前不会过期,这样可以避免在业务代码执行期间释放锁。
- ③释放锁(del),在锁被释放前,其他线程通过校验get得到的requestId,可以发现锁不是自己加的,所以无法去解锁。
- 小结:这种方式可以解决redis宕机导致死锁问题,可以保证锁只会被加锁客户端去解锁,可以保证锁在业务代码执行完成之后才会被释放。但是redis主从、哨兵模式下,master节点宕机的场景下,还是会出现死锁的问题。
redis持久化
- AOF 操作日志 (系统会优先使用)
- RDB 内存快照
RDB原理
通过内存快照实现,在指定的时间间隔内将内存中的数据写入磁盘中,恢复时将快照文件读取到内存中
触发RDB的三种方式:
- 手动触发使用save、bgsave
①save:阻塞redis服务器,直到RDB完成为止,如果redis数据内存过大,阻塞时间会较长,不建议使用,redis内部已被废弃
②bgsave:redis进程执行fork操作,创建子进程,RDB持久化过程由子进程完成,完成后自动结束。阻塞只发生在fork阶段,一般时间很短 (为什么要fork子进程:因为redis内部是单线程的,内存快照需要redis进行文件IO操作,fork出一个子进程可以保证主进程不进行IO操作,保持性能)
- 其他命令被动触发RDB
①FLUSHALL
②SHUT DOWN
③DEBUG RELOAD
- 通过redis.conf配置文件设置save
bgsave的工作流程
- 执行bgsave,redis主进程会检查现在是否有执行bgsave的子进程,如果有则返回
- 主进程执行fork操作创建子进程,fork操作过程中主进程是阻塞的
- 主进程fork完成后,bgsave命令返回,并不再阻塞主进程
- 子进程创建RDB文件,根据主进程内存生成临时快照文件,完成后对原有RDB文件进行原子替换
- 子进程发送信号给主进程表示完成,主进程更新统计信息
RDB的优缺点
①优点
- 对数据的完整性要求不高
- 主进程不进行IO操作,因此对大规模数据的回复有着极高的性能,适合全量恢复、备份等场景
②缺点
- fork操作时,会占用一定的内存空间
- 需要一定的时间间隔来进行操作,如果redis意外宕机,那么最后一次的修改会丢失
AOF持久化原理
AOF持久化是以独立的日志记载写命令,重启时执行AOF文件中的命令以达到恢复数据的目的。AOF的主要作用的解决了数据持久化的实时性,目前是redis持久化的主流方式。
- 命令的实时写入,调用到命令
- 所有写命令都追加到aof_buf(缓冲区)中,
- AOF缓冲区根据对应的策略向硬盘做同步操作
- 随着AOF越来越大,需要定期对AOF文件进行重写
- redis重启时,加载AOF文件进行数据恢复
AOF文件重写
- 主进程fork创建子进程
- 由子进程根据主进程内存快照执行AOF重写
- 主进程继续相应后面的命令,并写入重写缓冲区中(aof_rewrite_buf),在子进程完成重写后,再将重写缓冲区的命令写入新的AOF文件中
AOF缓冲区与AOF重写缓冲区区别,缓冲区的同步策略
- 所有写命令都会进入AOF缓冲区,最后根据同步策略写入AOF文件中
- AOF重写缓冲区是在重写过程中,主进程接收到的写命令会写入重写缓冲区中,当重写完成时,主进程会将重写缓冲区的数据写入新的AOF文件中,然后替换原来的AOF文件
缓冲区同步策略:通过参数appendfsync控制
- always:命令写入aof_buf后调用系统fsync操作同步到AOF文件,fsync完成后线程返回
- everysec:命令写入aof_buf后调用系统write操作,write完成后线程返回。fsync同步操作由线程每秒调用一次(建议策略)
- no:命令写入aof_buf后调用系统write操作,不对AOF文件做fsync同步,同步硬盘操作由操作系统负责,通常同步周期最长30秒
AOF重写为什么能使AOF文件变小
- 重写时,进程内已经过时的数据,不再写入新的AOF文件中
- 旧的AOF文件内含有很多无效命令,重写使用内存数据直接生成,新的AOF文件只保留最终的数据的写入命令
- 多条写命令可以合并为一条,为了防止溢出,以64个元素为界拆分为多条
redis缓存穿透
缓存穿透:故意访问缓存中没有的数据,使得查询落到数据库上,导致数据库压力增大
解决方案:
- ①查询redis后返回空值,缓存至内存中,并设置过期时间
- ②布隆过滤器
布隆过滤器
原理:将写入redis中的数据的key进行hash,将多个hash算法的结果放入一个足够大的数组中,后续收到查询请求时,对key进行同样的hash,如果hash结果作为index在数组中都能找到数据,则这个key可能存在于redis数据中;否则,一定不存在与redis数据中,此场景则无需访问redis。
redis缓存击穿
缓存击穿:某个key在被高并发访问时缓存过期,导致大量并发请求落到数据库,造成数据库压力。
对过期的key加锁;redis SETNX命令。保证初次访问数据库后,会回设一个值在缓存中
Redis缓存雪崩
缓存雪崩:缓存层出现问题,不能正常工作。于是所有的请求都会访问数据库,给数据库造成压力。
解决方案:
①redis集群,保证redis高可用
②数据预热
③设置不同的过期时间
redis缓存过期与内存淘汰策略
- 缓存过期
①定时过期:针对每个设置了过期时间的key起一个定时器,过期则清除。对内存友好,对cpu不友好。
②惰性过期:对于过期的key,只有当访问这个key时,才去校验并清除。对cpu友好,对内存不友好。
③定期过期:定期去扫描部分设置了过期时间的数据,key如果已经过期,则清除。是内存和cpu折中的方案。
- 内存淘汰策略
小结:过期策略主要是针对设置了过期时间的数据过期时的淘汰策略;内存淘汰策略是指redis在内存不足时,采取的内存淘汰机制。
redis主从复制原理
①主从复制比较简单,当客户端给从服务发送SLAVEOF命令后,从服务会向主服务发送SYNC命令。
②收到SYNC命令的主服务器执行bgsave命令,fork出一个子进程,生成一个最新的RDB文件并使用一个缓冲区记录从现在开始执行的所有写命令。
③当主服务的bgsave命令执行完毕,主服务器将生成的RDB文件发送给从服务器,从服务器接收并加载这个RDB文件,将自己的数据库状态更新至主服务器执行bgsave命令时的数据库状态。
④主服务器将记录在缓冲区里面的所有写命令发送给从服务器,从服务器执行这些写命令,将自己的数据库状态更新至主服务器当前所处的状态。
主从全量同步完成之后,之后每次执行写命令后,主服务会同步至从服务,保证主从数据一致。
参考:https://www.cnblogs.com/CryFace/p/13498188.html