目录
第一章 nosql概述(not only sql)
1.1为什么需要nosql
1.2 nosql数据库的四大分类
NoSQL数据库的四大分类表格分析
第二章 redis概述
2.1概述:
2.2 redis特性:
第三章 API
3.1全局命令
3.2字符串
3.3 hash:
3.4 列表list
3.5集合set
3.6有序集合zset
第四章 redis管理器
4.1 全局命令
4.2.1.键的迁移:把部分数据迁移到另一台redis服务器
4.3.1 redis数据库管理
第五章 redis持久化
第六章 redis主从复制
6.1方式:
6.2相关命令:
6.3注意事项:
6.4Redis主从拓扑:
6.5复制原理:
6.6数据同步:
6.7 主从的缺点:
6.8 主从故障如何故障转移
第七章 redis哨兵
7.1哨兵机制(sentinel)的高可用
7.2 哨兵机制-故障转移流程A
7.3 RedisSentinel如何安装与部署
7.4安装与部署1(先搭主从)
7.5哨兵机制核心配置
7.6 哨兵其它配置
7.7哨兵机制启动
7.8RedisSentinel节点测试
7.9 RedisSentinel如何监控2个redis主节点呢?
7.10 部署建议
7.11 哨兵的API
7.12 客户端连接(redis-sentinel例子工程)
第八章 redis集群高可用
7.1 分区规则
非关系型数据库
High Performance-高并发读写
Huge Storage - 海量数据的高效率存储和访问
High Scalability && High Availabitiy - 高可扩展性和高可用性
NoSQL数据库在以下的这几种情况下比较适用:
1 、数据模型比较简单;
2、需要灵活性更强的IT系统;
3、对数据库性能要求较高;
4、不需要高度的数据一致性;
5、对于给定key,比较容易映射复杂值的环境
nosql存在的一些问题
这一类数据库主要会使用到一个哈希表,这个表中有一个特定的键和一个指针指向特定的数据。Key/value模型对于IT系统来说的优势在于简单、易部署。但是如果DBA只对部分值进行查询或更新的时候,Key/value就显得效率低下了。举例如:Tokyo Cabinet/Tyrant, Redis, Voldemort, Oracle BDB.
这部分数据库通常是用来应对分布式存储的海量数据。键仍然存在,但是它们的特点是指向了多个列。这些列是由列家族来安排的。如:Cassandra, HBase, Riak.
文档型数据库的灵感是来自于Lotus Notes办公软件的,而且它同第一种键值存储相类似。该类型的数据模型是版本化的文档,半结构化的文档以特定的格式存储,比如JSON。文档型数据库可 以看作是键值数据库的升级版,允许之间嵌套键值。而且文档型数据库比键值数据库的查询效率更高。如:CouchDB, MongoDb. 国内也有文档型数据库SequoiaDB,已经开源。
图形结构的数据库同其他行列以及刚性结构的SQL数据库不同,它是使用灵活的图形模型,并且能够扩展到多个服务器上。NoSQL数据库没有标准的查询语言(SQL),因此进行数据库查询需要制定数据模型。许多NoSQL数据库都有REST式的数据接口或者查询API。如:Neo4J, InfoGrid, Infinite Graph.
分类 | Examples举例 | 典型应用场景 | 数据模型 | 优点 | 缺点 |
---|---|---|---|---|---|
键值(key-value) | Tokyo Cabinet/Tyrant, Redis, Voldemort, Oracle BDB | 内容缓存,主要用于处理大量数据的高访问负载,也用于一些日志系统等等。 | Key 指向 Value 的键值对,通常用hash table来实现 | 查找速度快 | 数据无结构化,通常只被当作字符串或者二进制数据 |
列存储数据库 | Cassandra, HBase, Riak | 分布式的文件系统 | 以列簇式存储,将同一列数据存在一起 | 查找速度快,可扩展性强,更容易进行分布式扩展 | 功能相对局限 |
文档型数据库 | CouchDB, MongoDb | Web应用(与Key-Value类似,Value是结构化的,不同的是数据库能够了解Value的内容) | Key-Value对应的键值对,Value为结构化数据 | 数据结构要求不严格,表结构可变,不需要像关系型数据库一样需要预先定义表结构 | 查询性能不高,而且缺乏统一的查询语法。 |
图形(Graph)数据库 | Neo4J, InfoGrid, Infinite Graph | 社交网络,推荐系统等。专注于构建关系图谱 | 图结构 | 利用图结构相关算法。比如最短路径寻址,N度关系查找等 | 需要对整个图做计算才能得出需要的信息,而且这种结构不太好做分布式的集群方案。 |
用C语言开发的一个开源的、高性能的键值对(key-value)数据库,通过提供多种键值数据类型来适应不同场景的数据需求。高性能键值对数据库。其中value可以为string、hash、list、set、zset等多种数据结构,可以满足很多应用场景。还提供了键过期,发布订阅,事务,流水线,等附加功能,
流水线: Redis 的流水线功能允许客户端一次将多个命令请求发送给服务器, 并将被执行的多个命令请求的结果在一个命令回复中全部返回给客户端, 使用这个功能可以有效地减少客户端在执行多个命令时需要与服务器进行通信的次数。
1〉速度快,数据放在内存中,官方给出的读写性能10万/S,与机器性能也有关
a,数据放内存中是速度快的主要原因
b,C语言实现,与操作系统距离近
c,使用了单线程架构,预防多线程可能产生的竞争问题
2〉键值对的数据结构服务器
3〉简单稳定:单线程
4〉持久化:发生断电或机器故障,数据可能会丢失,持久化到硬盘
6〉主从复制:实现多个相同数据的redis副本
8〉高可用和分布式:哨兵机制实现高可用,保证redis节点故障发现和自动转移
9〉客户端语言多:java php python c c++ nodejs等
redis支持的键值数据类型
应用场景
redis的优点
非常快速:每一秒钟可执行大约110000次的set操作,81000次的get操作。并发大概十万。
支持多种数据类型:字符串,列表,有序/三列集合,集合等等类型。
操作具有原子性:原子性保证了并发情况下,服务器能接收更新的值。
注:单线程。
单线程架构
列举例子:三个客户端同时执行命令
客户端1:set name test
客户端2:incr num
客户端3:incr num
执行过程:发送指令-〉执行命令-〉返回结果
执行命令:单线程执行,所有命令进入队列,按顺序执行,使用I/O多路复用解决I/O问题,后面有介绍(由于读写操作等待用户输入或输出都是阻塞的,所以 I/O 操作在一般情况下往往不能直接返回,这会导致某一文件的 I/O 阻塞导致整个进程无法对其它客户提供服务 ,IO多路复用模型是建立在内核提供的多路分离函数select基础之上的,使用select函数可以避免同步非阻塞IO模型中轮询等待的问题)
单线程快原因:纯内存访问, 非阻塞I/O(使用多路复用),单线程避免线程切换和竞争产生资源消耗
问题:如果某个命令执行,会造成其它命令的阻塞
切换数据库:select 2
命令 | 描述 |
keys * | 查看所有键,如果存在大量键,线上禁止使用此指令 |
dbsize | 键总数 |
exists key | 检查键是否存在,存在返回1,不存在返回0 |
expire key seconds | 键过期 |
ttl key | 查看剩余的过期时间 |
type key | 键的数据结构类型,键不存在返回none |
flushall | 清空所有数据库节点,redis默认16个节点 |
flushdb | 清空当前数据库节点(0) |
select datanode | 切换节点,总共16个节点,默认是0节点。 |
3.2.1字符串类型:字符串(xml,json),数字(整形,浮点数),二进制(图片,音、视频)最大不能超过512MB。
3.2.2 API:
命令 | 描述 |
---|---|
set | |
set key value | 设值 |
set key value ex 10 | // ex表示过期,单位是秒; |
setnx key value | 不存在key时才能插入,存在key返回0失败,不存在key返回1成功;注:setnx可用做分布式锁 |
mset key1 value1 key2 value2....... | 批量操作设值 |
get | |
get key | 获取值 |
mget key1 key2..... | 批量获取 |
运算 | |
incr key | 自增,必须为整数自加1,非整数返回错误,无key键从0自增返回1 |
decr key | 递减整数 |
incrby key value | 加法,整数值+value |
incrbyfloat key 1.1 | 浮点数 加法 |
decrby key value | 减法整数 |
其它 | |
append key value | 追加值 |
strlen key | 获取字符串长度 |
getrange key index1,index2 | 截取字符串,返回下标第index1个到 第index2个,下标从0开始。 |
object encoding key | 内部编码 int :8个字长;embstr:小于等于39字节字符串;raw大于39字节的字符串 |
是一个string类型的field和value和value的映射表,hash特别适合用于存储对象;
命令 | 描述 |
---|---|
hset key field value | 设值,field value类似于map中的key value |
hget key field | 取值 |
hdel key field | 删值 |
hlen key | 长度 |
hmset key field1 value1 field2 value2.... | 批量设值 |
hmget key field1 field2... | 批量取值 |
hkeys key | 获取所有field |
hvals key | 获取所有value |
hgetall key | 获取所有field value |
hincrby key field value hincrbyfloat key field value |
加法 |
hdecrby key field value | 减法 |
内部编码:ziplist<压缩列表>和hashtable<哈希表>
当field个数少且没有大的value时,内部编码为ziplist
当value大于64字节,内部编码由ziplist变成hashtable
1.字符串:
优点:简单直观,每个键对应一个值
缺点:键数过多,占用内存多,用户信息过于分散,不用于生产环境
HASH类型是稀疏,每个键可以有不同的filed, 若用redis模拟做关系复杂查询开发因难,维护成本高
2.将对象序列化存入reids:set user:1 serialize(userInfo);
优点:编程简单,若使用序列化合理内存使用率高
缺点:序列化与反序列化有一定开销,更新属性时需要把userInfo全取出来进行反序列化,更新后再序列化到redis
3.使用hash类型: hmset user:1 name james age 23 sex boy
优点:简单直观,使用合理可减少内存空间消耗
缺点:要控制ziplist与hashtable两种编码转换,且hashtable会消耗更多内存
总结:对于更新不多的情况下,可以使用序列化,对于VALUE值不大于64字节可以使用hash类型
用来存储多个有序的字符串,一个列表最多可存2的32次方减1个元素
因为有序,可以通过索引下标获取元素或某个范围内元素列表,列表元素可以重复
命令 | 描述 |
---|---|
rpush key val1 val2 val3 | 从右向左添加val1 val2 val3 |
lpush key val1 val2 val3 | 从左向右添加 |
linsert key before val1 val2 | 在val1之前插入val2 ,after为之后 |
lrange key 0 -1 | 从左到右获取列表的所有元素 |
lindex key index | index下标,正数从左到右,负数-1表示最末尾 |
llen key | 返回当前列表长度 |
lpop key | 把最左边的第一个元素删除 返回删除的值 |
rpop key | 删除最右边的元素,返回删除的值 |
lrem key count valu | 删除指定元素value,删除count个 |
ltrim key index1 index2 | 只保留从第index1到index2的元素,其它全部删除,下标从0开始 |
set key index value | 把下标为index的值改成value。 |
内部编码:
object encoding key
1,ziplist:当元素个数少且没大元素,编码为ziplist,减少内存的使用
2,linkedlist:当元素超过512个,或元素超过64字节,内部编码变成linkedlist链表;
保存多元素,与列表不一样的是不允许有重复元素,且集合是无序,一个集合最多可存2的32次方减1个元素,除了支持增删改查,还支持集合交集、并集、差集;
命令 | 描述 |
---|---|
sadd key v1 v2 v3 | 当插入相同的元素,则重复无效,返回0, |
smember key | 获取key集合的所有元素,返回结果无序 |
srem key value | 删除指定value |
scard key | 计算元素个数 |
sismember key value | 判断元素value在key集合中是否存在,存在返回1,不存在返回0 |
srandmember key count | 随机返回count个元素 |
spop key count | 随机获取count个元素,并删除 |
sinter key1 key2 key3..... | 求多个结合的交集 |
sunion key1 key2 key3...... | 求并集 |
sdiff key1 key2..... | 差集 |
sinterstore key key1 key2... | 将key1 key2...交集保存到key |
sunionstore key key1 key2.. | 将key1 key2...合集保存到key |
sdiffstore key key1 key2... | 将key1 key2...差集保存到key |
内部编码: object encoding key
1.intset:当元素个数少(小于512个)且都为整数,redis使用intset减少内存的使用
2.hashtable:当超过512个或不为整数(比如a b)时,编码为hashtable
使用场景:
标签,社交,查询有共同兴趣爱好的人,智能推荐
有序集合,可排序的,但是唯一。
命令 | 描述 |
---|---|
zadd key score member [score member......] |
如果该元素已经存在则会用新的分数替换原有的分数。返回值是新加入到集合中的元素个数,不包含之前已经存在的元素。 |
zadd key value member |
点赞数1, 返回操作成功的条数1 |
zadd key nx value member | 添加的必须不存在,主用于添加 |
zadd key xx incr value member | 必须存在,主用于修改 |
zadd key xx ch incr value member |
|
zscore key member | 获取元素的分数 |
zrem key member [member …] | 移除有序集key中的一个或多个成员,不存在的成员将被忽略。当key存在但不是有序集类型时,返回一个错误。 |
zrange key start stop [withscores] | 按照元素分数从小到大的顺序返回索引从start到stop之间的所有元素(包含两端的元素) |
zrevrange key start stop [withscores] | 按照元素分数从大到小的顺序返回索引从start到stop之间的所有元素(包含两端的元素) |
zrange scoreboard 0 1 withscores | 如果需要获得元素的分数的可以在命令尾部加上WITHSCORES参数 |
zrange key member | 获取元素的排名,从小到大 |
zrevrange key member | 获取元素的排名,从大到小 |
zrangebyscore key min max [withscores] [LIMIT offset count] | 获得指定分数范围的元素 |
zincrby key increment member | 增加某个元素的分数 |
zcard key | 获得集合中元素的数量 |
zcount key min max | 获得指定分数范围内的元素个数 |
ZREMRANGEBYRANK key start stop | 按照排名范围删除元素 |
ZREMRANGEBYSCORE key min max | 按照分数范围删除元素 |
zinterstore destination numkeys key ... [WEIGHTS weight] [AGGREGATE SUM|MIN|MAX] | 有序集合交集 destination:交集产生新的元素存储键名称 numkeys: 要做交集计算的键个数 key :元素键值 weights:每个被选中的键对应值乘weight, 默认为1 |
zunionstore destination numkeys key ... [WEIGHTS weight] [AGGREGATE SUM|MIN|MAX] | 有序集合并集(合并去重):
destination:交集产生新的元素存储键名称 numkeys: 要做交集计算的键个数 key :元素键值 weights:每个被选中的键对应值乘weight, 默认为1 |
有序集合内部编码 object encoding key
1.ziplist:当元素个数少(小于128个),元素值小于64字节时,使用ziplist编码,可有效减少内存的使用
2.skiplist: 大于128个元素或元素值大于64字节时为skiplist编码与LIST SET 对比:
数据结构 | 能否重复 | 是否有序 | 有序实现方式 | 应用场景 |
---|---|---|---|---|
列表 | 是 | 是 | 索引下标 | 时间轴,消息队列 |
集合 | 否 | 否 | 无 | 标签,社交 |
有序结合 | 否 | 是 | 分值 | 排行榜,点赞数 |
命令 | 描述 |
GEOADD | 增加地理位置的坐标,可以批量添加地理位置 |
GEODIST | 获取两个地理位置的距离 |
GEOHASH | 获取某个地理位置的geohash值 |
GEOPOS | 获取指定位置的坐标,可以批量获取多个地理位置的坐标 |
GEORADIUS |
根据给定地理位置坐标获取指定范围内的地理位置集合
(注意:该命令的中心点由输入的经度和纬度决定)
|
GEORADIUSBYMEMBER
|
根据给定成员的位置获取指定范围内的位置信息集合
(注意:该命令的中心点是由给定的位置元素决定)
|
生产者发布消息到频道
消费者订阅频道,从频道接收消息
生产者、消费者彼此相互不了解
类似kafka
4.1.1 键重命名 rename oldKey newkey //格式
rename oldKey newKey //若oldKey之前存在则被覆盖
set name james ;set name1 mike //数据初始化
renamenx name name1 //重命名失败,只有当name1不存在才能改名
4.1.2 返回随机键 dbsize //redis有16个库,查看当前库的键值对总数
randomkey //返回随机键
4.1.3 键过期:expire name:03 20 //键name:03 在10秒后过期
ttl name:03 //查看过期按秒到计时,当返回-2说明已删除
pttl name:03 //查看过期按毫秒到时计
set name:05 james //初始化数据
pexpire name:05 20000 //20000毫秒(20S)后过期
expire name:06 -2 //直接过期,和del一样
expireat name:04 1516971599 //设置在2018/01/26 20:59:59过期
时间转时间戳:网址http://tool.chinaz.com/Tools/unixtime.aspx
persist key //去掉过期
注意:对于字符串重设值后,expire无效,
set name james
expire name 50
ttl name
set name james1 //此时expire取消
ttl name //返回-1, 长期有效
1, move key db //reids有16个库, 编号为0-15
这种模式不建议在生产环境使用,在同一个reids里可以玩
2, dump key;
restore key ttl value//实现不同redis实例的键迁移,ttl=0代表没有过期时间
3,migrate指令迁移到其它实例redis,在1.111服务器上将test移到118
4.2.2 键的遍历:
redis提供了两个命令来遍历所有的键
1,键全量遍历:
mset country china city bj name james //设置3个字符串键值对
keys * //返回所有的键, *匹配任意字符多个字符
keys *y //以结尾的键,
keys n*e //以n开头以e结尾,返回name
keys n?me // ?问号代表只匹配一个字符 返回name,全局匹配
keys n?m* //返回name
keys [j,l]* //返回以j l开头的所有键 keys [j]ames 全量匹配james
考虑到是单线程, 在生产环境不建议使用,如果键多可能会阻塞
如果键少,可以
2,渐进式遍历
mset a a b b c c d d e e f f g g h h i i j j k k l l m m n n o o p p q q r r s s t t u u v v w w x x y y z z //初始化26个字母键值对
字符串类型:
scan 0 match n* count 20 //匹配以n开头的键,取20条,第一次scan 0开始
第二次从游标4096开始取20个以n开头的键,相当于一页一页的取
当最后返回0时,键被取完
比如将old:user开头的元素全删掉
注:可有效地解决keys命令可能产生的阻塞问题
用法和scan一样
select 0 //共16个库, 0 --15, select切换数据库
set name james
select 1
get name //隔离了,取不到,和mysql不同库一样
其中redis3.0以后的版本慢慢弱化了这个功能,如在redis cluster中只允许0数据库
原因:1,redis单线程,如果用多个库,这些库使用同一个CPU,彼此会有影响
2,多数据库,调试与运维麻烦,若有一个慢查询,会影响其它库查询速度
3,来回切换,容易混乱
flushdb: 只清空当前数据库的键值对 dbsiz 0
flushall: 清空所有库的键值对 (这两个指令慎用!!!!)
4.4 redis功能细解
4.4.1 慢查询原因分析:与mysql一样:当执行时间超过阀值,会将发生时间 耗时 命令记录
redis命令生命周期:发送 排队 执行 返回
慢查询只统计第3个执行步骤的时间
预设阀值:两种方式,默认为10毫秒
使用config set完后,若想将配置持久化保存到redis.conf,要执行config rewrite
2,redis.conf修改:找到slowlog-log-slower-than 10000 ,修改保存即可
注意:slowlog-log-slower-than =0记录所有命令 -1命令都不记录
原理:慢查询记录也是存在队列里的,slow-max-len 存放的记录最大条数,比如设置的slow-max-len=10,当有第11条慢查询命令插入时,队列的第一条命令就会出列,第11条入列到慢查询队列中, 可以config set动态设置,也可以修改redis.conf完成配置。
获取队列里慢查询的命令:slowlog get 查询返回的结构如下
获取慢查询列表当前的长度:slowlog len //以上只有1条慢查询,返回1
对慢查询列表清理(重置):slowlog reset //再查slowlog len 此时返回0 清空
对于线上slow-max-len配置的建议:线上可加大slow-max-len的值,记录慢查询存长命令时redis会做截断,不会占用大量内存,线上可设置1000以上
对于线上slowlog-log-slower-than配置的建议:默认为10毫秒,根据redis并发量来调整,对于高并发比建议为1毫秒
注意:1,慢查询只记录命令在redis的执行时间,不包括排队、网络传输时间
4.5 redis-cli详解
./redis-cli -r 3 -h 192.168.1.111 -a 12345678 ping //返回pong表示127.0.0.1:6379能通,r代表次数
./redis-cli -r 100 -i 1 info |grep used_memory_human //每秒输出内存使用量,输100次,i代表执行的时间间隔
./redis-cli -p 6379 -h 192.168.1.111 -a 12345678
redis支持RDB和AOF两种持久化机制,持久化可以避免因进程退出而造成数据丢失;
5.1RDB持久化把当前进程数据生成快照(.rdb)文件保存到硬盘的过程,有手动触发和自动触发
手动触发有save和bgsave两命令
save命令:阻塞当前Redis,直到RDB持久化过程完成为止,若内存实例比较大会造成长时间阻塞,线上环境不建议用它
bgsave命令:redis进程执行fork操作创建子线程,由子线程完成持久化,阻塞时间很短(微秒级),是save的优化,在执行redis-cli shutdown关闭redis服务时,如果没有开启AOF持久化,自动执行bgsave;
显然bgsave是对save的优化。
5.2RDB文件的操作
命令:config set dir /usr/local //设置rdb文件保存路径
备份:bgsave //将dump.rdb保存到usr/local下
恢复:将dump.rdb放到redis安装目录与redis.conf同级目录,重启redis即可
优点:1,压缩后的二进制文,适用于备份、全量复制,用于灾难恢复
2,加载RDB恢复数据远快于AOF方式
缺点:1,无法做到实时持久化,每次都要创建子进程,频繁操作成本过高
2,保存后的二进制文件,存在老版本不兼容新版本rdb文件的问题
5.3AOF持久化
针对RDB不适合实时持久化,redis提供了AOF持久化方式来解决
开启:redis.conf设置:appendonly yes (默认不开启,为no)
默认文件名:appendfilename "appendonly.aof"
流程说明:1,所有的写入命令(set hset)会append追加到aof_buf缓冲区中
2,AOF缓冲区向硬盘做sync同步
3,随着AOF文件越来越大,需定期对AOF文件rewrite重写,达到压缩
4,当redis服务重启,可load加载AOF文件进行恢复
AOF持久化流程:命令写入(append),文件同步(sync),文件重写(rewrite),重启加载(load)
redis的AOF配置详解:
appendonly yes //启用aof持久化方式
# appendfsync always //每收到写命令就立即强制写入磁盘,最慢的,但是保证完全的持久化,不推荐使用
appendfsync everysec //每秒强制写入磁盘一次,性能和持久化方面做了折中,推荐
# appendfsync no //完全依赖os,性能最好,持久化没保证(操作系统自身的同步)
no-appendfsync-on-rewrite yes //正在导出rdb快照的过程中,要不要停止同步aof
auto-aof-rewrite-percentage 100 //aof文件大小比起上次重写时的大小,增长率100%时,重写
auto-aof-rewrite-min-size 64mb //aof文件,至少超过64M时,重写
如何从AOF恢复?
1. 设置appendonly yes;
2. 将appendonly.aof放到dir参数指定的目录;
3. 启动Redis,Redis会自动加载appendonly.aof文件。
redis重启时恢复加载AOF与RDB顺序及流程:
1,当AOF和RDB文件同时存在时,优先加载
2,若关闭了AOF,加载RDB文件
3,加载AOF/RDB成功,redis重启成功
4,AOF/RDB存在错误,redis启动失败并打印错误信息
参数关闭时:
无论大小都会及时发布到从节点,占带宽,适用于主从网络好的场景,
参数启用时:
主节点合并所有数据成TCP包节省带宽,默认为40毫秒发一次,取决于内核,主从的同步延迟40毫秒,适用于网络环境复杂或带宽紧张,如跨机房
一主一从:用于主节点故障转移从节点,当主节点的“写”命令并发高且需要持久化,可以只在从节点开启AOF(主节点不需要)
一主多从:针对“读”较多的场景,“读”由多个从节点来分担,但节点越多,主节点同步到多节点的次数也越多,影响带宽,也加重主节点的稳定
树状主从:一主多从的缺点(主节点推送次数多压力大)可用些方案解决,
主节点只推送一次数据到从节点1,再由从节点2推送到11,减轻主节点推送的压力
执行slave master port后,
与主节点连接,同步主节点的数据,6380:>info replication:查看主从及同步信息
redis 2.8版本以上使用psync命令完成同步,过程分“全量”与“部分”复制
全量复制:
一般用于初次复制场景(第一次建立SLAVE后全量)
部分复制:
网络出现问题,从节占再次连主时,主节点补发缺少的数据,每次数据增加同步
心跳:
主从有长连接心跳,主节点默认每10S向从节点发ping命令,repl-ping-slave-period控制发送频率
1,主从复制,若主节点出现问题,则不能提供服务,需要人工修改配置将从变主
2,主从复制主节点的写能力单机,能力有限
3,单机节点的存储能力也有限
A,主节点(master)故障,从节点slave-1端执行 slaveof no one后变成新主节点;
B,其它的节点成为新主节点的从节点,并从新节点复制数据;
C,需要人工干预,无法实现高可用。
原理:当主节点出现故障时,由Redis Sentinel自动完成故障发现和转移,并通知应用方,实现高可用性。
A,由Sentinel节点定期监控发现主节点是否出现了故障
sentinel会向master发送心跳PING来确认master是否存活,如果master在“一定时间范围”内不回应PONG 或者是回复了一个错误消息,那么这个sentinel会主观地(单方面地)认为这个master已经不可用了
B,当主节点出现故障,此时假设3个Sentinel节点共同选举了Sentinel3节点为领导者sentinel,负载处理主节点的故障转移
C,由Sentinel3领导者节点执行故障转移,过程和主从复制一样,但是自动执行
我们以3个Sentinel节点、2个从节点、1个主节点为例进行安装部署
前提:
先搭建好一主两从redis的主从复制,和之前复制的搭建一样,搭建方式如下:
A主节点6379节点(/usr/local/bin/conf/redis6379.conf):
修改 requirepass 12345678,注释掉#bind 127.0.0.1
B从节点redis6380.conf和redis6381.conf:
修改 requirepass 12345678 ,注释掉#bind 127.0.0.1,
加上masterauth 12345678 ,加上slaveof 127.0.0.1 6379
注意:当主从起来后,主节点可读写,从节点只可读不可写
redis sentinel哨兵机制配置(也是3个节点):
/usr/local/bin/conf/sentinel_26379.conf
/usr/local/bin/conf/sentinel_26380.conf
/usr/local/bin/conf/sentinel_26381.conf
将三个文件的端口改成: 26379 26380 26381
sentinel monitor mymaster 192.168.1.111 6379 2 //监听主节点6379
sentinel auth-pass mymaster 12345678 //连接主节点时的密码
三个配置除端口外,其它一样
配完此脚本,哨兵机制可正常启动运行。
sentinel monitor mymaster 192.168.1.111 6379 2 //监控主节点的IP地址端口
sentinel auth-pass mymaster 12345678 //sentinel连主节点的密码
sentinel config-epoch mymaster 2 //执行故障转移时, 最多可以有多少个从节点同时对新的主节点进行数据同步
sentinel leader-epoch mymaster 2
sentinel failover-timeout mymaster 180000 //故障转移超时时间180s,
a,如果转移超时失败,下次转移时时间为之前的2倍;
b,从节点变主节点时,从节点执行slaveof no one命令一直失败的话,当时间超过180S时,则故障转移失败
c,从节点复制新主节点时间超过180S转移失败
sentinel down-after-milliseconds mymaster 300000//sentinel节点定期向主节点ping命令
启动sentinel服务:
./redis-sentinel conf/sentinel_26379.conf &
./redis-sentinel conf/sentinel_26380.conf &
./redis-sentinel conf/sentinel_26381.conf &
测试:kill -9 6379 杀掉6379的redis服务
看日志是分配6380 还是6381做为主节点,当6379服务再启动时,已变成从节点
假设6380升级为主节点:
进入6380>info replication
role:master
打开sentinel_26379.conf等三个配置,
sentinel monitor mymaster 192.168.1.111 6380 2 //有2个sentinel认为master下线
打开redis6379.conf等三个配置, slaveof 127.0.0.1 6380,也变成了6380
注意:生产环境建议让redis Sentinel部署到不同的物理机上。
坑点:sentinel monitor mymaster 192.168.1.111 6379 2
//切记将IP不要写成127.0.0.1
不然使用JedisSentinelPool取jedis连接的时候会变成取127.0.0.1 6379的错误地址
我们以3个Sentinel节点、2个从节点、1个主节点为例进行安装部署
原配置加上一句:sentinel monitor mymasterB 192.168.1.112 6379 2
a,sentinel节点应部署在多台物理机(线上环境)
b,至少三个且奇数个sentinel节点
c,3个sentinel可同时监控一个主节点或多个主节点,当监听N个主节点较多时,如果sentinel出现异常,会对多个主节点有影响,同时还会造成sentinel节点产生过多的网络连接,一般线上建议还是, 3个sentinel监听一个主节点
命令:redis-cli -p 26379 //进入哨兵的命令模式,使用redis-cli进入
26379> sentinel masters或sentinel master mymaster
26379> sentinel slaves mymaster
26379> sentinel sentinels mymaster //查sentinel节点集合(不包括当前26379)
26379> sentinel failover mymaster //对主节点强制故障转移,没和其它节点协商
./redis-cli -p 26380 shutdown //关闭
远程客户端连接时,要打开protected-mode no
在使用工程redis-sentinel,调用jedis查询的流程如下:
1,将三个sentinel的IP和端口 加入JedisSentinelPool
2,根据IP和地址创建JedisSentinelPool池对象
3,在这个对象创建完后,此时该对象已把redis的主节点
RedisCluster是Redis的分布式解决方案,在3.0版本后推出的方案,有效地解决了Redis分布式的需求,当遇到单机内存、并发等瓶颈时,可使用此方案来解决这些问题
常见的分区:
redis集群使用了哈希分区,顺序分区暂用不到,不做具体说明;
RedisCluster采用了哈希分区的“虚拟槽分区”方式(哈希分区分节点取余、一致性哈希分区和虚拟槽分区)。
根据hash值取模,对应到虚拟槽上(范围)。比如:server1:[1-n1],server2:[n1-n2],server3:[n2-n3]
插槽:集群内部会将所有的key映射到16384个插槽中,集群中的每个数据库实例负责其中部分的插槽的读写。
键与插槽的关系:
Redis会将key的有效部分,使用CRC16算法计算出散列值,然后对16384取余数,从
而把key分配到插槽中。键名的有效部分规则是:
1:如果键名包含{},那么有效部分就是{}中的值
2:否则就是取整个键名
移动已分配的插槽:
获取插槽对应的节点:
Redis要达到高性能需要做到:
1.Value尽可能的小。当value太大时,比如json数,几十kb,或者上mb的数据。大的数据量在带宽的限制下直接的效果就是QPS骤降,跌到几百都毫不意外。而且,redis单线程这一点,导致某个大的value访问会阻塞所有其他的操作。
2.使用Pipeline或Lua Script。Redis一般被用做网络服务。所有的请求都是跨网络进行的。所以TCP Round Trip的长短对Redis的性能表现很重要。尽量减少Round Trip可以有效的提高吞吐。所以,通常的优化方法是使用Pipeline,使得客户端可以一次性把一组Redis命令发给Redis Server;或者预先在Redis Server中定义Lua Script,使用时直接调用。但无论是Pipeline还是Lua Script,都会受到业务需求的制约——不是所有业务都适合用Pipeline/Lua Script的。
3.网路一定要快。redis性能一般情况都会被网络影响。
4.不开启RDB或者AOF。开启任何一种持久化方案都会影响Redis的性能表现。因为开启持久化会有同步磁盘的操作。
注意:
1.redis适合一些不太在乎数据丢失的场景,如果针对的数据是绝对不能有失的,那么还是要使用ACID的数据库。
2.队列在redis5.0版本之前最好不要使用,因为还不够完善,即使是5.0版本的Stream Date Type。实现了类似于Kafka的append only数据结构和API。不过很可能要到5.2才能在生产中使用(2019年年底),而且目前redis的队列还没有经过市场的检验。
3.在 事务这一块,redis没有ACID事务,他的原子性只针对于服务端,对于使用redis的客户端则需要其他的处理来保证线程安全,redis持久化没有提供相互节点同步数据的方式。
所以redis适合的场景:
描述:不怎么精确的set,特点是高效地插入和查询,可以用来告诉你 “某样东西一定不存在或者可能存在”。相比于传统的 List、Set、Map 等数据结构,它更高效、占用空间更少,但是缺点是其返回的结果是概率性的,而不是确切的,可能误判。
用处:进行精度控制。比如缓存穿透,使用布隆过滤器。
原理: 本质是一个bit数组,通过多次hash产生复数个下标,然后把bit数组中的对应下标的值从0变为1。
提高精度方法:1.扩容bit数组的长度,2.增加hash函数,这样可以有更多的下标。
redisBloom: redis的module插件。
参考https://www.jianshu.com/p/2104d11ee0a2
1.定期删除(默认会每秒进行十次过期扫描(100ms一次))
设置了过期时间的key会放在过期字典中。通过贪心策略来获取过期字典中的key。
1.从过期字典中随机 n 个 key;
2.删除这 n个 key 中已经过期的 key;
3.如果过期的 key 比率超过 1/4,那就重复步骤 1;
2.惰性删除(获取key时检查,如果过期就删除)
最长时间未被使用。使用双向链表。
LinkedHashMap模拟LRU算法。
removeEldestEntry(Map.Entry
当accessOrder为false时(默认情况),linkedHashMap只会按插入顺序维护双向链表。而开启了accessOrder之后,linkedHashMap就会把每一次对结点的访问也作为标准来进行排序。也就是说,在每次插入结点/访问结点的时候,都会将相应结点移动到双向链表的尾部,从而达到按访问顺序进行排序的目的。
一定时间内,访问次数最少的。
cpu 处理相比于 IO 可以忽略
每个客户端建立连接时,都需要服务端为其 创建 socket 套接字,建立连接
然后该客户端的每个请求都要经历以下几步:
(1)等待请求数据数据从客户端发送过来
(2)将请求数据从内核复制到用户进程的缓冲区(buffer)
(3)对请求数据进行处理(对于 redis 而言,一般就是简单的 get/set)
由于操作简单+只涉及内存,所以第(3)步的处理很简单、很快,主要时间耗在(1)步