目录
1 NoSQL
1.1 NoSQL特点
1.2 NoSQL对比
1.3 关系型数据库和非关系型数据库
2 介绍
2.1 概述
2.2 Redis能干什么
2.3 特性
2.4 单线程+多路IO复用
3 数据类型
3.1 五大数据类型
3.1.1 String(字符串)
3.1.2 Hash(哈希)
3.1.3 List(列表)
3.1.4 Set(集合)
3.1.5 Zset(有序集合)
3.2 三种特殊数据类型
3.2.1 Geospatial(地理位置)
3.2.2 Hyperloglog(基数统计)
3.2.3 BitMaps(位图)
4 事务
4.1 Redis事务三特性
4.2 Multi、Exec、discard
4.3 事务的错误处理
5 持久化
5.1 RDB(Redis DataBase)
5.1.1 fork进程
5.1.2 RDB持久化流程
5.1.4 触发RDB快照
5.1.5 RDB的备份
5.1.6 总结
5.2 AOF(Append Only File)
5.2.1 AOF持久化流程
5.2.2 启动/修复/恢复
5.2.3 AOF同步频率
5.2.4 Rewrite压缩
5.2.5 总结
5.3 总结
6 主从复制
6.1 复制原理
6.2 常见的3种情况
6.3 哨兵模式
7 集群
1 NoSQL
NoSQL(NoSQL = Not Only SQL ),意即“不仅仅是SQL”,泛指非关系型的数据库。
1.1 NoSQL特点
- 方便扩展:数据之间没有关系,很好扩展。
- 大数据量、高性能:Redis是内存数据库,一秒写8万次,读取11万,NoSQL的缓存记录级,是一种细粒度的缓存,性能会比较高。
- 灵活的数据模型:不需要事先设计数据库,可以随时根据存储的需要自定义数据格式。
- 高可用:nosql在不太影响性能的情况之下,就能够非常方便的实现高可用的架构。
- 不遵循SQL标准
- 不支持ACID
适用于
不适用于
- 需要事务支持
- 基于sql的结构化查询存储,处理复杂的关系
1.2 NoSQL对比
1.3 关系型数据库和非关系型数据库
定义
关系型数据库:指采用了二维表格模型来组织数据的数据库,也就是由二维表及其之间的联系所组成的一个数据组织。
非关系型数据库:分布式且一般不保证遵循ACID原则,以键值对存储,结构不固定,每一行可有不一样的列,不局限于固定结构,可减少时间空间花销。
关系型数据库
优点
- 易于维护(格式一致)
- sql通用,适用简易,而且也支持复杂操作(一个表以及多个表之间非常复杂的查询)
缺点
- 读写性能比较差,如果遇到大数据的读写,效率低
- 结构固定,导致灵活性很低
非关系型数据库
优点
- 根据添加的字段获取不同信息,不像关系型数据库需要多表关联查询
- 扩展性高,适用SNS中,一些软件上功能系统的提升。因为其结构问题,严格上不是一种数据库,是一种数据结构化存储方法的集合
- 速度快:nosql可以使用硬盘或者随机存储器作为载体,而关系型数据库只能使用硬盘
- 成本低:nosql数据库部署简单,基本都是开源软件
缺点
- 数据结构相对复杂,对于需要进行较复杂查询的数据,关系型数据库显的更为合适
- 不适合持久存储海量数据
- 无事务处理
2 介绍
2.1 概述
Redis(Remote Dictionary Server ),即远程字典服务。Redis 是一个免费开源的、遵守BSD协议的、高性能的key-value 内存型数据库。
它具有丰富的数据结构:如字符串(strings),散列(hashes),列表(lists),集合(sets),有序集合(sorted sets)与范围查询,bitmaps(位图),hyperloglogs(基数统计)和地理空间(geospatial)索引半径查询。
Redis 内置了复制(replication),LUA脚本(Lua scripting), LRU驱动事件(LRU eviction),事务(transactions) 和不同级别的磁盘持久化(persistence),并通过Redis哨兵(Sentinel)和自动分区(Cluster)提供高可用性(high availability)。它可以用作数据库、缓存和消息中间件。
2.2 Redis能干什么
-
内存存储、持久化,内存是断电即失的,所以需要持久化(RDB、AOF)
-
高效率、用于高速缓存
-
发布订阅系统
-
地图信息分析
-
计时器、计数器(eg:浏览量)
2.3 特性
- 多样的数据类型
- 持久化
- 集群
- 事务
2.4 单线程+多路IO复用
Redis是基于内存操作的,Redis的性能瓶颈不是CPU,而是机器内存和网络带宽。
多路io复用:
- 使用一个线程来检查多个文件描述符(Socket)的就绪状态(比如调用 select 和 poll 函数,传入多个文件描述符)
- 如果有一个文件描述符就绪,则返回,否则阻塞直到超时
- 得到就绪状态后进行真正的操作可以在同一个线程里执行,也可以启动线程执行(比如使用线程池)
3 数据类型
它支持字符串(strings),散列(hashes),列表(lists),集合(sets),有序集合(sorted sets)与范围查询,bitmaps(位图),hyperloglogs(基数统计)和地理空间(geospatial)
在redis中无论什么数据类型,在数据库中都是以key-value形式保存,通过进行对Redis-key的操作,来完成对数据库中数据的操作。
命令:
- exists key:判断键是否存在
- del key:删除键值对
- move key db:将键值对移动到指定数据库
- expire key second:设置键值对的过期时间
- type key:查看value的数据类型
- keys * :查看当前数据库所有key
- ttl key: 查看key的过期剩余时间,-2 表示key过期,-1表示key未设置过期时间,正数表示剩余时间值
更多命令学习:https://www.redis.net.cn/order/
3.1 五大数据类型
3.1.1 String(字符串)
- 一个key对应一个value
- 二进制安全的,即可包含任何数据
- value最多可以是512m
数据结构:
struct sdshdr{
//记录buf数组中已使用字节的数量
int len;
//记录buf数组中未使用的数量
int free;
//字节数组,用于保存字符串
char buf[];
}
常用命令:
- set key value 设置key值
- get key 查询key值
- mset k1 v1 k2 v2 k3 v3 ... 表示同时设置多个键值对
- mget k1 k2 k3 同时获取多个
- append key value 在字符串后面追加value,返回数字,如果key不存在,就相当于set key
- strlen key 返回key的值的字符串长度
- incr key 将key值存储的数字增1,只对数字值操作,如果为空,新增值为1
- decr key 将key值存储的数字减1,只对数字值操作,如果为空,新增值为1
- incrby/decrby key 10 给这个key设置每次增加/自减10,设置步长,指定增量
- setex key second value 表示设置后多少秒过期,seconds表示过期时间。ex就是expire
- setnx key value 表示如果这个key不存在才设置,如果存在则不设置。nx就是 not exist
使用场景:
- 计数器(incrby,decrby自增自减具有原子性)
- 统计多单位的数量:uid:123666:follow 0
- 粉丝数
- 对象存储缓存(json)
3.1.2 Hash(哈希)
Redis hash 是一个string类型的field和value的映射表,hash特别适合用于存储对象。可以将一个Hash表作为一个对象进行存储,表中存放对象的信息。
用户ID为查找的key,存储的value用户对象包含姓名,年龄,生日等信息,如果用普通的key/value结构来存储,有两种方式
数据结构:Hash类型对应的数据结构是两种:ziplist(压缩列表),hashtable(哈希表)。当field-value长度较短且个数较少时,使用ziplist,否则使用hashtable。
typedef struct dictht{
//哈希表数组
dictEntry **table;
//哈希表大小
unsigned long size;
//哈希表大小掩码,用于计算索引值
unsigned long sizemask;
//该哈希表已有节点的数量
unsigned long used;
}
typedef struct dictEntry{
//键
void *key;
//值
union{
void *val;
uint64_tu64;
int64_ts64;
}
struct dictEntry *next;
}
- hset key field value 给key集合中的filed键赋值value
- hget key1 field 集合field取出value
- hmset key1 field1 value1 field2 value2 ... 批量设置hash的值
- hmget key1 field1...批量获取hash的值
- hgetall key 获取所有的键值对
- hdel key field 删除指定的key的字段,对应的value也就没有了
- hlen key 获取这个hash中有多少个键值对
- hexists key field 查看哈希表key中,给定域field是否存在
- hkeys key 获取hash集合的所有field
- hvals key 获取hash集合的所有value
- hincrby key field increment 为哈希表key中的域field的值加上增量1 -1
- hsetnx key field value 如果不存在可以设置,如果存在,则不能设置
应用场景:
保存变更的数据,可以将一个用户变形为hset user:id:name zhangsan,因此hash更适合存储对象,而string比较适合存储字符串
3.1.3 List(列表)
Redis列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)。它的底层实际是个双向链表,对两端的操作性能很高,通过索引下标的操作中间的节点性能会较差。
数据结构:
typedef struct listNode{
//前置节点
struct listNode *prev;
//后置节点
struct listNode *next;
//节点的值
struct value;
}
Redis中List是可以进行双端操作的,所以命令也就分为了LXXX和RXXX两类
- lpush/rpush key value value...从左或者右插入一个或者多个值(头插与尾插)
- lpop/rpop key 从左或者右吐出一个或者多个值(值在键在,值都没,键都没)
- rpoplpush key1 key2 从key1列表右边吐出一个值,插入到key2的左边
- lrange key start stop 按照索引下标获取元素(从左到右)
- lrange key 0 -1 获取所有值 lindex key index 按照索引下标获得元素
- llen key 获取列表长度
- linsert key before/after value newvalue 在value的前面插入一个新值
- lrem key n value 从左边删除n个value值
- lset key index value 在列表key中的下标index中修改值value
特点:
- list实际上是一个双向链表,left, right 都可以插入值
- 如果key不存在,则创建新的链表
- 如果key存在,新增内容
- 如果移除了所有值,空链表,也代表不存在
- 在两边插入或者改动值,效率最高,修改中间元素,效率相对较低
应用场景:
列表显示(关注列表、留言评价...分页、热点新闻等),消息排队,消息队列(Lpush Rpop),栈(Lpush Lpop)
3.1.4 Set(集合)
Redis的Set是string类型的无序不重复集合,Redis 中集合是通过一个value为null的hash表实现的,所以添加,删除,查找的复杂度都是O(1)。
- sadd key v1 v2 v3.... 往key集合中添加一个或多个元素,重复元素只能添加进一个
- smembers key 获取所有元素
- sismember key value 判断value是否存在
- scard key 获取key中元素的个数
- srem key value 将value移除set集合
- srandmember key count 随机返回count个数的元素,count默认1
- spop key count 从key中随机移除一个或count个元素
- smove key1 key2 value 将key1中的指定元素移动到key2中
- sinter key1 key2 返回两个集合的交集元素
- sunion key1 key2 返回两个集合的并集元素
- sdiff key1 key2 返回两个集合的差集元素(key1有的,key2没有)
数据结构:Set数据结构是dict字典,字典是用哈希表实现的。Java中HashSet的内部实现使用的是HashMap,只不过所有的value都指向同一个对象。Redis的set结构也是一样,它的内部也使用hash结构,所有的value都指向同一个内部值。
应用场景:Redis为集合提供了求交集、并集、差集等操作,故可以用来求微博、b站等共同关注或共同好友(并集)
3.1.5 Zset(有序集合)
有序集合zset与普通集合set非常相似,是一个没有重复元素的字符串集合。
不同的是每个元素都会关联一个double类型的评分(score)。这个评分(score)被用来按照从最低分到最高分的方式排序集合中的成员。集合的成员是唯一的,但是评分可以是重复了(score相同时按字典顺序排序) 。
因为元素是有序的, 所以可以很快的根据评分(score)或者次序(position)来获取一个范围的元素。访问有序集合的中间元素也是非常快的,因此你能够使用有序集合作为一个没有重复成员的智能列表。
- zadd key score1 value1 score2 value2 将一个或多个member元素及其score值加入到有序key中
- zrange key start stop (withscores) 返回有序集key,下标在start与stop之间的元素,带withscores,可以让分数一起和值返回到结果集。
- zrangebyscore key min max(withscores) 返回有序集key,所有score值介于min和max之间(包括等于min或max)的成员。有序集成员按score的值递增次序排列
- zrevrangebyscore key max min (withscores)同上,改为从大到小排列
- zincrby key increment value 为元素的score加上增量
- zrem key value 删除该集合下,指定值的元素
- zcount key min max 统计该集合,分数区间内的元素个数
- zrank key value 返回该值在集合中的排名,从0开始
数据结构:SortedSet(zset)是Redis提供的一个非常特别的数据结构,一方面它等价于Java的数据结构Map,可以给每一个元素value赋予一个权重score,另一方面它又类似于TreeSet,内部的元素会按照权重score进行排序,可以得到每个元素的名次,还可以通过score的范围来获取元素的列表。
zset底层使用了两个数据结构
- hash,hash的作用就是关联元素value和权重score,保障元素value的唯一性,可以通过元素value找到相应的score值。
- 跳跃表,跳跃表的目的在于给元素value排序,根据score的范围获取元素列表。
跳跃表(跳表):有序集合在生活中比较常见,例如根据成绩对学生排名,根据得分对玩家排名等。对于有序集合的底层实现,可以用数组、平衡树、链表等。数组不便元素的插入、删除;平衡树或红黑树虽然效率高但结构复杂;链表查询需要遍历所有效率低。Redis采用的是跳跃表。跳跃表效率堪比红黑树,实现远比红黑树简单。
对比有序链表和跳跃表,从链表中查询出51
(1)有序链表
要查找值为51的元素,需要从第一个元素开始依次查找、比较才能找到。共需要6次比较。
(2)跳跃表
从第2层开始,1节点比51节点小,向后比较。21节点比51节点小,继续向后比较,后面就是NULL了;
所以从21节点向下到第1层在第1层,41节点比51节点小,继续向后,61节点比51节点大;
所以从41向下在第0层,51节点为要查找的节点,节点被找到,共查找4次。
应用场景:
- zset排序存储班级成绩表,工资表排序
- 普通消息,1.重要消息 2.带权重进行判断
- 排行榜应用实现,取Top N测试
3.2 三种特殊数据类型
3.2.1 Geospatial(地理位置)
GEO,Geographic,地理信息的缩写。该类型就是元素的2维坐标,在地图上就是经纬度。redis基于该类型提供了经纬度设置,范围查询,距离查询,经纬度Hash等常见操作。
命令参数:
- geoadd key longitude latitude member 添加地理位置(经度纬度名称)
- geopos key member 获取指定地区的坐标值
- geodist key member1 member2 (m km ft mi) 获取两个位置之间的直线距离
- georadius key longitude latitude radius (m km ft mi) 以给定的经纬度为中心,找出某一半径的内元素
3.2.2 Hyperloglog(基数统计)
HyperLogLog 是用来做基数统计的算法,优点是在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定的、并且是很小的。花费 12 KB 内存,就可以计算接近 2^64 个不同元素的基数。
基数:比如数据集 {1, 3, 5, 7, 5, 7, 8}, 那么这个数据集的基数集为 {1, 3, 5 ,7, 8}, 基数(不重复元素)为5。 基数估计就是在误差可接受的范围内,快速计算基数。
- 统计网页中页面访问量
- 只会根据输入元素来计算基数,而不会储存输入元素本身,不能像集合那样,返回输入的各个元素
- 基数估计是在误差可接受的范围内,快速计算(不重复元素的结算,例如:一个用户多次访问,也只能算作一个人)
命令参数:
- pfadd key element 添加指定的元素到hyperloglog中,成功则返回1,不成功返回0
- pfcount key 计算key的近似基数
- pfmerge destkey sourcekey sourcekey 一个或多个key合并后的结果存在另一个key
3.2.3 BitMaps(位图)
使用位存储,信息状态只有 0 和 1。统计用户信息,活跃,不活跃;登录 、 未登录; 两个状态的,都可以使用 Bitmaps。
- 合理使用操作位可以有效地提高内存使用率和开发使用率
- 本身是一个字符串,不是数据类型,数组的每个单元只能存放0和1,数组的下标在Bitmaps叫做偏移量
- 节省空间,一般存储活跃用户比较多
命令参数:
- setbit key offset value 设置值
- getbit key offset 取值
- bitcount key (start end)统计数值
- bitop and(or/not/xor)destkey key 复合操作,交并非异或,结果保存在destkey
4 事务
Redis事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
Redis事务的主要作用就是串联多个命令防止别的命令插队。
Redis是利用这种check-and-set(乐观锁)机制实现事务的。
4.1 Redis事务三特性
- 单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
- 没有隔离级别的概念:队列中的命令没有提交之前都不会实际被执行,因为事务提交前任何指令都不会被实际执行。
- 不保证原子性:事务中如果有一条命令执行失败,其后的命令仍然会被执行,没有回滚。
4.2 Multi、Exec、discard
从输入Multi命令开始,输入的命令都会依次进入命令队列中,但不会执行,直到输入Exec后,Redis会将之前的命令队列中的命令依次执行,组队的过程中可以通过discard来放弃组队。
- multi:组队阶段,还未执行
- exec:执行阶段,将multi的队列放进exec中
- discard:放弃multi在队列中的值
4.3 事务的错误处理
- 组队中某个命令出现了报告错误,执行时整个的所有队列都会被取消。
- 如果执行阶段某个命令报出了错误,则只有报错的命令不会被执行,而其他的命令都会执行,不会回滚。
5 持久化
Redis 提供了2个不同形式的持久化方式
- RDB(Redis DataBase)
- AOF(Append Of File)
5.1 RDB(Redis DataBase)
在指定的时间间隔内将内存中的数据集快照写入磁盘,恢复时是将快照文件直接读到内存里。
Redis会单独创建(fork)一个子进程来进行持久化,会先将数据写入到 一个临时文件中,待持久化过程都结束了,再用这个临时文件替换上次持久化好的文件。 整个过程中,主进程是不进行任何IO操作的,这就确保了极高的性能
- 如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,那RDB方式要比AOF方式更加的高效。
- RDB的缺点是最后一次持久化后的数据可能丢失。
5.1.1 fork进程
Fork的作用是复制一个与当前进程一样的进程。新进程的所有数据(变量、环境变量、程序计数器等) 数值都和原进程一致,但是是一个全新的进程,并作为原进程的子进程
- 在Linux程序中,fork()会产生一个和父进程完全相同的子进程,但子进程在此后多会exec系统调用,出于效率考虑,Linux中引入了“写时复制技术”
- 一般情况父进程和子进程会共用同一段物理内存,只有进程空间的各段的内容要发生变化时,才会将父进程的内容复制一份给子进程。
5.1.2 RDB持久化流程
5.1.4 触发RDB快照
save和bgsave的比较
- save :save时只管保存,其它不管,全部阻塞。手动保存。不建议。
- bgsave:Redis会在后台异步进行快照操作,快照同时还可以响应客户端请求。
RDB相关配置
- stop-writes-on-bgsave-error yes 关闭写入磁盘操作。比如当Redis无法写入磁盘的话,直接关掉Redis的写操作
- rdbcompression yes 对于存储到磁盘中的快照,可以设置是否进行压缩存储,如果你不想消耗CPU来进行压缩的话,可以设置为关闭此功能。推荐yes.
- rdbchecksum yes 增加数据校验,增加大约10%的性能消耗,如果希望获取到最大的性能提升,可以关闭此功能
- dbfilename dump.rdb 在redis.conf中配置文件名称,默认为dump.rdb
- save 3600 1 默认是1分钟内改了1万次,或5分钟内改了10次,或1个小时内改了1次。禁用保存策略,不设置save指令,或者给save传入空字符串
- dir ./ 默认为Redis启动时命令行所在的目录下
执行flushall命令,也会产生dump.rdb文件,但里面是空的,无意义。
5.1.5 RDB的备份
先通过config get dir ,查询rdb文件的目录。因为是临时文件,redis关闭之后,rdb的东西就会不见,所以将*.rdb的文件拷贝到别的地方,这里需要写crontab定时调度脚本去做数据备份,定时copy一份rdb的备份,到一个备份的目录中去,然后移除之前的备份,只保留最新的备份文件。
rdb的恢复
- 关闭Redis
- 先把备份的文件拷贝到工作目录下 cp dump2.rdb dump.rdb
- 启动Redis, 备份数据会直接加载
5.1.6 总结
优点:
- 适合大规模的数据恢复
- 对数据完整性和一致性要求不高更适合使用
- 节省磁盘空间
- 恢复速度快
缺点:
- Fork的时候,内存中的数据被克隆了一份,大致2倍的膨胀性需要考虑
- 虽然Redis在fork时使用了写时拷贝技术,但是如果数据庞大时还是比较消耗性能。
- 在备份周期在一定间隔时间做一次备份,所以如果Redis意外down掉的话,就会丢失最后一次快照后的所有修改。
5.2 AOF(Append Only File)
以日志的形式来记录每个写操作(增量保存),将Redis执行过的所有写指令记录下来(读操作不记录), 只许追加文件但不可以改写文件。
redis启动之初会读取该文件重新构建数据,换言之,redis 重启的话就根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作。
5.2.1 AOF持久化流程
- 客户端的请求写命令会被append追加到AOF缓冲区内;
- AOF缓冲区根据AOF持久化策略[always,everysec,no]将操作sync同步到磁盘的AOF文件中;
- AOF文件大小超过重写策略或手动重写时,会对AOF文件rewrite重写,压缩AOF文件容量;
- Redis服务重启时,会重新load加载AOF文件中的写操作达到数据恢复的目的。
5.2.2 启动/修复/恢复
- 文件名称默认为 appendonly.aof
- AOF文件的保存路径,同RDB的路径一致
- AOF默认不开启,开启AOF需要修改默认的appendonly no,改为yes
- AOF和RDB同时开启,系统默认取AOF的数据(数据不会存在丢失)
AOF的备份机制和性能虽然和RDB不同, 但是备份和恢复的操作同RDB一样,都是拷贝备份文件,需要恢复时再拷贝到Redis工作目录下,启动系统即加载。
正常恢复
- 修改默认的appendonly no,改为yes
- 将有数据的aof文件复制一份保存到对应目录(查看目录:config get dir)
- 恢复:重启redis然后重新加载
异常恢复
- 修改默认的appendonly no,改为yes
- 如遇到AOF文件损坏,通过/usr/local/bin/redis-check-aof--fix appendonly.aof进行恢复
- 备份被写坏的AOF文件
- 恢复:重启redis,然后重新加载
5.2.3 AOF同步频率
- appendfsync always:始终同步,每次Redis的写入都会立刻记入日志;性能较差但数据完整性比较好
- appendfsync everysec:每秒同步,每秒记入日志一次,如果宕机,本秒的数据可能丢失。
- appendfsync no:redis不主动进行同步,把同步时机交给操作系统。
5.2.4 Rewrite压缩
AOF采用文件追加方式,文件会越来越大为避免出现此种情况,新增了重写机制, 当AOF文件的大小超过所设定的阈值时,Redis就会启动AOF文件的内容压缩, 只保留可以恢复数据的最小指令集.可以使用命令bgrewriteaof。
重写原理:
AOF文件持续增长而过大时,会fork出一条新进程来将文件重写(也是先写临时文件最后再rename redis4.0版本后的重写,是指上就是把rdb 的快照,以二级制的形式附在新的aof头部,作为已有的历史数据,替换掉原来的流水账操作
- no-appendfsync-on-rewrite=yes: 不写入aof文件只写入缓存,用户请求不会阻塞,但是在这段时间如果宕机会丢失这段时间的缓存数据。(降低数据安全性,提高性能)
- no-appendfsync-on-rewrite=no:还是会把数据往磁盘里刷,但是遇到重写操作,可能会发生阻塞。(数据安全,但是性能降低)
触发机制,何时重写:
Redis会记录上次重写时的AOF大小,默认配置是当AOF文件大小是上次rewrite后大小的一倍且文件大于64M时触发。
重写虽然可以节约大量磁盘空间,减少恢复时间。但是每次重写还是有一定的负担的,因此设定Redis要满足一定条件才会进行重写。
- auto-aof-rewrite-percentage:设置重写的基准值,文件达到100%时开始重写(文件是原来重写后文件的2倍时触发)
- auto-aof-rewrite-min-size:设置重写的基准值,最小文件64MB。达到这个值开始重写。
例如:文件达到70MB开始重写,降到50MB,下次什么时候开始重写?100MB
系统载入时或者上次重写完毕时,Redis会记录此时AOF大小,设为base_size。
AOF当前大小>= base_size +base_size*100% (默认)且当前大小>=64mb(默认)的情况下,Redis会对AOF进行重写。
重写流程:
- bgrewriteaof触发重写,判断是否当前有bgsave或bgrewriteaof在运行,如果有,则等待该命令结束后再继续执行。
- 主进程fork出子进程执行重写操作,保证主进程不会阻塞。
- 子进程遍历redis内存中数据到临时文件,客户端的写请求同时写入aof_buf缓冲区和aof_rewrite_buf重写缓冲区保证原AOF文件完整以及新AOF文件生成期间的新的数据修改动作不会丢失。
- 子进程写完新的AOF文件后,向主进程发信号,父进程更新统计信息;主进程把aof_rewrite_buf中的数据写入到新的AOF文件。
- 使用新的AOF文件覆盖旧的AOF文件,完成AOF重写。
5.2.5 总结
优点:
- 备份机制更稳健,丢失数据概率更低
- 可读的日志文本,通过操作AOF稳健,可以处理误操作
缺点:
- 比起RDB占用更多的磁盘空间。
- 恢复备份速度要慢。
- 每次读写都同步的话,有一定的性能压力。
- 存在个别Bug,造成恢复不能
5.3 总结
- RDB持久化方式能够在指定的时间间隔能对你的数据进行快照存储。
- 如果对数据不敏感,可以选单独用RDB。
- AOF持久化方式记录每次对服务器写的操作,当服务器重启的时候会重新执行这些命令来恢复原始的数据,AOF命令以redis协议追加保存每次写的操作到文件末尾。
- Redis还能对AOF文件进行后台重写,使得AOF文件的体积不至于过大
- 只做缓存:如果你只希望你的数据在服务器运行的时候存在,可以不使用任何持久化方式。
- 同时开启两种持久化方式:当redis重启的时候会优先载入AOF文件来恢复原始的数据, 因为在通常情况下AOF文件保存的数据集要比RDB文件保存的数据集要完整。
- RDB的数据不实时,同时使用两者时服务器重启也只会找AOF文件。那要不要只使用AOF呢?建议不要,因为RDB更适合用于备份数据库(AOF在不断变化不好备份),快速重启,而且AOF可能潜在的bug,留着作为一个万一的手段。
- 性能建议:因为RDB文件只用作后备用途,建议只在Slave上持久化RDB文件,而且只要15分钟备份一次就够了,只保留save 900 1这条规则。如果使用AOF,好处是在最恶劣情况下也只会丢失不超过两秒数据,启动脚本较简单只load自己的AOF文件就可以了。代价,一是带来了持续的IO,二是AOF rewrite的最后将rewrite过程中产生的新数据写到新文件造成的阻塞几乎是不可避免的。只要硬盘许可,应该尽量减少AOF rewrite的频率,AOF重写的基础大小默认值64M太小了,可以设到5G以上。默认超过原大小100%大小时重写可以改到适当的数值。
6 主从复制
主机数据更新后根据配置和策略, 自动同步到备机的master/slaver机制,Master以写为主,Slave以读为主。
作用:
6.1 复制原理
- Slave启动成功连接到master后,从机slave会发送一个sync命令
- Master接到命令启动后台的存盘进程,同时收集所有接收到的用于修改数据集命令, 在后台进程执行完毕之后,master将传送整个数据文件到slave,以完成一次完全同步
- 全量复制:slave服务在接收到数据库文件数据后,将其存盘并加载到内存中。(刚开始从机连接主机,主机一次给)
- 增量复制:Master继续将新的所有收集到的修改命令依次传给slave,完成同步 (主机修改了数据会给予从机修改的数据同步,叫做增量复制)
- 只要是重新连接master,一次完全同步(全量复制)将被自动执行,rdb的数据就会给从机。
6.2 常见的3种情况
一主两仆:主机挂掉,执行shutdown,从机info replication还是显示其主机是挂掉的哪个,如果从机挂掉,执行shutdown。主机开始写数据,从机在开启的时候,恢复数据的时候是从主机从头开始追加的。
薪火相传:上一个Slave可以是下一个slave的Master,Slave同样可以接收其他 slaves的连接和同步请求,那么该slave作为了链条中下一个的master, 可以有效减轻master的写压力,去中心化降低风险。
反客为主:当一个master宕机后,后面的slave可以立刻升为master,其后面的slave不用做任何修改 可以使用命令:slaveof no one 将从机变为主机
6.3 哨兵模式
主要是为了监控主机宕机之后,从机可以立马变为主机,就和上面的反客为主一样,不用手动设置能够后台监控主机是否故障,如果故障了根据投票数自动将从库转换为主库。
从机变成主机,其判定规则 (顺序依次往下,优先级》偏移量》runid)
- 优先级在redis.conf中默认:slave-priority 100,值越小优先级越高
- 偏移量是指获得原主机数据最全的,也就是数据越多,变主机的机会越大
- 每个redis实例启动后都会随机生成一个40位的runid,值越小优先级越高
复制会有延时:由于所有的写操作都是先在Master上操作,然后同步更新到Slave上,所以Master同步到Slave机器有一定的延迟,当系统很繁忙的时候,延迟问题会更加严重,Slave机器数量的增加也会使这个问题更加严重。
7 集群
定义:
Redis 集群实现了对Redis的水平扩容,即启动N个redis节点,将整个数据库分布存储在这N个节点中,每个节点存储总数据的1/N。
Redis 集群通过分区(partition)来提供一定程度的可用性(availability): 即使集群中有一部分节点失效或者无法进行通讯, 集群也可以继续处理命令请求。
集群配置参数:
- 将rdb,aof文件都删除掉
- cluster-enabled yes 打开集群模式
- cluster-config-file nodes-6379.conf 设定节点配置文件名
- cluster-node-timeout 15000 设定节点失联时间,超过该时间(毫秒),集群自动进行主从切换。
slots:
一个 Redis 集群包含 16384 个插槽(hash slot),数据库中的每个键都属于这 16384 个插槽的其中一个。
集群使用公式 CRC16(key) % 16384 来计算键 key 属于哪个槽,其中 CRC16(key) 语句用于计算键 key 的 CRC16 校验和 。
集群中的每个节点负责处理一部分插槽。举个例子, 如果一个集群可以有主节点,其中:
- 节点 A 负责处理 0 号至 5460 号插槽。
- 节点 B 负责处理 5461 号至 10922 号插槽。
- 节点 C 负责处理 10923 号至 16383 号插槽
在redis-cli每次录入、查询键值,redis都会计算出该key应该送往的插槽,如果不是该客户端对应服务器的插槽,redis会报错,并告知应前往的redis实例地址和端口。redis-cli客户端提供了 –c 参数实现自动重定向。
优势:
劣势:
- 多键操作是不被支持的
- 多键的Redis事务是不被支持的。lua脚本不被支持
- 由于集群方案出现较晚,很多公司已经采用了其他的集群方案,而代理或者客户端分片的方案想要迁移至redis cluster,需要整体迁移而不是逐步过渡,复杂度较大。