1.Nosql讲解
(1)Nosql概述
①我们现在处于大数据时代
②90年代,一个基本的网站访问量不会太大,单个数据库就完全足够使用,那个时候,更多的是使用静态网页Html,所以服务器根本没有太大的压力
③这种情况下,整个网站的瓶颈是什么?
1)数据量如果太大,一个机器完全放不下
2)当数据的索引(Mysql使用B+Tree)很大时,机器的内存也不够用
3)访问量(读写混合)太大,一个服务器完全承受不了…
只要开始出现了这三种情况,就必须晋级…
(2)Memcached(缓存) + Mysql(免费) + 垂直拆分(读写分离)
①网站80%的情况下都是在执行读取操作,每次都要去查询数据库的话就会十分麻烦,而且耗费大量时间,所以说我们希望可以减轻数据库的压力,于是可以使用缓存机制来保证效率!
②发展过程: 优化Mysql底层的数据结构和索引----->文件缓存(IO)----->Memcached(当时最热门的缓存技术)
(3)分库分表 + 水平拆分 + Mysql集群
①技术和业务在发展的同时,对人的要求也越来越高
②数据库的本质: 读 + 写
③早些年使用MyISAM: 表锁,十分影响效率,在高并发下就会出现严重的问题(锁定表)
之后转战Innodb: 行锁(锁定行)
慢慢的就开始使用分库分表来解决写的压力
Mysql在那个年代推出了表分区,但是并没有多少公司使用,之后Mysql推出了集群,这个就很好的满足了那个年代的很多需求
④如今的年代,Mysql等关系型数据库就不够用了,数据量很多,变化很快
有的利用Mysql来存储一些比较大的文件(比如博客),图片 等,这样数据库表就很大,访问时效率较低
如果有一种数据库来专门处理这种数据的话,那么Mysql 的压力就会变得很小(我们现在研究的就是如何处理这些 问题)
(4)为什么要使用Nosql
①如今时代的高速发展,用户的个人信息、社交网络、地理位置、用户自己产生的数据、用户日志等爆发式增长
Mysql等关系型数据库已经不实用了,这个时候我们就需要使用Nosql数据库(非关系型数据库),Nosql数据库可以很好的处理这些情况
(5)Nosql的概念
①Nosql = Not only SQL (不仅仅是sql,泛指非关系型数据库)
②随着web2.0互联网的诞生,传统的关系型数据库很难对付web2.0时代,尤其是大规模的、高并发的社区,这样就会暴露出来很多难以克服的问题,Nosql在当今大数据环境下发展的非常迅速,是我们必须要掌握的一门技术!
③很多的数据类型(用户的个人信息、社交网络、地理位置、用户自己产生的数据、用户日志等)的存储不需要一个固定的格式,不需要多余的操作就可以实现横向扩展(多个用户同时操作)
④Nosql的特点
1)方便扩展(数据之间没有关系,耦合性低,很好扩展)
2)大数据量、高性能(Redis一秒可以写8万次,读取11万次,Nosql是一种细粒度的缓存,性能会比较高)
3)数据类型是多样性的(不需要事先设计数据库,随取随用,一般数据量比较大的表,很多人根本无法设计出来)
4)传统的关系型数据库和NoSQL的区别
a.传统的关系型数据库RDBMS
a)结构化组织(行和列)
b)结构化SQL查询
c)数据和关系都定义在单独的表中
d)数据操作语言,数据定义语言…
e)基础的事务
f)严格的一致性(任何时候都要求一致)
b.Nosql
a)不仅仅是数据
b)没有固定的查询语言
c)键值对存储、列存储、文档存储、图形数据库(社交关系)
d)最终一致性(只要求结果一致)
e)CAP定理和BASE理论
f)高性能、高可用、高扩展(三高)
c.了解: 3v + 3高
a)3v—主要是描述问题的
i.海量Volume
ii.多样Variety
iii.实时Velocity
b)3高—主要是对程序的要求(即解决问题)
i.高性能
ii.高并发
iii.高可扩
d.真正在公司中的实践: NoSQL + RDBMS一起使用才是最强的
2.阿里巴巴架构演进
(1)思考: 这么多东西难道都是放在一个数据库当中的吗
(2)开源才是技术的王道
任何一家互联网公司,都不可能只是简简单单的让用户能用就好了
大量公司做的都是相同的业务,随着这样的竞争,业务是越来越完善,然后对开发者的要求也是越来越高
(3)要知道,一个简单的网页背后的技术一定不是大家所想的那么简单
大型互联网存在的问题:
数据类型太多
数据源繁多,经常需要重构
数据经常需要大面积的改造
解决方法:
以上都是Nosql入门概述,不仅能够提高知识,还可以帮 助我们了解大厂的工作内容.
3.Nosql四大分类
(1)KV键值对
①新浪: Redis(用C语言编写的,单线程的)
②美团: Redis + Tair
③阿里、百度: Redis + Memcache
(2)文档型数据库(bson格式,和json类似)
①MongoDB(一般必须要掌握)
1)MongoDB是一个基于分布式文件存储的数据库,主要用来处理大量的文档,用c++编写的
2)MongoDB是一个介于关系型数据库和非关系型数据库中间的产品,MongoDB是非关系型数据库当中功能最丰富、最像关系型数据库的
(3)列存储数据库
①HBase
②分布式文件系统
(4)图形关系数据库
①它不是用来存图形的,存放的是关系,比如: 社交网络、广告推荐
4.Redis入门
(1)Redis是什么
①Redis(Remote Dictionary Server ),即远程字典服务
②是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API
③免费和开源
是当下最热门的Nosql技术之一,也被人们称之为结构化 数据库
④redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步
⑤
(2)Redis能干嘛
①内存存储、持久化(内存中是断电即失,所以说持久化很重要)
②效率高,可以用于高速缓存
③发布订阅系统(即一些简单的消息队列操作)
④地图信息分析
⑤计时器、计数器(比如: 某条信息的浏览量)
⑥…
(3)Redis的特性
①多样的数据类型
②持久化
③集群
④事务
⑤…
5.Redis安装(windows & linux)
(1)注意: Windows在GitHub上下载(已经停更很久了)
(2)Redis推荐都是在Linux服务器上搭建的,所以我们都是基于linux的Redis学习
(3)Redis解压目录
启动Redis
使用客户端连接Redis
Windows下使用Redis确实简单,但是推荐使用Linux去开发 使用Redis
(4)linux下安装Redis
①将Redis安装包发送到home目录下的redis文件夹
②将安装包移动到/opt文件夹目录下
1)mv redis-5.0.7.tar.gz /opt
③解压
1)tar -zxvf redis-5.0.7.tar.gz
④得到的Redis文件
⑤进入Redis解压后的文件
⑥基本的环境安装
1)yum install gcc-c++
2)make
3)make install (只是一个确认命令)
4)Redis默认的安装路径: /usr/local/bin/
5)移动配置文件,避免因为更改配置文件内容后对其产生影响
6)Redis默认不是后台启动的,修改配置文件使其后台启动
7)启动Redis服务(通过指定的配置文件启动)
8)测试是否成功–使用Redis客户端进行连接
9)查看redis的进程是否开启
ps -ef|grep redis: 查看此时开启的进程
10)如何关闭Redis服务: shutdown exit
11)后面我们会使用单机多Redis启动集群测试
12)测试性能
a.redis-benchmark 是一个压力测试工具
b.官方自带的性能测试工具
c.使用: redis-benchmark 命令参数
简单测试一下:
测试: 100个并发连接,100000个请求
redis-benchmark -h localhost -p 6379 -c 100 -n 100000
6.Redis的基础知识说明
(1)Redis默认有16个数据库
默认使用的是第0个数据库
可以使用select进行切换数据库
查看数据库大小
不同的数据库可以存放不同的值
查看数据库所有的key
清除当前数据库
清除所有数据库的内容
(2)Redis是单线程的
①Redis其实是很快的,官方表示Redis是基于内存操作的,CPU不是Redis的性能瓶颈,Redis的瓶颈其实是机器的内存和网络的带宽,官方觉得既然Redis可以使用单线程来实现,所以就使用单线程即可
②为什么Redis端口号默认为6379
粉丝效应(了解即可)
③为什么Redis是单线程还这么快
1)Redis是基于c语言编写的,官方提供的数据为100000+的QPS(每秒查询率),完全不比同样是使用key-value的MeMecache差
2)我们对于线程存在的误区
a.高性能的服务器一定是多线程的
b.多线程(CPU会上下文切换)一定比单线程效率高
我们要知道读取速度是CPU>内存>硬盘
3)核心: Redis是将所有的数据都全部存放在内存中的,所以说使用单线程去操作,效率就是最高的,多线程(CPU存在上下文切换,耗时的缺点),对于内存系统来说,如果没有上下文切换,效率就是最高的,每次读写都是在一个CPU上,所以这个就是最佳的方案
4)Redis的三大作用
a.可以用做数据库
b.缓存
c.消息中间件MQ
7.五大基本数据类型
(1)关于Redis-key的基本命令
EXISTS: 判断是否存在当前key
Move: 从哪个数据库移除哪些key
EXPIRE: 设置当前key多长时间后过期(单位是秒)
TTL: 查看当前key的剩余时间
Type: 查看当前key的类型
(2)String
①Append: 追加字符串(如果当前key不存在,就相当于set key value)
②Strlen: 获取字符串长度
③Incr: 自增1
④Decr: 自减1
⑤Incrby: 可以设置步长,指定增量
⑥Decrby: 指定减量
⑦Getrange: 截取字符串(闭区间)
⑧Setrange: 替换从指定位置开始的字符串
⑨Setex: (exist)设置过期时间(过期之前是可以访问的)
⑩Setnx: (not exist)不存在才设置(若存在则设置失败,在分布式锁中经常使用)
⑪Mset: 批量设置key
⑫Mget: 批量获取值
⑬Msetnx: 是一个原子性的操作,要么一起成功,要么一起失败
⑭设置对象
1)这里的key是一个巧妙的设计,user:{id}:{filed},这样的设计在Redis中是完全可以的
⑮Getset: 先get再set
1)如果不存在值,则返回nil
2)如果存在值,则获取原来的值并返回,然后再设置新的值
(3)List
①基本的数据类型,列表
②在Redis里面,我们可以把List完成栈、队列、阻塞队列(即两边均可通)
③!!!所有的List命令都是用l开头的
1)Lpush: 将一个或多个值,插入到列表的头部(左边)
④Rpush: 插入到列表的尾部(右边)
⑤Redis不区分大小写命令
⑥Lpop: 从左边取出值
⑦Rpop: 从右边取出值
⑧通过下标获取值
⑨Llen: 获取列表的长度
⑩Lrem: 移除指定的值(可以移除多个相同的值)
⑪Ltrim: 从左边开始通过下标截取指定长度的元素
此时的List已经被修改了,只剩下截取了的元素
⑫Rpoplpush: 移除列表的最后一个元素,并将它移动到新的列表当中
⑬Lset: 将列表中指定下标的值替换为另外一个值,相当于更新操作
Exists: 判断该列表是否存在,如果存在,会更新当前下标的值,不存在则会报错
⑭Insert: 将某个具体的值插入到列表中某个元素的前面或者后面
⑮小结
1)List实际上是一个链表
2)如果key不存在,则会创建新的链表
3)如果key存在,则会增加内容
4)如果移除了所有值,那么就是一个空链表
5)在两边插入或者改动值,效率最高,中间元素可能效率相对来说会低一点
6)可以做成栈(Lpush Lpop)、也可以做成队列(Lpush Rpop)
(4)Set
①set中的值是不能重复的
Sadd: 向set中添加元素
Smembers: 查看指定set中的所有值
Sismember: 判断某一个值是否在set集合中
②Scard: 获取set集合中的元素个数
③Srem: 移除指定的元素
④Srandmember: 随机抽选出一个元素
后面也可以指定随机抽取的元素个数
⑤Spop: 随机移除元素
⑥将set集合中一个指定的值移动到另外一个set集合中
⑦例如: 微博、b站都有共同关注(交集)
1)数字集合类
a.差集 sdiff
b.交集 sinter
c.并集 sunion
(5)Hash
①Map集合: key-map–key-
②本质上和String类型没有太大区别,还是一个简单的key-value
③Hdel: 删除指定的key字段,对应的value也一起删除
④Hlen: 获取当前hash的长度
⑤Hexists: 判断指定字段是否存在
⑥只获得所有的key
⑦只获得所有的value
⑧Hsetnx: 如果不存在,则可以设置,如果存在,则不设置
⑨Hash更适合于对象的存储,String更适合字符串存储
(6)Zset
①有序集合,即可以排序的集合
②在set的基础上,增加了一个值
③排序的实现
1)Zrangebyscore: 通过成绩进行排序
2)-inf: 负无穷 +inf: 正无穷
3)Zrevrange: 从大到小排序(默认从小到大排序)
4)Withscores: 打印成绩
④移除指定元素
⑤获取有序集合中的个数
⑥Zcount: 获取指定区间的元素个数
8.三种特殊数据类型
(1)Geospatial(Geo)
①地理位置 例如:定位、附近的人、打车距离的计算…
②Redis的Geo在Redis3.2版本就已经推出了,这个功能可以推算出地理位置的信息,包括: 两地之间的距离…
③总共有6个命令
④Geoadd: 将指定的地理空间位置(经度、纬度、名称)添加到指定的key中
规则:两级无法直接添加,我们一般会下载城市数据,直 接通过java程序一次性写入
有效经度: -180到180度
有效纬度: -85.05112878 到 85.05112878 度
⑤Geopos: 获取指定的城市的经度和纬度
获得当前定位,一定是一个坐标值
⑥Geodist: 返回两个给定位置之间的距离
1)返回两个位置的距离
2)如果两个位置之间的其中一个不存在,则返回空值
3)指定单位的参数 unit 必须是以下单位的其中一个:
默认为m
m 表示单位为米。
km 表示单位为千米。
mi 表示单位为英里。
ft 表示单位为英尺。
⑦Georadius: 以当前给定的经纬度为中心,找出某一半径内的元素
1)前提: 所有数据都应该存放到set集合中,才会让结果更加准确
2)Withdist: 显示到中间位置的距离
3)Withcoord: 经纬度
4)Count: 指定查找的个数
⑧Georadiusbymember: 找出位于指定范围内的元素,中心点是由给定的元素位置决定
⑨Geohash: 该命令将返回11个字符的Geohash字符串
1)其实就是将二维的经纬度转换为一维的字符串
2)如果两个字符串越接近,那么这两个城市的距离就越近
Geo底层的实现原理: 其实就是Zset有序集合
Geo命令中没有删除指令,我们可以使用Zset中的删除命令
来操作Geo,从而达到删除实现
所以我们可以使用Zset命令来操作Geo
(2)Hyperloglog
①用来做基数统计,是一种数据结构
1)基数: 不重复的元素,可以接收一定的误差
2)例如: 一个人统计一个网站的多个网页,统计网站访问人数时,只能算一个人
3)传统的统计方式: 用set保存用户的id,然后就可以统计set中的元素数量作为判断标准
这个方式如果保存大量的用户id,将会非常麻烦,因为这种方式非常消耗内存空间,对性能有影响
我们的目的不是为了保存用户id,而是为了计数
4)优点: 占用的内存是固定的,保存2^64个不同的元素,只需要占用12KB的内存,所以如果从内存角度来比较的话,Hyperloglog应该是首选
②测试使用
③如果允许容错,那么一定可以选择Hyperloglog
④如果不允许容错,就使用set集合或者自己实现的数据类型即可
(3)Bitmap
①位图,也是一种数据结构
②都是操作二进制位来进行记录,只有0和1两个状态
③实现: 统计用户信息、活跃或不活跃、登录或未登录、打卡和未打卡
④只要是两个状态的,就可以使用Bitmap
⑤例如: 使用周一到周日的打卡情况(后面不需要自己手写,直接使用java中的循环即可)
前面的第一个0表示周一
后面的1表示打卡了,0表示未打卡
⑥查看某一天是否打卡过
⑦统计打卡的天数
统计这周打卡的天数,如果小于总天数,就可以扣全勤奖
9.Redis事务操作
(1)Redis的单条命令是保证原子性的,但是Redis的事务是不保证原子性的
(2)事务的本质: 一组命令的集合
(3)一个事务中的所有命令都会被序列化,在事务执行过程中,会按照顺序执行(相当于队列)
(4)一次性、顺序性、排他性(即不会被其他语句干扰)
(5)Redis事务没有隔离级别的概念,所有的命令在事务中其实并没有直接被执行,只有发起执行命令的时候才会被执行
(6)事务执行步骤
①开启事务—multi
②命令入队—就是我们写的命令
③执行事务—exec
(7)正常执行事务
每一个事务执行完就会被销毁,要想再使用事务,就需要再次开启事务
(8)放弃事务
①一旦放弃事务,事务中的所有语句都不会被执行
(9)事务中的命令存在错误
①编译型异常(代码有问题,命令写错)
1)此时事务中的所有命令都不会被执行
②运行时异常(语法错误)
1)执行命令的时候,除了报错的命令无法执行,其他命令是可以被正常执行的,错误命令会抛出异常
10.Redis实现乐观锁(使用watch进行监控)
(1)悲观锁
①很悲观,认为什么时候都会出现问题,无论做什么都会加锁
(2)乐观锁
①很乐观,认为什么时候都不会出现问题,所以不会上锁,更新数据的时候去判断一下,在此期间是否有人修改过这个数据
②获取version
③更新的时候比较version
(3)Redis监视测试
①正常测试成功
事务正常结束
数据在此期间没有任何变动,这个时候就会正常执行成功
②测试多线程修改值,使用watch可以当作Redis的乐观锁来操作
注: 事务一旦提交,watch监控便会停止,如果需要监控,就需要再次开启监控
第一个线程中,事务提交之前,在第二个线程中对money做了改变,这样watch监控就会知道事务中的数据已经改变了,就不会提交事务,返回nil
一旦提交失败,此时的watch监控就没什么用了,就先解锁,获取最新的值,然后在重新加锁,再执行事务,便可成功
提交事务时,会比对监视的值是否发生了变化,如果没有变化,那么可以执行成功,如果变了,就会执行失败
11.通过Jedis来操作Redis
(1)我们可以使用Java来操作Redis
(2)什么是Jedis
①是Redis官方推荐的Java连接开发工具,使用java操作Redis的一个中间件
(3)测试
①导入相应的依赖
②编码测试
1)连接数据库
2)操作命令
3)断开连接
12.SpringBoot集成Redis
(1)Redis属于SpringData
(2)在springboot2.x之后,Jedis就被替换成了lettuce
(3)区别
①Jedis: 采用的直连,如果多个线程同时操作的话,是不安全的,要想避免不安全,就要使用Jedis pool连接池,更像BIO模式
②Lettuce: 底层采用的是Netty网络框架,实例可以在多个线程当中进行共享,不存在线程不安全的情况,可以减少线程的数量,更像NIO模式
(4)源码分析
①默认的RedisTemplate没有过多的设置,Redis对象都是需要序列化的
②两个泛型都是Object类型,后面使用需要强制转换
③由于String类型是Redis中最常使用的类型,所以说单独提出来了一个bean
(5)测试
①导入依赖
②配置连接
1)在配置文件中修改或者使用连接池时,应该选择lettuce pool连接池,因为Jedis pool连接池中的很多依赖在现在的springboot版本中根本没实现
③测试
序列化配置
(6)自定义redisTemplate
①存储对象时,需要序列化
如果不序列化,将会报错
②默认使用JDK序列化,我们也可以使用其他的序列化方式
③编写一个自己的redisTemplate模板
④使用自己编写的redisTemplate实现序列化
⑤在企业中,我们不会直接使用redisTemplate的原生API
而是会自己编写一个redisTemplate的工具类,将一些常用的方法实现
13.Redis配置文件详解
(1)启动的时候,就通过配置文件来启动
(2)分析
①单位
不区分大小写
②包含其他文件
③网络配置
④通用的配置
1)以守护线程的方式执行,默认为no,我们需要改为yes
2)pid文件: 如果以后台的方式运行,我们就需要指定一个pid文件
3)日志
4)日志生成的文件名,后面需要修改
5)是否总是显示logo,默认为yes
6)快照
a.主要用于持久化,即在规定的时间内,执行了多少次操作,则会持久化到文件中
b.Redis是内存数据库,如果没有持久化,那么数据就会断电即失
c.save 900 1
a)如果900秒内,至少有一个key进行了修改,我们就需要进行持久化操作
d.save 300 10
a)如果300秒内,至少有10个key进行了修改,我们就需要进行持久化操作
e.save 60 10000
a)如果60秒内,至少有10000个key进行了修改,我们就需要进行持久化操作
f.我们之后学习持久化,会自己定义这个时间
7)持久化如果出错了,是否还需要继续工作,默认为yes
8)是否压缩rdb文件,需要消耗一些cpu的资源,默认为yes
9)保存rdb文件的时候,是否需要进行错误的检查校验,默认为yes
10)rdb文件保存的目录
11)主从复制,后面讲解
12)设置密码
Redis默认是没有密码的,我们可以自己设置
方式一: 直接在配置文件中设置(不推荐使用这种)
方式二: 通过命令设置
13)客户端的一些限制
设置能连接Redis的最大客户端的数量
14)Redis能配置的最大内存容量
15)当Redis内存的使用达到上限之后的处理策略
maxmemory-policy 六种方式
1、volatile-lru:只对设置了过期时间的key进行LRU(默认值)
2、allkeys-lru : 删除lru算法的key
3、volatile-random:随机删除即将过期key
4、allkeys-random:随机删除
5、volatile-ttl : 删除即将过期的
6、noeviction : 永不过期,返回错误
16)AOF配置
默认是不开启AOF模式的,默认使用RDB方式进行持久化,在大部分的情况下,RDB完全够用
持久化文件的名字: “appendonly.aof”
always: 每次修改都会同步,消耗性能
everysec: 每秒执行一次同步,也有可能会丢失这一秒的数据
no: 不执行同步,这个时候操作系统会自己同步数据,速度最快
14.Redis持久化
(1)面试和工作中,持久化都是重点
(2)Redis是内存数据库,如果不将内存中的数据库状态保存到磁盘,那么一旦服务进程退出,服务器中的数据库状态也会消失,所以Redis提供了持久化功能
(3)RDB(Redis DataBase)
①概念
②分析
在指定的时间间隔内将内存中的数据集快照写入磁盘,也就是快照,它恢复时是将快照文件直接读到内存中
Redis会单独创建(fork)一个子进程来进行持久化,会先将数据写入到一个临时文件中,待持久化过程结束了,再用这个临时文件替换掉上次持久化好的文件,在整个过程中,主进程是不进行任何IO操作的,这样就确保了极高的性能,如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,那么RDB方式比AOF方式更加的高效,RDB的缺点就是最后一次持久化后的数据可能会丢失
我们默认的方式就是RDB,一般情况下够用,不需要修改这个配置
③RDB保存的文件的dump.rdb文件
④触发规则
1)save的规则满足的情况下,会自动触发RDB规则
2)执行flushall命令,也会触发我们的RDB规则
3)退出Redis时,默认也会产生RDB文件
备份就会自动生成一个dump.rdb文件
⑤如何恢复RDB文件
1)只需要将RDB文件放在我们的Redis启动目录中即可,Redis启动时就会自动检查dump.rdb文件并恢复其中的数据
2)如果在bin这个目录下存在dump.rdb文件,启动时就会自动恢复其中的数据
3)其实RDB文件自己默认的配置就够我们使用了,但是我们还是要去学习了解
4)有时候在生产环境中,我们会将这个RDB文件进行备份
⑥RDB文件的优点和缺点
1)优点
a.适合大规模的数据恢复
b.对数据的完整性要求不高
2)缺点
a.需要一定的时间间隔进行操作(我们可以自己设置时间间隔),如果Redis意外宕机了,那么最后一次修改的数据就没了
b.fork子进程的时候,会占用一定的内存空间
(4)AOF(Append Only File)
①概念
②分析
1)AOF其实就是将我们所有的命令都记录下来,恢复的时候就把这个文件的所有命令全部在执行一遍
2)以日志的形式来记录每一个操作,将Redis执行过的所有指令都记录下来(读取的操作不记录),只许追加文件,不可以改写文件,Redis启动之后就会读取该文件并重新构建数据,简而言之,就是Redis重启的话,就会根据日志文件的内容将写指令从前到后执行一遍,以完成数据的恢复工作
③AOF保存的文件是appendonly.aof文件
④默认是不开启AOF的,我们需要手动进行配置,只需要将appendonly改为yes即可
⑤重启Redsi就可以生效了
⑥如果AOF配置文件有错误,这个时候Redis是启动不起来的
1)我们先设置几个值
2)然后关闭Redis
3)再删除RDB文件并查看AOF文件
4)然后我们给AOF文件随便加点东西,导致其有错误
5)再重启Redis,发现无法启动
⑦解决办法
1)Redis给我们提供了一个工具: redis-check-aof
2)这个工具可以修复错误的AOF文件
3)如果AOF文件恢复正常,再次重启Redis,可以正常启动
⑧AOF文件的优点和缺点
1)优点
a.每一次修改都同步,文件的完整性会更好
b.默认每秒同步一次,可能会丢失一秒的数据
c.从不同步,效率最高
2)缺点
a.相对于数据文件来说,AOF远远大于RDB,修复的速度也比RDB慢
b.AOF运行的效率也要比RDB慢,所以我们Redis默认的配置就是RDB持久化
⑨重写操作(了解即可)
1)如果AOF文件大于64兆,Redis就会fork一个新的进程来将AOF文件进行重写
2)因为AOF默认的就是文件的无限追加,这样文件的大小就会越来越大
⑩扩展
1.Redis 默认开启RDB持久化方式,在指定的时间间隔内,执行指定次数的写操作,则将内存中的数据写入到磁盘中。
2.RDB 持久化适合大规模的数据恢复但它的数据一致性和完整性较差。
3.Redis 需要手动开启AOF持久化方式,默认是每秒将写操作日志追加到AOF文件中。
4.AOF 的数据完整性比RDB高,但记录内容多了,会影响数据恢复的效率。
5.Redis 针对 AOF文件大的问题,提供重写的瘦身机制。
6.若只打算用Redis 做缓存,可以关闭持久化。
7.若打算使用Redis 的持久化。建议RDB和AOF都开启。其实RDB更适合做数据的备份,留一后手。AOF出问题了,还有RDB
15.Redis实现订阅发布
(1)Redis发布订阅(pub/sub)是一种消息通信模式,发送者(pub)发送消息,订阅者(sub)接收消息
(2)Redis客户端可以订阅任意数量的频道
(3)订阅/发布消息图示
(4)下图展示了频道channel1,以及订阅这个频道的三个客户端之间的关系
(5)当有新消息通过PUBLISH命令发送给频道时,这个消息就会被发送给订阅它的三个客户端
(6)Redis发布订阅的常用命令
这些命令被广泛用于构建即时通信应用,比如网络聊天室、实时广播、实时提醒等
(7)测试一下
①订阅端
②发送端
(8)原理
①Redis是使用C实现的,通过分析Redis源码里的pubsub.c文件,可以了解发布和订阅机制的底层实现,借此加深对Redis的理解
②Redis通过PUBLISH、SUBSCRIBE、PSUBSCRIBE等命令实现发布和订阅功能
③通过SUBSCRIBE命令订阅某频道后,redis-server里面维护了一个字典,字典的键就是一个个频道,而字典的值便是一个链表,链表中保存了所有订阅这个频道的客户端,SUBSCIRBE命令的关键,就是将客户端添加到给定频道的订阅链表之中
④通过PUBLISH命令向订阅者发送消息,redis-server会使用给定的频道作为键,在它所维护的频道字典中查找订阅了这个频道的所有客户端的链表,遍历这个链表,将消息发布给所有的订阅者
⑤Pub/sub从字面上理解就是发布和订阅,在Redis中,你可以设定对某一个key值进行消息发布和消息订阅,当一个key值上进行消息发布后,所有订阅它的客户端都会收到相应的消息,这一功能最明显的用法就是用作实时消息系统、普通的聊天、群聊等功能
16.Redis集群环境搭建
(1)
17.Redis主从复制
(1)概念
①主从复制: 是指将一台Redis服务器的数据,复制到其他的Redis服务器,前者称为主节点(master/leader),后者称为从节点(slave/follower),数据的复制是单向的,只能由主节点到从节点,master以写为主,slave以读为主
②默认情况下,每台Redis服务器都是主节点,且一个主节点可以有多个从节点(或者没有从节点),但一个从节点只能有一个主节点
③主从复制的作用
1)数据冗余: 主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式
2)故障恢复: 当主节点出现问题时,可以由从节点提供服务,实现快速的故障恢复,实际上是一种服务的冗余
3)负载均衡: 在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从结点提供读服务(即写Redis数据时连接主节点,读取Redis数据时连接从节点),分担服务器负载,尤其是在写少读多的场景下,通过多个从结点分担读的负载,可以大大提高Redis服务器的并发量
4)高可用(集群)基石: 除了上述作用外,主从复制还是哨兵和集群能够实施的基础,所以说主从复制是Redis高可用的基础
(2)一般来说,要将Redis运行在工程项目中的话,只使用一台Redis是万万不能的,原因如下:
①从结构上来说,单个Redis服务器可能会发生单点故障,并且一台服务器需要处理所有的请求负载,压力较大
②从容量上,单个Redis服务器内存容量有限,就算一台Redis服务器内存容量为256G,也不能将所有的内存都用做Redis存储的内存,一般来说,单台Redis最大使用内存不应该超过20G
③例如: 电商网站上的商品,一般都是一次上传,无数次浏览的,也就是”少写多读”
④主从复制结构
主从复制,读写分离(一主二从)80%的情况下,都是在进行读操作,减缓服务器的压力,架构中经常使用
(3)在公司中,主从复制是必须要使用的,因为在真实的项目中不可能单机使用Redis
(4)环境配置
①查看当前库的信息
1)Role: 默认为主节点
2)Connected_slaves: 从节点个数
②复制三个配置文件,然后修改对应的信息
1)端口号
2)Pidfile
3)日志log
4)Dump.rdb
③启动3个Redis
④一主二从
1)默认情况下,每台Redis服务器都是主节点
2)主机(79)、从机(80、81)
3)我们一般情况下只需要配置从机就好了
(5)这个时候再查看主机的信息
(6)真实的主从配置应该在配置文件中配置,这样的话就是永久的,我们这里使用的是命令配置的,这样是暂时的
(7)解析细节
①主机可以写,但是从机不能写,只能读,主机中的所有信息和数据,都会自动被从机保存
②如果主机断开连接,从机依旧是连接到主机的,但是这个时候没有写操作。这个时候,如果主机连接上了,从机依旧可以和主机连上,可以直接获取到主机写的信息
③如果是使用命令行来配置的主从复制,这个时候如果从机断开了连接,再次重启时,就会变为主机,只要让它再次变为从机,并连接到主机,依旧是可以访问到主机的所有数据的
(8)主从复制原理
①slave从机启动成功后连接到master主机后会发送一个sync同步命令
②master主机接到命令后,会启动后台的存盘进程,同时收集所有接收到的用于修改数据集命令,在后台进程执行完毕之后,master主机将传送整个数据文件到slave从机,并完成一次完全同步
③全量复制: 而slave从机服务在接收到数据文件的数据后,将其存盘并加载到内存中
④增量复制: master主机继续将新的所有收集到的修改命令依次传给slave从机,完成同步
⑤但是只要是重新连接到master主机,一次完全同步(全量复制)将被自动执行,我们的数据就一定可以在从机中看到
18.宕机后手动配置主机
(1)层层链路
这个时候也是可以完成我们的主从复制的
(2)这个时候,我们将79断开,此时80、81依旧都是从节点,这个时候,我们可不可以选择一个老大来当主机呢(手动选择)
(3)如果这个时候,79又重新连接上了,也只是变回了主机,没有从机,只能重新配置从机
19.Redis哨兵模式(重点)
(1)这是一种自动选取主机的模式
(2)概述
①主从切换技术的方法是: 当主服务器宕机后,需要手动把一台服务器切换为主服务器,这就需要人工干预,费时费力,还会造成一段时间内服务不可用,这不是一种推荐的方式,更多时候,我们优先考虑哨兵模式,Redis 2.8开始就正式提供了Sentinel(哨兵)架构来解决这个问题
②能够自动后台主机是否出现了故障,如果出现了故障,就会根据投票数自动将从机转为主机
③哨兵模式是一种特殊的模式,首先Redis提供了哨兵的命令,哨兵是一个独立的进程,作为进程,它会独立运行,其原理是哨兵通过发送命令,等待Redis服务器的响应,从而监控运行时的多个Redis实例
④图示
这里的哨兵主要有两个作用
1.通过发送命令,让Redis服务器返回监控其运行状态,包括主服务器和从服务器
2.当哨兵监测到master宕机后,会自动将slave转换为master,然后通过发布订阅模式通知其他的从服务器,修改配置文件,让它们切换主机
(3)然而一个哨兵进程对Redis服务器进行监控,可能会出现问题,因此,我们可以使用多个哨兵进行监控,各个哨兵之间还会进行监控,这样就形成了多哨兵模式
假设主服务器宕机,哨兵1会先检测到这个结果,系统不会马上进行从新选举的一个过程,仅仅是哨兵1主观的认为主服务器不可用,这个现象为主观下线。当后面的哨兵也检测到主服务器不可用,并且数量达到了一定值时,那么哨兵之间就会进行一次投票,投票的结果由一个哨兵发起,进行故障转移操作。切换成功后,就会通过发布订阅方式,让各个哨兵把自己监控的从服务器实现切换主机,这个过程称为客观下线。
(4)测试一下
①我们目前的状态是一主二从
②首先编写哨兵配置文件
不能写错单词,否则会报错
格式: sentinel monitor 被监视的名称 host port 1
后面的数字1表示: 当主机挂了的时候,slave投票看让谁接替成为主机比较好,票数最多的就会成为新的主机
③启动哨兵
④测试当主机79宕机了之后,哨兵会怎么办
⑤如果主机此时回来了,只能归并到新的主机下,当作从机,这就是哨兵模式的规则
(5)哨兵模式的优点和缺点
①优点
1)哨兵集群,基于主从复制模式,所有的主从配置的优点,它全都有
2)主从可以切换,故障可以转移,系统的可用性就会更好
3)哨兵模式就是主从模式的升级版,从最开始的手动到自动,更加健壮
②缺点
1)Redis不好在线扩容,集群容量一旦到达上限,在线扩容就会显示十分麻烦
2)实现哨兵模式的配置其实是很麻烦的,里面有很多选择
(6)哨兵模式配置文件详解
Example sentinel.conf
哨兵sentinel实例运行的端口 默认26379
port 26379
哨兵sentinel的工作目录
dir /tmp
哨兵sentinel监控的redis主节点的 ip port
master-name 可以自己命名的主节点名字 只能由字母A-z、数字0-9 、这三个字符".-_"组成。
quorum 当这些quorum个数sentinel哨兵认为master主节点失联 那么这时 客观上认为主节点失联了
sentinel monitor
sentinel monitor mymaster 127.0.0.1 6379 2
当在Redis实例中开启了requirepass foobared 授权密码 这样所有连接Redis实例的客户端都要提供密码
设置哨兵sentinel 连接主从的密码 注意必须为主从设置一样的验证密码
sentinel auth-pass
sentinel auth-pass mymaster MySUPER–secret-0123passw0rd
指定多少毫秒之后 主节点没有应答哨兵sentinel 此时 哨兵主观上认为主节点下线 默认30秒
sentinel down-after-milliseconds
sentinel down-after-milliseconds mymaster 30000
这个配置项指定了在发生failover主备切换时最多可以有多少个slave同时对新的master进行 同步,
这个数字越小,完成failover所需的时间就越长,
但是如果这个数字越大,就意味着越 多的slave因为replication而不可用。
可以通过将这个值设为 1 来保证每次只有一个slave 处于不能处理命令请求的状态。
sentinel parallel-syncs
sentinel parallel-syncs mymaster 1
故障转移的超时时间 failover-timeout 可以用在以下这些方面:
#1. 同一个sentinel对同一个master两次failover之间的间隔时间。
#2. 当一个slave从一个错误的master那里同步数据开始计算时间。直到slave被纠正为向正确的master那里同步数据时。
#3.当想要取消一个正在进行的failover所需要的时间。
#4.当进行failover时,配置所有slaves指向新的master所需的最大时间。不过,即使过了这个超时,slaves依然会被正确配置为指向master,但是就不按parallel-syncs所配置的规则来了
默认三分钟
sentinel failover-timeout
sentinel failover-timeout mymaster 180000
SCRIPTS EXECUTION
#配置当某一事件发生时所需要执行的脚本,可以通过脚本来通知管理员,例如当系统运行不正常时发邮件通知相关人员。
#对于脚本的运行结果有以下规则:
#若脚本执行后返回1,那么该脚本稍后将会被再次执行,重复次数目前默认为10
#若脚本执行后返回2,或者比2更高的一个返回值,脚本将不会重复执行。
#如果脚本在执行过程中由于收到系统中断信号被终止了,则同返回值为1时的行为相同。
#一个脚本的最大执行时间为60s,如果超过这个时间,脚本将会被一个SIGKILL信号终止,之后重新执行。
#通知型脚本:当sentinel有任何警告级别的事件发生时(比如说redis实例的主观失效和客观失效等等),将会去调用这个脚本,
这时这个脚本应该通过邮件,SMS等方式去通知系统管理员关于系统不正常运行的信息。调用该脚本时,将传给脚本两个参数,
一个是事件的类型,
一个是事件的描述。
如果sentinel.conf配置文件中配置了这个脚本路径,那么必须保证这个脚本存在于这个路径,并且是可执行的,否则sentinel无法正常启动成功。
#通知脚本
sentinel notification-script
sentinel notification-script mymaster /var/redis/notify.sh
客户端重新配置主节点参数脚本
当一个master由于failover而发生改变时,这个脚本将会被调用,通知相关的客户端关于master地址已经发生改变的信息。
以下参数将会在调用脚本时传给脚本:
目前总是“failover”,
是“leader”或者“observer”中的一个。
参数 from-ip, from-port, to-ip, to-port是用来和旧的master和新的master(即旧的slave)通信的
这个脚本应该是通用的,能被多次调用,不是针对性的。
sentinel client-reconfig-script
sentinel client-reconfig-script mymaster /var/redis/reconfig.sh
20.Redis的缓存穿透、击穿及雪崩(面试高频、工作常用)
(1)缓存穿透(查不到)
①缓存穿透的概念很简单,用户想要查询一个数据,发现Redis内存数据库当中并没有,也就是缓存没有命中,于是就会向持久层数据库发起查询,发现也没有该数据,于是本次查询就会失败,当用户很多的时候,缓存都没有命中,于是所有的请求就都会去找持久层数据库,这就给持久层数据库造成了很大的压力,这时候就相当于出现了缓存穿透。
(2)解决方案
①布隆过滤器
是一种数据结构,对所有可能查询的参数以hash形式存储,在控制层先进行校验,不符合则丢弃,从而避免了对底层存储系统的查询压力
②缓存空对象
当存储层(持久层数据库)不命中后,即使返回的空对象也将其缓存起来,同时还会设置一个过期时间,之后再访问这
个数据将会从缓存中获取,保护了后端的持久层数据库
但是这个方法存在两个问题:
1.如果空值能够被缓存起来,这就意味着缓存需要更多的空间来存储更多的键,因为这当中可能会有很多空值的键
2.即使对空值设置了过期时间,还是会存在缓存层和存储层的数据会有一段时间窗口的不一致,这对于需要保持一致性的业务有一定的影响
(3)缓存击穿(查的次数太多,缓存过期)
①概述
1)这里需要注意和缓存穿透的区别,缓存击穿,是指一个key非常热点,中不停的扛着大并发,大并发集中对这一个点进行访问,当这个key在失效的瞬间,持续的大并发就会穿破缓存,直接请求数据库。
2)当某个key在过期的瞬间,有大量的请求并发访问,这类数据一般是热点数据,由于缓存过期,会同时访问数据库来查询最新数据,并且回写缓存,会导致数据库瞬间压力变大
②解决方案
1)设置热点数据永不过期
a.从缓存层面来看,没有设置过期时间,所以不会出现热点key过期后产生的问题,但是另一方面,Redis的缓存有可能到达极限,这个时候可能就需要删除一些key,并不能保证每个key能够永久不过期
2)加互斥锁
a.分布式锁: 使用分布式锁,可以保证对于每一个key同时只有一个线程去查询后端服务,其他线程没有获得分布式锁的权限,因此只需要等待即可。这种方式将高并发的压力转移到了分布式锁,因此对分布式锁的考验很大
(4)缓存雪崩
①概述
1)缓存雪崩,是指在某一个时间段,缓存集中过期失效,比如Redsi宕机
2)产生雪崩的原因之一,比如在双十一零点,会迎来一波抢购,这波商品比较集中的放在了缓存中,假设缓存一个小时,那么到了凌晨一点的时候,这批商品的缓存时间就过期了,而这批商品的访问查询,就会全部落到数据库上,对于数据库而言,就会产生周期性的压力波峰,于是所有的请求都会到达存储层,存储层的调用量会暴增,最终造成存储层挂掉
3)其实集中过期,并不是非常致命,比较致命的缓存雪崩,是缓存服务器某个结点宕机或断网,因为自然形成的缓存雪崩,一定是在某个时间段集中创建缓存,这个时候,数据库也是可以顶住压力的。无非就是对数据库产生了周期性的压力而已,而缓存服务节点的宕机,对数据库服务造成的压力是不可预知的,很有可能瞬间就把数据库压垮。
4)比如: 双11的时候,淘宝便会停掉一些服务,保证主要的服务高可用
②解决方案
1)Redis高可用
这个思想的含义就是,既然Redis有可能挂掉,那我多增设几台Redis,这样一台挂掉之后其他的还可以继续工作,其实说简单点就是搭建集群
2)限流降级
这个解决方案的思想就是,在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如: 对某个key只允许一个线程查询和写缓存,其他线程则等待
3)数据预热
数据预热的含义就是,在正式部署之前,我先把可能得数据先预先访问一遍,这样部分可能大量访问的数据就会加载到缓存中,在即将发生大并发访问之前手动触发加载缓存不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀。