redis 入门
NoSql概述
NoSql年代
问题:数据量过大、数据索引过大(B+Tree),机器内存不够、访问量(读写混合),服务器承受不了
缓存 Memcached + MySQL+垂直拆分(读写分离)
网站80%的情况下在读取,用MySQL太过繁琐;希望减轻数据库的压力,可以使用缓存保证效率
发展过程:优化数据结构和索引->文件缓存(IO)->Memcached(当时最热门技术)
分库分表+水平拆分+Mysql集群
本质:数据库的读和写
早些年MyISAM:表锁,(读写时,所在的表被锁,十分影响并发性)
Innodb:行锁
最近
MySQL关系型数据库不够用,数据多且变化快
用:图型;JSON
MySQL有的使用他来存储较大文件,数据库很大,效率变低,如果有专门数据库负责,压力就会变小
目前一个基本的互联网项目
为什么要用
用户的个人信息、地理位置,用户自己产生的数据、用户日志爆炸式增长,nosql能很好的处理以上问题
NoSql
NoSql = Not Only Sql(不仅仅是Sql)
关系型数据库:表格,行,列
泛指非关系型数据库,传统关系型数据库很难对付web2.0时代,尤其是超大规模的高并发的社区。NoSql在当今大数据环境下发展十分迅速,Redis是发展最快的,而且是我们当下必须要掌握的一个技术!
很多的数据类型用户的个人信息,社交网络,地理位置。这些数据类型存储不需要一个固定的格式,不需要多余的操作就可以横向扩展!使用键值对来存放。
NoSql特点—解耦
1、方便扩展(数据之间没有关系,很好扩展)
2、大数据量高性能(NoSql的缓存记录级,是一种细粒度的缓存,性能会比较高,redis一秒写8万次读11万次)
3、数据类型是多样型(不需要设计数据库,随取随用)
4、传统的RDBMS和NoSql
传统的 RDBMS(关系型数据库)
- 结构化组织
- SQL
- 数据和关系都存在单独的表中
- 操作语言,定义语言
- 严格的一致性
- …
Nosql
- 不仅仅是数据
- 没有固定的查询语言
- 键值对存储,文档存储,图形数据库(社交关系)
- 最终一致性
- CAP定理和BASE (异地多活)
- 高性能,高可用,高可扩
- …
了解:3V+3高
- 大数据时代的3V:主要是描述问题的
1、海量Volume
2、多样Variety
3、实时Velocity
- 互联网需求的3高:主要是对程序的要求
1、高并发(Java JUC)
2、高可拓(随时水平拆分,机器不够了,扩展机器来解决)
3、高性能(保证用户体验和性能)
NoSql+RDBMS->《阿里巴巴的架构演进》
NoSql的四大分类
KV键值对
- 新浪:Redis
- 美团:Redis+Tair
- 阿里、百度:Redis+MemCache
文档型数据库:(bson格式和json一样)
- MongoDB
- mongodb基于分布式文件存储的数据库,C++编写,主要用来处理大量的文档
- MongoDB是一个介于关系型数据库和非关系型数据库中间的产品,MongoDB是非关系型数据库中功能最丰富,最想关系型数据库的。
- ConthDB
列存储数据库
- HBase
- 分布式文件系统
图关系数据库
不是存图形,放的是关系,比如朋友圈社交网络
- neo4j
- InfoGrid
- redisgraph
Redis
Redis是什么?
Redis(==Re==mote==Di==ctionary==S==erve),即远程字典服务
Redis能干嘛?
1、内存存储,持久化,内存中是断电即失,所以说持久化很重要(rdb、aof)
2、效率高,可以用于高速缓存
3、发布订阅系统
4、地图信息分析
5、计时器、计数器(浏览量! )
特性
1、多样的数据类型
2、持久化
3、集群
4、事物
测试性能
简单测试
1 | # 测试:100个并发连接 100000请求 |
测试图
redis默认有16个数据库,默认使用第0个
1 | 127.0.0.1:6379> config get databases # 命令行查看数据库数量databases |
keys * :查看当前数据库中所有的key。
flushdb:清空当前数据库中的键值对。
flushall:清空所有数据库的键值对。
Redis是单线程的,Redis是基于内存操作的。所以Redis的性能瓶颈不是CPU,而是机器内存和网络带宽。
那么为什么Redis的速度如此快呢,性能这么高呢?QPS达到10W+
Redis为什么单线程还这么快?
误区1:高性能的服务器一定是多线程的?
误区2:多线程(CPU上下文会切换!)一定比单线程效率高!
核心:Redis是将所有的数据放在内存中的,所以说使用单线程去操作效率就是最高的,多线程(CPU上下文会切换:耗时的操作!),对于内存系统来说,如果没有上下文切换效率就是最高的,多次读写都是在一个CPU上的,在内存存储数据情况下,单线程就是最佳的方案。
五大数据类型
Redis是一个开源(BSD许可),内存存储的数据结构服务器,可用作数据库,高速缓存和消息队列代理。它支持字符串、哈希表、列表、集合、有序集合,位图,hyperloglogs等数据类型。内置复制、Lua脚本、LRU收回、事务以及不同级别磁盘持久化功能,同时通过Redis Sentinel提供高可用,通过Redis Cluster提供自动分区。
key
在redis中无论什么数据类型,在数据库中都是以key-value形式保存,通过进行对Redis-key的操作,来完成对数据库中数据的操作。
exists key:判断键是否存在
del key:删除键值对
move key db:将键值对移动到指定数据库
expire key second:设置键值对的过期时间
type key:查看value的数据类型
1 | 127.0.0.1:6379> keys * # 查看当前数据库所有key |
关于TTL命令
Redis的key,通过TTL命令返回key的过期时间,一般来说有3种:
当前key没有设置过期时间,所以会返回-1.
当前key有设置过期时间,而且key已经过期,所以会返回-2.
当前key有设置过期时间,且key还没有过期,故会返回key的正常剩余时间.
关于重命名RENAME和RENAMENX
RENAME key newkey修改 key 的名称
RENAMENX key newkey仅当 newkey 不存在时,将 key 改名为 newkey 。
更多命令学习:https://www.redis.net.cn/order/
String
1 | 127.0.0.1:6379> set views 0 #初始浏览量为0 |
截取字符串getrange
1 | 127.0.0.1:6379> keys * |
替换setrange
1 | 127.0.0.1:6379> set key2 abcdefg |
setex:设置过期时间
setnx :不存在在设置(在分布式锁中常常使用)**
1 | 127.0.0.1:6379> setex key3 30 "hello" |
一次性获取,设置多个值:mset,mget
1 | 127.0.0.1:6379> keys * |
对象
1 | mset user:1{name:zhangsan,age:3} #设置一个user:1对象 值为json字符来保存一个对象 |
这里的key设计:user:{id}:{field}
1 | 127.0.0.1:6379> mset user:1:name fang user:1:age 2 |
先get在set——-getset
1 | 127.0.0.1:6379> getset db redis #没有值则返回nil |
string类型的使用场景:value除了字符串还可以是数字
- 计数器
- 统计多单位数量
- 粉丝数
- 对象缓存存储
Set
redis里面可以把list完成栈,队列,阻塞队列!
所有的list命令以l开头
1 | 127.0.0.1:6379> lpush list one #将一个或多个值插入列表头部(左)尾部添加值为Rpush |
移除元素
1 | 127.0.0.1:6379> LRANGE list 0 -1 |
lindex 通过下标获得值
1 | 127.0.0.1:6379> lindex list 1 |
llen返回列表长度
1 | ... |
移除指定值lrem
1 | 127.0.0.1:6379> LPUSH list three one four |
trim 修剪list
1 | 127.0.0.1:6379> LRANGE LIST 0 -1 |
rpop lpush,将列表右边元素移到另一个列表的左边
1 | ... |
lset list 0 item将下标为0的元素替换为item
1 | 127.0.0.1:6379> exists list #判断列表是否存在 |
linsert 在指定值的前面或者后面插入具体值
1 | 127.0.0.1:6379> LPUSH list one |
小结:
1.实际上是一个双向链表
2.key不存在,创建新链表
3.移除所有元素,相当于空链表,代表不存在
4.在两边插入或者改动值,效率高,中间元素,相对效率低
可以做消息队列
Hash
redis hash是一个String类型的field和value映射表,hash特别适合用来存储对象
set是一个简化的hash,只变动key,而value使用默认值填充,可以将一个Hash表作为一个对象存储,表中存放对象的信息
命令 | 作用 |
---|---|
HSET key field value | 将哈希表key的字段field的值设为value,重复设置则被覆盖,返回0 |
HMSET key field1 value1[field2 value2]… | 同时将多个键值对设置到哈希表key中 |
HSETNX key field value | 当字段不存在时,设置字段值 |
HEXISTS key field | 查看哈希表key中field是否存在 |
HGET key field value | 获取存储在field中的值 |
HMGET key field1 [field2..] | 获取所有给定field的值 |
HGETALL key | 获取在哈希表key的所有字段和值 |
HKEYS key | 获取哈希表key中的所有field |
HLEN key | 获取哈希表中字段的数量 |
HVALS key | 获取所有的值 |
HDEL key field1 [field2..] | 删除哈希表key中的一个或多个field |
HINCRBY key field n | 为key中的指定field整数值加上增量n,并返回增量后结果一样只适用于整数型字段 |
HINCRBYFLOAT key field n | 为key的指定字段的浮点数值加上增量n |
HSCAN key cursor [MATCH pattern] [COUNT count] | 迭代哈希表中的键值对 |
Zset
有序集合,每一个元素都会关联一个double类型的分数,redis通过分数来为集合中的成员进行从小到大的排序。socre相同,按字典顺序排序
有序集合成员是唯一的,但分数是可以重复的
应用案例:
- set排序 存储班级成绩表 工资表排序!
- 普通消息,1.重要消息 2.带权重进行判断
- 排行榜应用实现,取Top N测试
三种特殊数据类型
geospatial 地理位置
Geo 底层的索引结构是 sorted set
在redis 3.2版本推出;这个功能可以推算地理位置的信息,两地之间的距离
只有6个命令
- 有效的经度介于 -180 度至 180 度之间。
- 有效的纬度介于 -85.05112878 度至 85.05112878 度之间。
当用户尝试输入一个超出范围的经度或者纬度时,GEOADD命令将返回一个错误。
说明: 没有 GEODEL 命令,因为可以使用 ZREM来删除元素。Geo 底层的索引结构是 sorted set 。
1 | #添加地理位置 |
GEOPOS用于从给定的 key 里返回所有指定名称(member)的位置(经度和纬度),不存在的返回 nil;
命令接受可变数量的位置元素作为输入, 所以即使用户只给定了一个位置元素, 命令也会返回数组回复
1 | 127.0.0.1:6379> GEOPOS China:city Beijing Chongqing |
GEODIST 命令用于返回两个给定位置之间的直线距离。
如果两个位置之间的其中一个不存在, 那么命令返回空值。
距离单位参数说明:
- m :米,默认单位。
- km :千米。
- mi :英里。
- ft :英尺。
1 | 127.0.0.1:6379> GEODIST China:city Beijing Shanghai km |
GEODIST命令以给定的经纬度为中心, 返回键包含的位置元素当中, 与中心的距离不超过给定最大距离的所有位置元素
命令会返回额外的信息:
WITHDIST
: 在返回位置元素的同时, 将位置元素与中心之间的距离也一并返回。 距离的单位和用户给定的范围单位保持一致。
WITHCOORD
: 将位置元素的经度和维度也一并返回
WITHHASH
: 以 52 位有符号整数的形式, 返回位置元素经过原始 geohash 编码的有序集合分值。 这个选项主要用于底层应用或者调试, 实际中的作用并不大。
命令默认返回未排序的位置元素。 通过以下两个参数, 用户可以指定被返回位置元素的排序方式:
ASC
: 根据中心的位置, 按照从近到远的方式返回位置元素。 DESC
: 根据中心的位置, 按照从远到近的方式返回位置元素。
在默认情况下,命令会返回所有匹配的位置元素。 虽然用户可以使用 COUNT <count>
选项去获取前 N 个匹配元素, 但是因为命令在内部可能会需要对所有被匹配的元素进行处理, 所以在对一个非常大的区域进行搜索时, 即使只使用 COUNT
选项去获取少量元素, 命令的执行速度也可能会非常慢。 但是从另一方面来说, 使用 COUNT
选项去减少需要返回的元素数量, 对于减少带宽来说仍然是非常有用的
1 | 127.0.0.1:6379> GEORADIUS China:city 110 30 500 km withdist |
用于返回一个或多个位置元素的 Geohash 表示。Redis GEO 使用 geohash 来保存地理位置的坐标。
一个数组, 数组的每个项都是一个 geohash 。 命令返回的 geohash 的位置与用户给定的位置元素的位置一一对应。
1 | 127.0.0.1:6379> GEOHASH China:city Shanghai Hangzhou |
Hyperloglog 基数统计
基数:不重复元素
redis 2.8.9版本更新了该数据结构
优点:占用的内存是固定的,2^64不同的元素的技术,只需要12kb的内存,从内存角度看,该数据结构为首选
因为 HyperLogLog 只会根据输入元素来计算基数,而不会储存输入元素本身,所以 HyperLogLog 不能像集合那样,返回输入的各个元素。
其底层使用string数据类型
应用场景
网页的访问量(UV),用户访问多次,只能算作一个人
传统实现,存储用户的id,然后每次进行比较。当用户变多之后这种方式及其浪费空间,而我们的目的只是计数,Hyperloglog就能帮助我们利用最小的空间完成。
1 | ----------PFADD--PFCOUNT--------------------- |
如果允许容错,那么一定可以使用Hyperloglog !
如果不允许容错,就使用set或者自己的数据类型即可 !
Bitmaps
使用位存储;是一串连续的二进制数字,每一位所在的位置为偏移;非常的省内存
应用场景:只有是和否的统计,类似于打卡
1 | ------------setbit--getbit-------------- |
事务
基本操作
本质:一组命令的集合,一个事务中的所有命令都会被序列化,在事务执行过程中,会按照顺序执行
一次性,顺序性,排他性!执行一些列的命令
redis事务没有隔离级别的概念
所有命令在事务中并未直接执行,只有发起执行命令的时候才会执行
redis单条命令是保存原子性的,但是事务不保证原子性
redis的事务
开启事务
1 | 127.0.0.1:6379> multi |
命令入队
1 | 127.0.0.1:6379> SET k1 v1 |
执行事务,执行完之后,事务就不再开启了,需要则需重新开启
1 | 127.0.0.1:6379> exec |
放弃事务,事务队列中的命令都不会被执行
1 | 127.0.0.1:6379> DISCARD |
编译时异常:代码有问题、命令有错。事务中所有的命令都不会被执行
1 | 127.0.0.1:6379> multi |
运行时异常:事务队列中存在语法性错误,那么其他命令可以正常执行错误命令抛出异常
1 | 127.0.0.1:6379> multi |
悲观锁
很悲观,什么时候都会出问题,无论做什么都会加锁,很影响性能
乐观锁
认为什么都不会出问题,所以不会上锁,更新数据的时候判断一下,在此期间是否有人修改过这个数据
获取version
更新的时候比较version
redis监视测试
1 | 127.0.0.1:6379> set money 100 # 设置余额:100 |
测试多线程修改值,使用watch可以当作redis的乐观锁操作
我们启动另外一个客户端模拟插队线程。
线程1:
1 | 127.0.0.1:6379> watch money # money上锁 |
模拟线程插队,线程2:
1 | 127.0.0.1:6379> INCRBY money 500 # 修改了线程一中监视的money |
回到线程1,执行事务:
1 | 127.0.0.1:6379> EXEC # 执行之前,另一个线程修改了我们的值,这个时候就会导致事务执行失败 |
解锁获取最新值,然后再加锁进行事务。
解锁获取最新值,然后再加锁进行事务。
如果发现事务执行失败,先unwatch进行解锁。
注意:每次提交执行exec后都会自动释放锁,不管是否成功
持久化
redis是内存数据库,如果不讲内存中的数据库保存到磁盘,一旦服务器进程结束,数据库状态也会消失
RDB(Redis Database)
会在指定的时间内将内存中的数据集快照写入磁盘,恢复时将snapshot文件直接读入内存中
redis会单独fork一个子进程来进行持久化,会先将数据都写入一个一个临时文件,待持久化过程都结束了,再用这个临时文件替换上次持久化好的文件。整个过程中,主进程不进行任何IO操作,进而确保了较高性能;
如果需要进行大规模数据的恢复且对于数据恢复的完整性不是很敏感,那么RDB更高效;
缺点:最后一次持久化的数据可能丢失
默认的是RDB模式,一般情况下不需要修改
在生产环境下,有时候会对其进行备份
RDB保存的文件:dump.rdb
触发情况(如果没有文件,会自动生成)
- 满足save规则
- 执行flushall命令
- 退出redis
使用save命令,会立刻对当前内存中的数据进行持久化,但是会阻塞,save是同步命令,会占用redis的主进程,若redis数据非常多,则命令执行会很慢
bgsave是异步进行,进行持久化操作时候还可以继续响应客户端请求
两者对比
恢复rdb:将rdb文件放在redis的启动目录下,redis在启动时会自动检查并恢复其中的数据
查看需要存在的位置
1 | config get dir |
优点:
- 适合大规模的数据恢复
- 如果对数据完整性要求不高
缺点:
- 需要一定的时间间隔,如果redis意外宕机,最后一次修改的数据就没了
- fork进程的时候,会占用一定的内存空间
AOF(Append Only File)
将我们所有的命令都记录下来,类似history,恢复的时候,将所有操作再执行
以日志的形式记录每个写操作,将redis执行过的命令记录下来(读操作除外),只许追加文件而不可以改写文件。redis启动之初会读取该文件重新构建数据。换言之redis重启时会根据日志内容将写指令从前到后的执行一次
文件:appendonly.aof
默认是不开启,需要手动进行配置,之后重启redis就可以生效
如果aof文件有错误,则无法启动redis,需要修复文件
redis提供工具
1 | redis-check-aof --fix |
配置
1 | appendonly yes # 默认是不开启aof模式的,默认是使用rdb方式持久化的,在大部分的情况下,rdb完全够用 |
优点
- 每一次修改都同步,提高文件完整性
- 每秒同步一次,可能会丢失一秒的数据
- 从不同步,效率最高
缺点
- 数据文件AOF远远大于RDB,修复速度比rdb慢
- AOF运行效率也要比RDB慢,所以redis默认的配置是RDB
重写规则
aof默认是文件的无限追加,所以文件会很大
拓展
- 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 这条规则。
- 如果Enable AOF ,好处是在最恶劣情况下也只会丢失不超过两秒数据,启动脚本较简单只load自己的AOF文件就可以了,代价一是带来了持续的IO,二是AOF rewrite 的最后将 rewrite 过程中产生的新数据写到新文件造成的阻塞几乎是不可避免的。只要硬盘许可,应该尽量减少AOF rewrite的频率,AOF重写的基础大小默认值64M太小了,可以设到5G以上,默认超过原大小100%大小重写可以改到适当的数值。
- 如果不Enable AOF ,仅靠 Master-Slave Repllcation 实现高可用性也可以,能省掉一大笔IO,也减少了rewrite时带来的系统波动。代价是如果Master/Slave 同时倒掉,会丢失十几分钟的数据,启动脚本也要比较两个 Master/Slave 中的 RDB文件,载入较新的那个,微博就是这种架构。
Redis发布订阅
发布订阅是一种消息通信模式,发送者发送消息,订阅者接收消息
redis客户端可订阅任意数量的频道
订阅/发布消息图:
下图展示了频道channel1和订阅这个频道的三个客户端 client2、client5和client1之间的关系:
当有新消息通过publish命令发送给频道channel1时,这个消息会被发送给订阅她的三个客户端
命令
1 | PSUBSCRIBE pattern [pattern..] #订阅一个或多个符合给定模式的频道。 |
示例
1 | #------------订阅端---------------------- |
原理
Redis是使用C实现的,通过分析 Redis 源码里的 pubsub.c 文件,了解发布和订阅机制的底层实现,籍此加深对 Redis 的理解。
Redis 通过 PUBLISH 、SUBSCRIBE 和 PSUBSCRIBE 等命令实现发布和订阅功能。
每个 Redis 服务器进程都维持着一个表示服务器状态的 redis.h/redisServer 结构, 结构的 pubsub_channels 属性是一个字典, 这个字典就用于保存订阅频道的信息,其中,字典的键为正在被订阅的频道, 而字典的值则是一个链表, 链表中保存了所有订阅这个频道的客户端。发送消息则相当于遍历列表发送。
也可以设定对某一个key值发布消息,消息发布后所有被订阅的客户端都会收到消息,用法:实时聊天系统
客户端订阅,就被链接到对应频道的链表的尾部,退订则就是将客户端节点从链表中移除
缺点
- 如果一个客户端订阅了频道,但自己读取消息的速度却不够快的话,那么不断积压的消息会使redis输出缓冲区的体积变得越来越大,这可能使得redis本身的速度变慢,甚至直接崩溃。
- 这和数据传输可靠性有关,如果在订阅方断线,那么他将会丢失所有在短线期间发布者发布的消息。
应用
- 消息订阅:公众号订阅,微博关注等等(起始更多是使用消息队列来进行实现)
- 多人在线聊天室。
稍微复杂的场景,我们就会使用消息中间件MQ处理。
redis主从复制
概念
主从复制,是指将一台Redis服务器的数据,复制到其他的Redis服务器。前者称为主节点(Master/Leader),后者称为从节点(Slave/Follower), 数据的复制是单向的!只能由主节点复制到从节点(主节点以写为主、从节点以读为主)。
默认情况下,每台Redis服务器都是主节点,一个主节点可以有0个或者多个从节点,但每个从节点只能由一个主节点。
作用
- 数据冗余:主从复制实现了数据的热备份,是持久化之外的一种数据冗余的方式。
- 故障恢复:当主节点故障时,从节点可以暂时替代主节点提供服务,是一种服务冗余的方式
- 负载均衡:在主从复制的基础上,配合读写分离,由主节点进行写操作,从节点进行读操作,分担服务器的负载;尤其是在多读少写的场景下,通过多个从节点分担负载,提高并发量。
- 高可用基石:主从复制还是哨兵和集群能够实施的基础。
为什么使用集群
一般来说,要将Redis运用于工程项目中,只使用一台Redis是万万不能的(宕机),原因如下:
1、从结构上,单个Redis服务器会发生单点故障,并且一台服务器需要处理所有的请求负载,压力较大;
2、从容量上,单个Redis服务器内存容量有限,就算一台Redis服务器内存容量为256G,也不能将所有内存用作Redis存储内存,一般来说,单台Redis最大使用内存不应该超过20G。
电商网站上的商品,一般都是一次上传,无数次浏览的,说专业点也就是”多读少写”。
对于这种场景,我们可以使如下这种架构:
主从复制,读写分离! 80% 的情况下都是在进行读操作!减缓服务器的压力!架构中经常使用! 一主二从!
只要在公司中,主从复制就是必须要使用的,因为在真实的项目中不可能单机使用Redis!
总结
- 单台服务器难以负载大量的请求
- 单台服务器故障率高,系统崩坏概率大
- 单台服务器内存容量有限。
环境配置
只配置从库,不用配置主库!
1 | 127.0.0.1:6379> info replication |
复制3个配置文件,然后修改对应的信息
- 端口
- pid名字
- log文件名
- dump.rdb名字
启动单机多服务集群,可通过进程信息查看:
1 | ps -ef|grep redis |
一主二从配置
默认情况下,每台Redis服务器都是主节点;
一般情况下只用配置从机就好了
认老大!一主(79)二从(80,81)
使用此命令就可以为从机配置主机:
1 | SLAVEOF 127.0.0.1 [PORT] |
真实的从主配置应该在配置文件中配置,这样的话是永久的,我们这里使用的是命令,暂时的!
使用规则
1.从机只能读,不能写,主机可读可写但是多用于写。
1 | 127.0.0.1:6381> set name sakura # 从机6381写入失败 |
2.当主机断电宕机后,默认情况下从机的角色不会发生变化 ,集群中只是失去了写操作,当主机恢复以后,又会连接上从机恢复原状。
3.当从机断电宕机后,若不是使用配置文件配置的从机,再次启动后作为主机是无法获取之前主机的数据的,若此时重新配置称为从机,又可以获取到主机的所有数据。这里就要提到一个同步原理。
4.第二条中提到,默认情况下,主机故障后,不会出现新的主机,有两种方式可以产生新的主机:
- 从机手动执行命令slaveof no one,这样执行以后从机会独立出来成为一个主机
- 使用哨兵模式(自动选举)
如果没有老大了,这个时候能不能选择出来一个老大呢?手动!
如果主机断开了连接,我们可以使用SLAVEOF no one
让自己变成主机!其他的节点就可以手动连接到最新的主节点(手动)!如果这个时候老大修复了,那么就重新连接!
复制原理
Slave 启动成功连接到 master 后会发送一个sync同步命令
Master 接到命令,启动后台的存盘进程,同时收集所有接收到的用于修改数据集命令,在后台进程执行完毕之后,master将传送整个数据文件到slave,并完成一次完全同步。
全量复制:而slave服务在接收到数据库文件数据后,将其存盘并加载到内存中。
增量复制:Master 继续将新的所有收集到的修改命令依次传给slave,完成同步
但是只要是重新连接master,一次完全同步(全量复制)将被自动执行! 我们的数据一定可以在从机中看到!
层层链路
上一个M连接下一个S
这时候也可以完成主从复制
哨兵模式
(自动选举老大的模式)
概述
主从切换技术的方法是:当主服务器宕机后,需要手动把一台从服务器切换为主服务器,这就需要人工干预,费事费力,还会造成一段时间内服务不可用。这不是一种推荐的方式,更多时候,我们优先考虑哨兵模式。Redis从2.8开始正式提供了Sentinel(哨兵) 架构来解决这个问题。能够后台监控主机是否故障,如果故障了根据投票数自动将从库转换为主库。
哨兵模式是一种特殊的模式,首先Redis提供了哨兵的命令,哨兵是一个独立的进程,作为进程,它会独立运行。其原理是哨兵通过发送命令,等待Redis服务器响应,从而监控运行的多个Redis实例。
哨兵的作用:
- 通过发送命令,让Redis服务器返回监控其运行状态,包括主服务器和从服务器。
- 当哨兵监测到master宕机,会自动将slave切换成master,然后通过发布订阅模式通知其他的从服务器,修改配置文件,让它们切换主机。
然而一个哨兵进程对Redis服务器进行监控,可能会出现问题,为此,我们可以使用多个哨兵进行监控。各个哨兵之间还会进行监控,这样就形成了多哨兵模式。
假设主服务器宕机,哨兵1先检测到这个结果,系统并不会马上进行failover过程,仅仅是哨兵1主观的认为主服务器不可用,这个现象成为主观下线。当后面的哨兵也检测到主服务器不可用,并且数量达到一定值时,那么哨兵之间就会进行一次投票,投票的结果由一个哨兵发起,进行failover[故障转移]操作。切换成功后,就会通过发布订阅模式,让各个哨兵把自己监控的从服务器实现切换主机,这个过程称为客观下线。
测试
配置哨兵配置文件 sentinel.conf
1 | # sentinel monitor 被监控的名称 host port 1 |
后面的这个数字1,代表主机挂了,slave投票看让谁接替成为主机,票数最多的,就会成为主机!
哨兵模式优缺点
优点:
- 哨兵集群,基于主从复制模式,所有主从复制的优点,它都有
- 主从可以切换,故障可以转移,系统的可用性更好
- 哨兵模式是主从模式的升级,手动到自动,更加健壮 缺点:
- Redis不好在线扩容,集群容量一旦达到上限,在线扩容就十分麻烦
- 实现哨兵模式的配置其实是很麻烦的,里面有很多配置项
哨兵模式的全部配置
完整的哨兵模式配置文件 sentinel.conf
1 | # Example sentinel.conf |