1.NoSQL概述及发展
(1)单机MySQL发展
单机MySQL(只有一台mysql机器)------Memcached 缓存+mysql+垂直拆分(读写分离)-------分库分表+水平拆分+Mysql集群
-
在单机MySql时代:只有一台数据库服务器,当时的数据量不大就一台机器就可以解决
-
后面出现了数据量大的情况,同时读写的需求增加
网站百80%的操作都是在读,减轻数据库的压力可以使用缓存提高效率,缓存使用什么技术都没所谓
就出现了缓存技术使读写加快(缓存:开辟一块缓存区域,先从数据库中取出来之后放在缓存里,之后就直接从缓存中读取,没有重新访问数据库的操作)
缓存的技术发展:优化数据结构和索引-----文件缓存(IO)-----Memcached
-
读的问题可以利用缓存进行解决,但是写数据的问题也比较大:
利用mysql集群解决读的问题 分库分表+水平拆分+mysql集群
(2)NoSQL概述
①概念:
NoSQL:Not Only SQL 不仅仅是SQL 非关系型数据库 不保证数据库的ACID性 去掉了关系数据库的关系型
NoSQL的出现:
互联网web2.0网站的兴起,传统的关系数据库在处理web2.0网站,特别是超大规模和高并发的SNS类型的web2.0纯动态网站已经显得力不从心,出现了很多难以克服的问题,而非关系型的数据库则由于其本身的特点得到了非常迅速的发展。
用户的个人信息、社交网络、地理位置、用户自己生产的数据、用户日志等等数据爆发式的生长
NoSQL数据库的产生就是为了解决大规模数据集合多重数据种类带来的挑战,特别是大数据应用难题。
②优势
主要是易扩展(数据之间没有关系,比较容易扩展)、大数据量高性能(读写性能特别快)、灵活的数据模型(nosql不需要提前为数据建立字段)、高可用(可以在不太影响性能的情况在方便实现高可用架构比如HBase模型)
传统的RDBMS:
-
结构化组织:
-
sql有关系的
-
数据和关系都存储在表中
-
严格的数据一致性
-
事务
NoSQL:
-
没有固定的查询语言
-
很多存储的方式:列存储、文档存储、图形存储(社交关系)
-
可以不满足严格一致性
-
高可用、高性能、高可扩展性
-
灵活的数据模型
③分类
-
KV键值对
新浪:Redis
美团:Redis+Tair
阿里,百度:Redis+memecache
-
文档型数据库(Bson格式和Json格式)
MongoDB
MongoDB是一个基于分布式文件存储的数据库,C++编写,用来处理大量的文档
MongoDB是一个介于关系型数据库和非关系型中间的产品,非关系型数据库中功能最丰富的,最像关系型数据库的
ConthDB
-
列存储数据库
HBase
分布式文件系统
-
图形关系数据库
不是用来存放图形的,是用来寸关系的,朋友圈社交,广告推荐
Neo4j,InfoGrid
2.Redis简介
(1)基本内容
Redis:Remote Dictionary Server 远程字典服务
c语言编写、基于网络、基于内存可持久化的日志型、Key-value型数据库、提供多种语言的API
读的速度是11w,写的速度是8w
-
Redis作用
-
内存存储,持久化,内存是断电即失的,持久化很重要, 持久化有两种机制(RBD,AOF)
-
效率高,可以用于高速缓存
-
发布订阅系统
-
地图信息分析
-
计数器,(浏览量)
-
-
特性
-
多样的数据类型
-
持久化
-
集群
-
事务
-
-
常用网站
可以做数据库、缓存和消息中间件MQ
Redis默认端口号:6379
redis默认有16个数据库,默认使用的第0个数据库
(2)Redis单线程:
Redis是基于内存操作,CPU不是redis的性能瓶颈,Redis的瓶颈是根据机器的内存和网络带宽,既然可以单线程可以实现,那就使用单线程
为什么单线程还这么快?
高性能的服务器不一定都是多线程
多线程需要CPU上下文切换也比较消耗资源
核心:Redis是将所有的数据全部都是挡在内存当中的,对于内存系统来说没有上下文切换效率就是最高的,多次读写都是在一个cpu上,在内存系统中单线程就是最好的解决方案
3.Redis数据类型
(1)基本数据类型
①String类型
可以包含任何数据,如图像、序列化对象。一个键最多能存储512MB。
使用场景:
1、String通常用于保存单个字符串或JSON字符串数据
2、因为String是二进制安全的,所以可以把保密要求高的图片文件内容作为字符串来存储
3、计数器:常规Key-Value缓存应用,如微博数、粉丝数。INCR本身就具有原子性特性,所以不会有线程安全问题
-
计数器 :incr 自增1,可以用作增加浏览量increase
-
统计多单位的数量
-
粉丝数
-
对象缓存
②List
类似于Java中的LinkedList。底层实际上是一个链表,单值单value。
元素最多为 2^32-1个
应用场景:
1、对数据大的集合数据删减 列表显示、关注列表、粉丝列表、留言评价...分页、热点新闻等
2、任务队列 list通常用来实现一个消息队列,而且可以确保先后顺序,不必像MySQL那样通过 order by来排序
在Redis中可以将List作为栈、队列、阻塞队列,消息排队、消息队列、堆、栈
-
实际上是一个链表,left、right 都可以插入值
-
如果key不存在,创建新的链表
-
如果key存在,新增value
-
移除所有值,空链表
-
在两边插入效率最高,对中间元素操作,效率会降低
③Hash
map集合,key - map 本质和string类型没有太大区别,还是一个简单的key - value
做用户信息保存,比用string类型好点,存储经常变动的信息,hash更适合存储对象
每个hash可 以存储2^32-1(40亿左右)键值对。
应用场景:
通常用来存储一个用户信息的对象数据
1、相比于存储对象的string类型的json串,json串修改单个属性需要将整个值取出来。而hash不 需要。
2、相比于多个key-value存储对象,hash节省了很多内存空间
3、如果hash的属性值被删除完,那么hash的key也会被redis删除
④Set集合
:唯一、无序,单值多value。元素最多为2^32-1个
-
将用户放到set中,共同关注、共同爱好、推荐好友
-
利用唯一性,可以统计访问网站的所有独立 IP
集合可以做交集并集之类的
确定性(集合中的元素必须是确定的)
互异性(集合中的元素互不相同。)
无序性(集合中的元素没有先后之分)
⑤Zset
有序集合。有序且不重复(成员唯一)元素最多为2^32-1个
可以作为一个排行榜功能,进场刷新,或者任务等级排序之类的,都可以做
在set的基础上增加了一个值score排序
(2)特殊数据类型
①geospatial(地图)
以推算地理位置的信息,两地之间的距离,方圆几公里之内的人
需要注意:
-
地球南北两极无法直接添加,
-
一般会下载城市数据,直接通过Java程序一次性导入
-
有效经纬度范围从-180到180,超出范围时会返回错误,
-
key由(纬度,经度,名称)构成
②hyperloglog (计数唯一事物)
是一种概率数据结构,计数唯一事物,从技术上讲估计一个集合的基数,通常计数唯一项需要使用成比例的内存,因为需要记录使用过的元素,以免多次记录,但是hyperloglog的算法可以用内存换精度,虽然有误差,但是误差小于1%,算法的神奇之处在于只需要很小的内存,最大也不超过12k,类似集合的功能,能记录2^64的计数,从内存的角度来说,hyperloglog是首选
能用在网页的UV
传统方式,set保存用户的id,用户可能是uuid,这样可能占用巨大的内存 但是只是需要计数功能并不需要保存用户的id
③bitmaps(打卡)
统计用户信息,活跃与不活跃,登录未登录只有两种状态的数据,可以使用BitMaps
可以用作打卡功能实现,到达一定数目之后进行统计,判断预期数目与统计得出的数目是否达到预期
4.Redis事务与锁
(1)Redis事务
Redis事务实际上就是一组命令的集合,一次执行多条命令
Redis只保证单调命令的原子性,不保证事务的原子性
单条命令是原子性执行的,但事务不保证原子性,且没有回滚
-
编译型异常(代码有问题,命令有错) ,事务中的所有命令都不会被执行
-
运行时异常(比如1/0之类的),执行命令的时候其他命令是可以正常执行的,错误命令抛出异常
(2)锁
为了保证事务的原子性,可以进行加锁来实现
①悲观锁
写多读少的情况
认为所有事务都需要加锁,这样读写的效率特别低,但是很安全。在整个数据处理过程中,数据处于锁定状态
-
线程操作数据,对数据添加排他锁
-
传统数据库使用的几种加锁机制,都是在操作之前上锁
-
行锁 表锁 读锁 写锁
-
-
在Java中同步用synchronized关键字实现
-
悲观锁住要分共享锁和排他锁
-
共享锁 shared locks
读锁、S锁,多个事务对同一个数据可以共享一把锁,都能访问数据,只能读不能修改
-
排他锁 exclusive locks
写锁、X锁、不能与其他锁并存,一个事务获取了数据行的排他锁,其他事物就不能再获得该行的其他锁,获取排他锁的事物可以对数据行读取和修改
悲观并发控制 先取锁再访问 的保守策略,开销大,还增加死锁的概率,降低并发性,其他事务必须等待
-
②乐观锁
认为都不会出问问题,比较乐观,数据并发性较高
-
假设数据一般情况不会造成冲突,在数据提交更新时正式对数据的冲突与否进行检测,发现了冲突,发出错误信息,让用户决定如何处理,适用于读操作多的场景,可以提高程序的吞吐量
-
为了避免数据库的幻读,业务处理时间过长,乐观锁不会刻意使用数据库本身的锁机制,而是一句数据本身来保证数据的正确性
③watch监视
redis实现锁的一种方式
-
当有多个线程在操控redis的时候
-
被watch监视的key值如果发生改变,正在进行的事务将会失败
-
每次加锁后都要进行解锁,再加锁去重新获取最新的值
5.Redis持久化机制
(1)为什么需要持久化这个机制?
redis是基于内存的数据库,如果不将内存中的数据库状态保存到磁盘中,那么服务器进程退出,服务器中的数据库也会消失,所以需要持久化
Redis是通过RDB和AOF进行持久化机制
(2)RDB (Redis DataBase)
①基本工作方式
注意:rdb保存的就是数据库的数据
将当前进程的数据生成快照snapshot保存到硬盘的过程,数据保存到后缀为rdb的文件,rdb保存的是dump.rdb
②触发机制
-
手动:save和bySave命令
save:阻塞当前redis服务器的进程,直到rdb过程完成为止
bgSave:执行fork操作传建子线程,执行持久化的操作由子线程负责
-
自动触发
③优缺点
-
优点:
备份的文件是某个时间节点上的数据快照,非常适合备份,全量复制(恢复速度远远快于AOF)
-
缺点:
不能实时持久化,可能会出现数据丢失的情况,同时命令属于重量级的操作(子线程会创建一定的内存空间)
(3)AOF (append only file)
①基本工作方式
注意这里是记录命令
独立日志的方式记录每次写命令,重启的时候再重新执行AOF文件中的命令来恢复文件
命令写入append--------->文件同步sync------------>文件重写rewrite----------->重启加载load
命令写入:将命令追加到缓冲区
文件同步:AOF缓冲区向硬盘进行同步的操作
-
always 同步持久化 一更新就同步 性能差但是数据完整性好
-
everysec 异步
-
no 最后同步
文件重写:随着时间推迟,AOF文件越来越大,当文件大小超过一个阈值的时候,启动AOF文件压缩
-
fork分支出子进程
-
根据内存中的数据子进程创建临时aof文件
-
父进程执行的命令存放在缓存中,并且写入原aof文件
-
子进程完成新aof文件通知父进程
-
父进程将缓存中的命令写入临时文件
-
父进程用临时文件替换旧aof文件并重命名
-
后面的命令都追加到新的aof文件中
②优缺点
优点: 更高的数据安全性(相比于rdb而言比较不易丢失数据),比较清晰的日志文件、对日志的写入是append追加不会改变原有的内容
缺点:文件大小AOF文件>RDB文件 (AOF文件主要是一些命令比较多),同时AOF运行效率慢慢RDB
6.Redis发布订阅
①基本概念
实际上就是一个订阅者subscribe和发布者punish的关系,发布者发布消息,订阅这个频道的订阅者就可以收到消息(发布者发送的)
Redis通过publish、subscribe、psubscribe等命令实现发布和订阅的功能
订阅:subscribe订阅某个频道后,redis-server维护了一个字典,字典的键就是一个一个频道,字典的值就是一个链表(保存所有订阅这个频道的客户端),subscribe的关键就是将客户端添加到指定channel的订阅链表中
发送:publish,redis-sever会使用给定的channel作为键,遍历这个channel的订阅链表发布给订阅者
②使用场景
-
实时消息系统
-
订阅关注等
7.Redis分布式策略
(1)主从复制
①基本概念
有一个master服务器(以写为主),多个slave服务器(以读为主)
主节点(masterleader):一个Master有多个slave,将一台redis服务器数据,复制到其他的redis服务器(从节点(slave、follower)),
数据是单向的,只能从主节点到从节点,Master以写为主,Slave以读为主
默认情况下,每台redis服务器都是主节点,一个Master可以有多少Slave或没有从节点,一个从节点只能有一个主节点
一般的网站一次写入,无次数读取,读取的压力比写入的压力大很多,读多写少 ,主从复制,读写分离,80%的情况都在进行读操作,起码一主二从(首先主从复制至少就需要一主一从,但是哨兵模式出现后,当一台服务器宕机后,需要再选择一台服务器实现从服务器)
为什么不能用单台redis:
-
从结构上讲,单个redis服务器会发生单点故障,一台服务器需要处理所有请求,压力大,服务器容易出现宕机的危险导致所有服务不可用
-
从容量上讲,单个redis服务器内存容量有限,并且不能完全使用全部的内存,单台redis的最大内存不应该超过20g压力过大
②主从复制的原理
主机可以写和读,从机只能读不能写。
两种复制:
-
全量复制:master将所有消息都sync给slave,slave在接受到数据库文件数据后,将其存盘并加载到内存当中
-
增量复制:master继续将新的所有收集到的修改命令传给slave
工作机制:
③主从复制优缺点
优点:
-
数据冗余
实现了数据的热备份,是持久化之外的一种数据冗余方式
-
故障恢复
主节点出现问题,从节点可以提供服务,实现快速的故障恢复,实际上是一种服务的冗余
-
负载均衡
在主从复制的基础上,配合读写分离,主节点提供写服务,从节点提供读服务,写redis数据时连接主节点,读redis数据连接从节点,分担服务器负载,尤其在写少读多的场景下通过,多个从节点分担负载,可以提高redis性能
-
高可用(集群)基石
哨兵、集群,能够实施的基础,主从复制时高可用的基础
缺点:
-
主服务器异常时需要手动主从切换
当master宕机之后,slave就没有了master,就会一直等待,当master重连后,此时master仍然是slave的主机(slave会自动重连,这个过程不需要手动操作),而那个等待的过程十分耗时同时也存在风险,所以需要切换主机,但是切换主机需要手动操作
(2)哨兵模式
①基本概念
引入原因:
在主从复制模式下,当主机发生故障后要手动切换从机,这样十分不友好,可以引入哨兵进行监控每台redis服务器,当主机发生故障时,自动切换主机master
当哨兵模式检测到master宕机,会自动将slave切换成master,通过发布订阅模式通知其他的从服务器,修改配置文件,让他们切换主机
分类:
-
单哨兵模式:只有一个哨兵进行监控
-
多哨兵模式:有多个哨兵进行监控,哨兵之间也会互相监控
②工作机制
主要介绍一下多哨兵模式的工作机制
哨兵的连接和监控机制:
-
Step1:哨兵与master首先建立连接
-
Step2:哨兵定期向master和slave发送INFO命令,主要是获取当前数据库的相关信息实现新节点自动发现(这里只用配置master就能发现slave消息)
-
Step3:向同样监控数据库的哨兵共享自己的信息
-
Step4:ping命令监控数据库和节点是否停止服务
重新选举master机制:
-
确定master下线,就重新选举:
-
主观下线:哨兵进行ping命令时,redis服务器没有回复
-
客观下线:询问其他哨兵,认为master下线的哨兵达到指定数量
-
-
选择slave中priority最高的为master
-
如果priority一样就选择复制偏移量最大
-
如果上述两都一致就选择id最小的
③哨兵模式的优缺点
优点:
-
当master发生故障时,不需要手动切换master
-
基于集群,基于主从复制,所有的主从配置的优点,它全有
-
主从可以切换,故障可以切换,系统的可用性提高
-
哨兵模式就是主从模式的升级,手动到自动,更加健壮
缺点:
-
哨兵选举时不能对外服务
-
比较复制
(3)redis集群cluster
8.Redis缓存穿透、击穿、雪崩
①缓存穿透
概念:
缓存穿透主要是指没有使用缓存,直接绕过缓存访问数据库导致服务器宕机。
即用户查询数据redis数据库中没有访问持久层数据库,(比如没有那条数据时缓存就可能出现一直不命中的情况,只能访问持久层数据库)用户很多的时候缓存都没有命中,都是请求持久层数据库,给数据库很大压力
解决缓存穿透:
-
布隆过滤器:
是一种数据结构。在缓存区前加上一层过滤器,对查询的参数进行检验,对于不符合的参数直接丢失
-
缓存空对象
当第一次访问持久层数据库不命中时,将返回的空对象存储起来,同时设置一个过期时间,之后再访问这个数据就从缓存中获取,保护持久层数据源
但是这种空对象存在一定的问题:
-
存储空的key也需要空间
-
对空值设置了过期时间,还会存在缓存层和存储层的数据有一段时间窗口不一致,对于需要保持一致性的业务会有影响
②缓存击穿
概念:
缓存击穿主要是由于访问一个热点key信息导致缓存崩掉(一个点)。
这个key扛着高并发,当这个key失效时,持续大并发会导致直接穿破缓存直接请求数据库,导致当时数据库访问量过大。
解决:
-
设置热点数据不过期
-
这个方法理论上可行,但是现实上不可行,也不知道哪个key是热点数据同时一直缓存也会浪费空间
-
-
加互斥锁
分布式锁:使用分布式锁,保证对于每个key同时只有一个线程查询后端服务,其他线程没有获得分布式锁的权限,只需要等待即可,这种方式将高并发的压力转移到了分布式锁,因此对分布式锁的考验很大
③缓存雪崩
概念:
缓存雪崩主要是
在某一个时间段,缓存集中过期失效,redis宕机(很多点)
产生雪崩的原因之一,设置缓存的存活时间较短,大并发访问时刚好都过期,直接访问了数据库,对数据库而言,会产生周期性压力波峰,暴增时数据库可能会宕机
解决方案:
-
增加集群中服务器数量
异地多活
-
限流降级
缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量,对某个key只允许一个线程查询数据和写缓存,其他线程等待
-
数据预热
正式部署之前,把可能的数据提前访问一遍,可能大量访问的数据就会加载到缓存中,加载不同的key,设置不同的过期时间,让缓存时间尽量均匀