一、 Nosql概述
为什么要用Nosql
1.单机MySQL的时代
最早的模型:
APP —> DAL —> MySQL
DAL:数据库访问层
一个基本能网站访问量不会太大,单个服务器足够。那时候更多使用静态html。
思考一下,这种情况下:整个网站的瓶颈是什么?
1.数据量如果太大,一个机器放不下了。
2.数据的索引( B + Tree),一个机器内存放不下
3.访问量(读写混合),一个服务器承受不了~
只要开始出现以上三种情况之一,那么必须要晋级。
2、Memcached(缓存) + MySQL + 垂直拆分(读写分离)
网站读写比例是10:1, 每次都要去查询数据库就十分的麻烦!所以需要使用缓存来减轻数据库压力。
发展过程:优化数据结构和索引—>文件缓存(IO)—> Memcached(当时最热门的技术)
3.分库分表 + 水平拆分 + MySQL集群
技术和业务在发展的同时,对人的要求也越来越高了。
==== 本质:数据库(读/写)====
早些年MyIASM引擎:表锁(100万 ,查张三密码,但是却将整个表都锁起来,其他查询整个表都sql都需要等前一条sql查询完毕)高并发下会出现严重的锁问题
早些年Innodb引擎:行锁
慢慢的就开始使用分库分表来解决写的压力 !
MySQL在哪个年代推出了表分区!这个并没有多少公司使用!
MySQL的集群,很好满足哪个年代的所有需求!
4.现在年代
目前一个基本的互联网公司项目
为什么要用NoSQL
用户的个人信息,社交网络地理位置,用户自己产生的数据,用户的日志等等等爆发式增长!
这时候我们就需要使用NoSQL数据库了,NoSQL可以很好的处理以上的情况!
什么是NoSQL
NoSQL = Not Only SQL (不仅仅是SQL)
关系型数据库:表格,行和列的形式记录的
泛指非关系型数据库的,随着web2.0互联网的诞生!传统的关系型数据库很难对付web2.0时代!尤其是超大规模的高并发的社区!暴露出来很多难以克服的问题,NoSQL在当今大数据环境下发展的十分迅速。
很多数据类型用户的个人信息,社交网络,地理位置。这些数据类型的存储不需要一个固定的格式!不需要多余的操作就可以横向扩展!
NoSQL特点
解藕!
1.方便扩展(数据之间没有关系,很好扩展!)
2.大数据量高性能(Redis一秒写8.1万次,读取11万,NoSQL的缓存记录级,是一种细粒度的缓存,性能比较高)
3.数据类型是多样型的(不需要事前去设计数据库,随取随用,如果是数据量十分大的表,很多人就无法设计了)
4.传统的RDBMS和NoSQL
–传统的RDBMS:
–结构化组织
–SQL
–数据和关系都存在单独的表
–操作,数据定义语言
–严格的一致性
–基础的事务
。。。
NoSQL
–不仅仅是数据
–没有固定的查询语言
–键值对存储,列存储,文档存储,图形数据库(社交关系)
–最终一致性
–CAP定理https://writings.sh/post/cap-and-consistency-models和BASE (异地多活)
–高性能,高可用,高可扩
。。。
了解: 3V+3高
大数据时代的3V:主要是用来描述问题的
1.海量Volume
2.多样Variety
3.实时Velocity
大数据时代的3高:主要是对程序的要求
1.高并发
2.高可拓(随时水平拆分,机器不够了,可以扩展机器来解决)
3.高性能(保证用户体验和性能)
真正在公司中的实践:NoSQL + RDBMS一起使用才是最强的。
阿里巴巴的演进分析
思考问题:淘宝页面这么多的东西难道都是在一个数据库中的吗?
1.商品的基本信息
名称、价格、商家信息
在关系型数据库就可以解决了, MySQL/Oracle
2.商品的描述、评论(文字比较多)
在文档型数据库中,MongoDB
3.图片
一些分布式文件系统,FastDFS
淘宝自己的TFS,谷歌的GFS,Hadoop的HDFS
阿里云的OSS
4.商品的关键字(搜索)
搜索引擎 solr、elasticseatch
淘宝用的是ISearch
5.商品热门的波段信息
内存数据库,redis、memache
6.商品的交易、外部的支付接口
三方应用
大型互联网应用问题:
数据类型太多了!
数据源繁多,经常重构!
数据要改造,大面积改造?
NoSQL的四大分类
- 新浪:redis
- 美团:redis + Tair
- 阿里、百度:redis + memeche
文档型数据库(bson格式和json一样):
- MongoDB(一般必须要掌握)
- MongoDB是一个分布式文件存储的数据库,C++编写 ,主要用来处理大量的文档!
- MongoDB是一个介于关系型数据库和非关系型数据中中间的产品!是非关系型数据库中功能最丰富,最像关系型数据库
- ConthDB ,国外的
列存储数据库
- HBase
- 分布式文件系统
图形关系数据库
- 他不是存图形,存的是关系,比如:朋友圈社交网络,广告推荐!
- Neo4j,infoGrid
Redis的概述
Redis是什么
Remote Dictionary Server,即远程字典服务。 是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。
redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-salve(主从)同步。
免费和开源!是当下最热门的NoSQL技术之一!也被人们称之为结构化数据库!
Redis能干嘛?
1.内存存储、持久化,内存中是断电即失,所以说持久化很重要(rdb和aof)
2.效率高,用户高速缓存
3.发布订阅系统
4.地图信息分析
5.计时器、计数器(微信、微博浏览量)
。。。
Redis的特性
1.多样的数据类型
2.持久化
3.集群
4.事务
。。。
windows安装
1.下载安装包:https://github.com/dmajkic/redis/releases
2.下载完毕得到压缩包
3.解压到自己电脑上的环境目录下就可以
4.开启redis,双击运行服务即可
5.使用redis客户端来连接redis服务
linux安装
1.下载安装包! redis-6.2.1.tar.gz
2.解压redis的安装包 tar zxvf redis-6.2.1.tar.gz
3.进入解压后的文件,可以看到redis的配置文件,redis.conf
4.基本的环境安装
yum install gcc-c++
make # 会把所有需要的文件给配置上
make install
或者
直接进入redis文件夹
sudo make
sudo make install
5.redis默认不是后台启动的,我们需要修改配置文件
daemonize yes
6.启动redis服务!
服务可以设定通过哪个配置文件来启动
redis-server redis.conf
7.连接redis
redis-cli -p 6379
测试性能
redis-benchmark是一个压力测试工具!官方自带
# 测试100个并发连接,每个并发100000个请求。
redis-benchmark -h localhost -p 6379 -c 100 -n 100000
如何查看这些分析呢?
基础知识
默认有16个数据库,0-15
dbsize查看当前数据库大小
select 3 切换数据库
思考:为什么redis是6379!(意大利女星名字缩写,在手机按键上的号码)
Redis是单线程的!
redis是很快的,官方表示,redis是基于内存操作的,cpu不是redis的性能瓶颈,redis的瓶颈是根据机器的内存和网络带宽,既然可以使用单线程来实现,就使用单线程了!
redis是c语言写的,官方提供的数据为100000+的QPS,完全不比同样是使用key-value的Memecache差!
redis为什么单线程还这么快?
1、误区1:高性能的服务器一定是多线程的吗?
2、误区2:多线程(和cpu调度有关的,上下文会切换的)一定比单线程效率高吗?
cpu>内存>硬盘
核心:redis是将所有的数据全部放在内存中的,所以说使用单线程去操作效率就是最高的。
3、对于内存系统来说,如果没有上下文切换效率就是最高的!多次读写都是在一个cpu上的,在内存情况下,这个就是最佳的方案!
五大数据类型
- String
- List
- Hash
- Set
- Zset
三种特殊类型
朋友的定位,附近的人,打车距离计算?
Redis的Geo在Redis3.2版本就推出了!这个功能可以推算地理位置的信息,两地之间的距离,方圆几里的人!
只有六个命令:
geoadd 添加
- geoadd china:city 116.40 39.90 beijing
- geoadd china:city 106.50 29.53 chongqing
有效的经度从-180到+180
有效的纬度从-85到+85
GEOPOS获取指定城市的经度和纬度
- GEOPOS china:city beijing
GEODIST两点之间的距离
- GEODIST china:city beijing chongqing
- GEODIST china:city beijing chongqing km
GEORADIUS以给定的经纬度为中心,找出某半径内的元素
我附近的人?(获取所有附近的人的地址和定位),通过半径来查询
- GEORADIUS china:city 116 40 1000 km
- GEPRADIUS china:city 116 40 5 km withdist withcoord count 20
GEORADIUSBYMEMBER 以元素为中心,找出某半径内的元素
- GEORADIUSBYMEMBER china:city beijing 1000 km
GEO底层原理其实就是Zset!我们可以使用Zset命令来操作GEO!
Hyperloglog
什么是基数?
A{1,3,5,6,8,7}
B{1,3,5,7,8}
基数(不重复的元素)= 5 ,数据量很大时候可以接受误差!
简介
Redis2.8.9版本就更新了Hyperloglog数据结构
Redis Hyperloglog 基数统计的算法!
优点:占用内存是固定的,2^64不同的元素的基数,只需要废12kb内存,如果要从内存角度来比较,Hyperloglog是首选!
网页的UV(一个人访问一个网站多次,但还是算作一个人!)
传统的方式,set保存用户的id,然后可以统计set中的元素数量作为标准判断!
这个方式如果保存大量的用户id,就会比较麻烦,占内存。我们的目的是为了基数,而不是为了保存用户id;
0.81%的错误率!统计uv任务,可以忽略不计的!
- PFadd mykey a b c d e f g h i j k l m n 创建第一组元素
- PFcount mykey 统计第一组元素基数数量
- PFadd mykey2 o p q r s t u v
- PFcount mykey2
- PFmerge mykey3 mykey mykey2 并集
- PFcount mykey3
个人觉得就是去重统计数量
Bitmaps
位存储
统计用户信息,活跃,不活跃!登录、未登录!打卡!两个状态的,都可以使用Bitmaps!
Bitmaps位图,数据结构!都是操作二进制位来进行记录,只有0和1两个状态!
365天= 365bit, 1字节=8bit ,46个字节左右。
- setbit sign 0 1 星期0 打卡
- setbig sign 1 0 星期1 未打卡
- setbig sign 2 0 星期2 未打卡
使用bitmaps来记录周一到周日的打卡
- getbit sign 2 查看星期二是否打卡
- bitcount sign 统计这周的打卡的记录
事务
**Redis事物本质:**一组命令的集合!一个事务中的所有命令都会被序列化,在事务执行的过程中,会按照顺序执行!
一次性、顺序性、排他性!执行一系列的命令!
Redis事务没有隔离级别的概念,也就是所有的命令在事务中并没有马上执行!只有发起执行命令的时候才会执行。
Redis的单条命令是保存原子性的,但是事务不保证原子性!
Redis的事务:
- 开启事务(multi)
- 命令入队(…)
- 执行事务(exec)
正常执行事务
- multi
- set k1 v1
- set k2 v2
- get k2 这时候并没有获取到,因为命令还没有执行
- set k3 v3
- exec 此时才会将以上命令顺序执行
放弃事务
- multi
- set k1 v1
- set k2 v2
- set k4 v4
- discard 取消事务
- get k4 是个nil,事务中的命令都不会被执行
编译型异常(代码有问题,命令有错),事务中的所有命令都不会被执行!
- multi
- set k1 v1
- set k2 v2
- set k3 v3
- getset k3 命令有错 没有getset
- set k4 v4
- set k5 v5
- exec 报错,会发现所有命令都没有被执行
运行异常(1/0),如果事务队列中存在语法性,那么执行命令的时候,其他命令是可以正常执行的,错误命令抛出异常!
- set k1 “v1”
- multi
- incr k1
- set k2 v2
- set k3 v3
- get k3
- exec 只有incr k1 没有执行成功,其他命令正常执行
监控! watch
悲观锁:
- 很悲观,认为什么时候都会出问题,无论干什么都会加锁,影响性能!
乐观锁:
- 很乐观,认为什么时候都不会出现问题,所以不会加锁!更新数据的时候去判断一下,在此期间是否有人修改过这个数据。
- 获取version
- 更新的时候比较version
Redis监视测试
- set money 100
- set out 0
- watch money 监视一个key
- multi
- descby money 20 花掉20
- incrby out 20
- exec 执行之前,另外一个线程修改了我们的值,这个时候事务会执行失败!
再启动一个线程,也就是再开启一个窗口连接redis,来修改money
- get money
- set money 1000 充值 这个money变了
这个时候其他正在执行事务修改money的过程的exec会执行失败
解决的话就是:
- unwatch 放弃监视
- watch money 重新监视
- multi
- decrby money 10
- incrby out 10
- exec
如果执行失败,获取最新的值就好。
Jedis
使用java来操作redis
什么是jedis,是redis官方推荐的java连接开发工具!使用java操作redis中间件!
Redis.conf
启动的时候就是通过配置文件来启动的!
单位
1k => 1000bytes
1kb => 1024bytes
1m => 1000000 bytes
1mb => 1024 * 1024 bytes
1g = 1000000000 bytes
1gb => 1024 * 1024 * 1024 bytes
配置文件 unit单位,对大小写不敏感!
包含
就是可以把多个配置文件组成一个
网络
bind 127.0.0.1 # 绑定的ip
protected-mode yes # 保护模式
port 6379 # 端口
通用
daemonize yes # 以守护进程的方式运行,默认是no
pidfile /var/run/redis_6379.pid # 如果以后台方式运行,我们就需要指定一个pid进程文件!
# 日志
# Specify the server verbosity level.
# This can be one of:
# debug (a lot of information, useful for development/testing) 测试和开发用
# verbose (many rarely useful info, but not a mess like the debug level) 很像debug
# notice (moderately verbose, what you want in production probably) 生产环境
# warning (only very important / critical messages are logged) 非常关键的日志信息才会打印
loglevel notice
loggile "" # 日志文件位置名
databases 16 # 数据库的数量,默认16
always-show-logo yes # 是否总是显示logo
快照
持久化(快照),在规定的时间内,执行了多少次操作,则会持久化到文件。 .rdb .aof
redis是内存数据库,如果没有持久化,那么数据断电即失!
# 如果900秒内,如果至少有1个key进行了修改,我们就进行持久化操作
save 900 1
# 300秒之内, 超过1个key
save 300 10
# 60秒内 10000key
save 60 10000
stop-writes-on-bgsave-error yes # 持久化出错,是否还需要继续工作
rdbcompression yes # 是否压缩.rdb文件,需要消耗一些cpu的资源
rdbchecksum yes # 保存rdb文件的时候进行错误检查校验
dir ./ # rdb文件保存的目录
REPLICATION 复制
。。。
安全security
requirepass 123456 # 设置登录密码 ,但一般我们都在命令中设置
# config set requirepass "123456"
# auth 123456 # 登录
限制clients
MAXCLIENTS 10000 # 设置能连接上redis的客户端数量
maxmemory <bytes> # redis配置最大的内存容量
maxmemory-policy noeviction # 内存达到上线之后的处理策略
# 有六种
1. volatile-lru:只对设置了过期时间的key进行lru(默认值)
2. allkeys-lru:删除lru算法的key
3. volatile-random:随机删除即将过期的key
4. all keys-random:随机删除
5. volatile-ttl:删除即将过期的
6. noeviction:永不过期,返回错误
APPEND ONLY模式,aof配置
appendonly no # 默认是不开启aof模式的,默认是是使用rdb方式持久化的,在大部分情况下,rdb完全够用!
appendfilename "appendonly.aof" # 持久化文件的名字
# allendfsync always # 每次修改都会同步,消耗性能
appendfsync everysec # 每秒同步一次,可能会丢失这1秒的数据
# appendfsync no # 不执行同步,这个时候操作系统自己同步数据
Redis持久化
redis是内存数据库,如果不将内存中的数据库状态保存到磁盘,那么一旦服务器进行退出,服务器中的数据库状态也会消失。所以redis提供了持久化功能!
RDB
什么是RDB
在主从复制来说,rdb就是备用的。从从机上面来说
在指定的时间间隔内将内存中的数据集快照写入磁盘,也就是行话讲的Snapshot快照,它恢复是将快照文件直接读到内存里。
Redis会单独创建(fork)一个子进程来进行持久化,会先将数据写入一个临时文件中,待持久化的过程都结束了,再用这个临时文件替换上次持久化好的文件。整个过程中,主进程是不进行任何IO操作的。这就确保了极高的性能。如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,那RDB方式要比AOF更加的高效。RDB的最后一次持久化后的数据可能丢失。
rdb保存的文件是dump.rdb
触发机制
1、save规则满足的情况下,会自动触发
2、执行flushall命令,也会触发
3、退出redis,也会触发
备份就会自动出现一个.rdb文件
如何恢复RDB文件
1、只需要将rdb文件放到redis启动目录就可以,redis启动的时候会自动检查rump.rdb文件来恢复其中的数据
2、查看需要存在的位置
config get dir
优点:
1、适合大规模的数据恢复!
2、如果对数据完整性要求不是特别高!
缺点:
1、需要一定的时间间隔去操作!如果redis意外宕机,这个最后一次修改的数据就没有了!
2、fork进程的时候,会占用一定的内存空间!
AOF
什么是AOF
将我们的所有命令记录下来,history,恢复的时候把这个文件的所有命令执行一遍!
以日志的形式来记录每个写操作,将redis执行过的所有执行记录下来(只记录写操作),只许追加文件但不可以改写文件,redis启动之初会读取该文件重新构建数据。
Aof保存的是appendonly.aof文件
这个文件是可以手动修改的,如果这个文件有问题,我们的redis服务是起不来的,我们可以通过redis-check-aof来检查修复一下!
- redis-check-aof --fix appendonly.aof
重写规则
aof默认就是文件的无限追加,文件会越来越大!
no-appendfsync-on-rewrite no
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
如果aof文件大于64m,太大了!fork一个新进程来对我们的文件进行重写!
优点:
1、每一次修改都同步,保证了文件的完整性
2、每秒同步一次,可能会丢失1秒的数据
3、从不同步,效率最高
缺点:
1、相对于数据文件来说,aof远远大于rdb,修复的速度也比rdb慢!
2、aof运行效率也要比rdb慢,所以我们的redis默认的配置也是rdb,而不是aof!
扩展:
1、RDB持久化方式能够在指定的时间间隔内对你的数据进行快照存储
2、AOF持久化方式记录每次对服务器的写操作,当服务器重启的时候会重新执行这些命令来恢复数据。AOF命令以redis协议追加保存每次的写操作到文件末尾,redis还能对aof文件进行后台重写,使得aof文件对体积不至于太大。
3、只做缓存、如果你只希望你的数据在服务器运行的时候存在,你也可以不使用任何持久化
4、同时开启两种持久化方式
- 在这种情况下,当redis重启的时候会优先载入aof文件恢复数据,因为在通常情况下,aof文件保存的数据集要比rdb文件保存的数据集更加完整!
- rdb的数据不实时,同时使用两者服务器重启也只会找aof文件,那要不要使用aof呢?作者建议不要,因为rdb更适合用于备份数据库(aof在不断变化不好备份),快速重启,而且不会有aof可能潜在的bug,留着作为一个万一的手段!
5、性能建议
- 因为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发布订阅(pub/sub)是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息。微信、微博、关注系统!
Redis客户端可以订阅任意数量的频道。
订阅/发布消息图:
第一个:消息发送者
第二个:频道
第三个:消息订阅者
发送端
subscribe ttt # 订阅一个频道 ttt 等待读取信息
publish ttt "hello! " # 发布涉发布消息到频道ttt
原理
Redis使用c实现的,通过分析redis源码里的pubsub.c文件,了解发布和订阅机制的底层实现,加深对redis的理解。
redis通过publish,subscribe和psubscribe等命令实现发布和订阅功能。
通过subscribe命令订阅某频道后,redis-server里维护了一个字典,字典的键就是一个个频道!字典的值则是一个个链表,链表中保存了所有订阅这个channel的客户端。subscribe命令的关键,就是将客户端添加到给定channel的订阅链表中。
通过publish命令向订阅者发送消息,redis-server会使用给定的频道作为键,在它所维护的channel字典中查找记录订阅了这个频道的所有客户端的链表,遍历这个链表,将消息发布给所有订阅者。
Pub/Sub从字面上理解就是发布(publish)和订阅(subscribe),在redis中,你可以设定对某一个key进行消息发布及消息订阅,当一个key值上进行了消息发布后,所有订阅它的客户端都会收到效应的消息。这一功能最明显的用法就是用做实时消息系统,比如即使聊天,群聊等功能!
使用场景:
1、实时消息系统
2、实时聊天
3、订阅、关注系统
稍微复杂的场景我们就会使用消息中间件MQ,kafka
redis主从复制
概念
主从复制,是将一台redis服务器的数据,复制到其他的redis服务器。前者称为主节点(master/leader),后者称为从节点(slave/follower);数据的复制是单向的,只能从主节点到从节点。master以写为主,slave以读为主
默认情况下,每台reids服务器都是主节点;且一个主节点可以有多个从节点,或者没有从节点,但一个从节点只能有一个主节点。
主从复制的作用主要包括:
1、数据冗余:主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式。
2、故障恢复:当主节点出现问题后,可以由从节点提供服务,实现快速的故障恢复;实际上是一种服务的冗余。
3、负载均衡:在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务(即写redis数据时应用连接主节点,读redis数据时应用连接从节点),分担负载均衡;尤其是在写少读多点情景下,通过多个从节点分担负载,可以大大提高redis服务器的并发量。
4、高可用基石:除了上述作用以外,主从复制还是哨兵和集群能够实施的基础,因此说主从复制是redis高可用的基础。
一般来说,要将redis运用于工程项目中,只使用一台redis是万万不能的,原因如下:
1、从结构上,单个redis服务器会发生单点故障,并且一台服务器需要处理所有的负载均衡,压力太大;
2、从容量上,单个redis服务器内存容量有限,就算一台redis服务器内存容量为256G,也不能将所有内存用做redis存储内存,一般来说单台redis服务器的最大使用内存不应该超过20G。
电商网站上的商品,一般都是一次上传,无数次浏览的,说专业店就是“多读少写”
对于这种场景,我们可以使用如下这种结构:
环境配置
只配置从库,不用配置主库。因为默认redis就是一台主库。
info replication # 查看当前库的信息
roke:master # 角色 master
connected_slaves:0 # 没有从机
1、复制几份配置文件
2、修改对应的配置文件
- port 改一下
- logfile 改一下
- pidfile 改一下
- dbfilename dump_xxx.rdb 改一下
- demonize yes 后台开启
3、然后指定不同的配置文件进行启动
- redis-server xxx.conf
现在是好几台服务器都是主服务器
我们去需要配置从服务器的客户端
slaveof 127.0.0.1 6379 # 主机的ip和端口号
我们这里使用的命令,是暂时的。
真实的是需要我们在配置文件中修改。
去对应的配置文件中:
- replicaof ip port
这样这台服务器启动的时候就是从机器了。
细节
主机负责写,从机只能读不能写!主机中所有的信息和数据,都会自动被从机保存!这样主机挂了,从机数据还在。如果主机回来了,从机依然可以直接获取到主机写的数据!
如果是使用命令配置的主从,这个时候如果重启了,就会变回主机!只要变为从机,立马就可以从主机中获得数据!
复制原理
slave启动成功连接到master后会发送一个sync同步命令
master接收到命令,启动后台的存盘进程,同时收集所有接收到的用于修改数据集命令,在后台进行执行完毕后,master将传送整个数据文件到slaver,并完成一次完全同步。
全量复制:slave服务在接收到数据库文件数据后,将其存盘并加载到内存中
增量复制:master继续将新的所有收集到的修改命令依次传给slave,完成同步
但是只要是重新连接master,一次完全同步(全量复制)将会被自动执行!我们的数据一定会在从机中看到!
层层链路
上一个Master连接下一个S!
这时候80端口也是个从节点!
如果没有老大了,这个时候能不能选择一个老大出来?哨兵模式没出来之前都是手动的~
如果master断开了,我们可以使用
- SLAVEOF no one
来使目前这个成为主节点!
这个时候最开始的master再恢复,也没用了。除非重新配置
哨兵模式
自动选举老大的模式
概述
主从切换技术的方法是:当主服务器宕机后,需要手动把一台服务器切换为主服务器,这就需要人工干预。所以我们会考虑哨兵模式!Redis从2.8开始就正式提供了Sentinel(哨兵)架构来解决这个问题!
谋朝篡位的自动版,能够后台监控主机是否故障,如果故障了就根据投票数自动将从库转为主库。
哨兵模式是一种特殊的模式,首先Redis提供了哨兵的命令,哨兵是一个独立的进程,作为进程,他会独立运行。其原理是哨兵通过发送命令,等待Redis服务器响应,从而监控运行的多个Redis实例!
这里的哨兵有两个作用
- 通过发送命令,让Redis服务器返回监控其运行状态,包括主服务器和从服务器。
- 当哨兵监控到master当即,会自动将slave切换成master,然后通过发布订阅模式通知其他的从服务器,修改配置文件,让他们切换主机。
然而一个哨兵进程对Redis服务器进行监控,可能会出现问题,为此我们可以使用多个哨兵进行监控。各个哨兵之间还会进行监控,这样就形成了多哨兵模式!
假设主服务器宕机,哨兵1先检测到这个接口,系统兵不会马上进行failover过程,仅仅是哨兵1主管到认为主服务器不可用,这个现象称为主观下线,当后面的哨兵也检测到主服务器不可用,并且数量达到一定值的时候,那么哨兵之间就会进行一次投票,投票的结果由一个哨兵发起,进行failover故障转移操作。切换成功后,就会通过发布订阅模式,让各个哨兵把自己监控的从服务器实现切换主机,这个过程称为客观下线。
测试!
假设目前的状态是一主二从!
1、配置哨兵配置文件 sentinel.conf
# sentinel monitor 被监控的名称 host port 1
sentinel monitor myredis 127.0.0.1 6379 1
后面的这个数字1代表主机挂了,slave投票看让谁成为主机,票数最多的,就会成为主机!
2、启动哨兵!
redis-sentinel sentinel.config
3、如果master节点断开,这个时候就会在从机中随机选择一个服务器(这个里面有一个投票算法!)
这个时候主机回来了,也会归并到新的主机下,作为从机,这就是哨兵模式的规则!
哨兵模式
优点:
1、哨兵集群,基于主从复制模式,所有的主从配置优点,它全有
2、主从可以切换,故障可以转移,系统的可用性更好
3、哨兵模式就是主从模式的升级,手动到自动,更加健壮!
缺点:
1、redis不好在线扩容,集群容量一旦达到上限,在线扩容就很麻烦
2、实现哨兵模式的配置其实是很麻烦的,里面有很多选择
哨兵配置:
Redis缓存穿透和雪崩
Redis缓存的使用,极大的提升了应用程序的性能和效率,特别是查询数据方面。但同时,它也带来了一些问题。其实,最要害的问题,就是数据的一致性问题,从严格意义上讲,这个问题无解。如果对数据的一致性要求很高,那么就不能使用缓存。
另外的一些典型问题就是,缓存穿透、缓存雪崩和缓存击穿。目前,业界也都有比较流行的解决方案。
缓存穿透(查不到)
概念
缓存穿透的概念很简单 ,用户想要查询一个数据,发现redis内存数据库中没有,也就是缓存没有命中,于是向持久层数据库查询。发现也没有,于是本次查询失败。当用户很多的时候,缓存都没有命中,也是都去请求了持久层数据库。这回给持久层数据库造成很大的压力,这时候就相当于出现了缓存穿透。
解决方案
布隆过滤器
布隆过滤器是一种数据结构,对所有可能查询的参数以hash形式存储,在控制层先进行校验,不符合则丢弃,从而避免了对底层存储系统的查询压力。
缓存空对象
当存储层不命中的后,即使返回空对象也将其缓存起来,同时会设置一个过期时间,之后再访问这个数据的时候会从缓存数据库获取,保护后端数据源。
但是使用这种方法也会存在两个问题:
1、如果空值能够被缓存起来,这就意味着缓存需要更多的空间存储更多的键,因为这当中可能有很多控制的键。
2、即使对空值设置了过期时间,还是会存在缓存层和存储层的数据有一段时间窗口的不一致,这对于需要保持一致性业务会有影响。
缓存击穿(并发太大,缓存过期!)
概述
这里需要注意和缓存击穿的区别,缓存击穿,是指一个key非常热点,在不停的扛着大并发。大并发集中对这一个点进行访问,当这个key在失效的瞬间,持续的大量并发就会穿破缓存,直接请求数据库。
当某个key在过期的瞬间,有大量的并发请求,这类数据一般是热点数据,由于缓存过期,会同时访问数据库来查询最新数据,并且会写缓存,会导致数据库瞬间压力过大。
解决方案
1、设置热点数据永不过期
从缓存层面来说,没有设置过期时间,所以不回出现热点key过期后产生的问题。
2、加互斥锁
分布式锁:使用分布式锁,保证对于每个key同时只有一个线程去查询后端服务,其他线程没有获得分布式锁的权限,因此只需要等待即可。这种方式将高并发的压力转移到了分布式锁,因此对分布式锁的考验很大。
缓存雪崩
概念
缓存雪崩,是指某一个时间段,缓存集中过期失效,Redis宕机!
产生雪崩的原因之一,比如在双十一的时候,马上就要到双十一零点,很快就回迎来一波抢购,这波商品时间比较集中的放入了缓存,假设缓存过期时间是一个小时。那么到了凌晨一点的时候,这批商品的缓存 都过期了。而对这批商品的访问查询,都会落到数据库,对于数据库而言,就会产生周期性的压力波峰。于是所有请求都会达到存储层,存储层的调用会暴增,造成存储层会挂掉的情况!
双十一:会停掉一些服务,(保证主要的服务可用)
解决方案
1、redis高可用
这个思想的含义是,既然redis有可能挂掉,那么我多增几台redis,这样一台挂掉,其他还可以工作,其实就是搭建集群。异地多活!
2、限流降级
这个方案的思想是,在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某一个key指允许一个线程查询数据和写缓存,其他线程等待。
3、数据预热
数据加热的含义是在正式部署之前,我先把可能的数据显预先访问一遍,这样部分可能大量访问的数据会加载的缓存中。在即将发生高并发访问钱手动出发加载缓存不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀。