一、NoSQL
许多Web应用都将数据保存到关系型数据库( RDBMS)中,应用服务器从中读取数据并在浏览器中显示。但随着数据量的增大、访问的集中,就会出现RDBMS的负担加重、数据库响应恶化、 网站显示延迟等重大影响。Memcached/redis是高性能的分布式内存缓存服务器,通过缓存数据库查询结果,减少关系型数据库访问次数,以提高动态Web等应用的速度、 提高可扩展性。
1.NoSQL的优缺点
优点:- 高可扩展性- 分布式计算- 低成本- 架构的灵活性- 没有复杂的关系缺点:- 没有标准化- 有限的查询功能(到目前为止)- 最终一致是不直观的程序
2.关系型数据库与非关系型数据库的区别
1.首先了解一下 什么是关系型数据库?
关系型数据库最典型的数据结构是表,由二维表及其之间的联系所组成的一个数据组织。
id name age
优点:
1、易于维护:都是使用表结构,格式一致;
2、使用方便:SQL语言通用,可用于复杂查询;
3、复杂操作:支持SQL,可用于一个表以及多个表之间非常复杂的查询。
缺点:
1、读写性能比较差,尤其是海量数据的高效率读写;
2、固定的表结构,灵活度稍欠;
3、高并发读写需求,传统关系型数据库来说,硬盘I/O是一个很大的瓶颈;
二 非关系型数据库
什么非关系型数据库呢?
非关系型数据是一种数据结构化存储方法的集合,可以是文档或者键值对等
优点:
1、格式灵活:存储数据的格式可以是key,value形式、文档形式、图片形式等等,使用灵活,应用场景广泛,而关系型数据库则只支持基础类型。
2、速度快:nosql可以使用硬盘或者随机存储器作为载体,而关系型数据库只能使用硬盘;
3、高扩展性;
4、成本低:nosql数据库部署简单,基本都是开源软件。
缺点:
1、不提供sql支持,学习和使用成本较高;
2、无事务处理;
3、数据结构相对复杂,复杂查询方面稍欠。
二、Redis
1.Redis特点
1.丰富的数据结构 -----String,list,set,hash等数据结构的存储
2.支持持久化
3.支持事务 ---------------事务是指“一个完整的动作,要么全部执行,要么什么也没有做”。
4.支持主从
2.Redis和Memcache的区别
redis和memcache比较
1).Redis不仅仅支持简单的k/v类型的数据,同时还提供了list,set,zset,hash等数据结构的存储
2).Redis支持master-slave(主-从)模式应用
3).Redis支持数据的持久化
3.Redis数据持久化
(1)RDB和AOF的区别
一、redis提供了两种持久化的方式,分别是RDB(Redis DataBase)和AOF(Append Only File)。
======================================================================================
RDB(Redis DataBase):是在不同的时间点,将redis存储的数据生成快照并存储到磁盘等介质上;
特点:
1.周期性
2.不影响数据写入 #RDB会启动子进程,备份所有数据。当前进程,继续提供数据的读写。当备份完成,才替换老的备份文件。
3.高效 #一次性还原所有数据
4.完整性较差 #故障点到上一次备份,之间的数据无法恢复。
======================================================================================
AOF(Append Only File)则是换了一个角度来实现持久化,那就是将redis执行过的所有写指令记录下来,在下次redis重新启动时,只要把这些写指令从前到后再重复执行一遍,就可以实现数据恢复了。
特点:
1.实时性
2.完整性较好
3.体积大 #记录数据的指令,删除数据的指令都会被记录下来。
=======================================================================================
二、RDB和AOF两种方式也可以同时使用,在这种情况下,如果redis重启的话,则会优先采用AOF方式来进行数据恢复,这是因为AOF方式的数据恢复完整度更高。
三、如何选择方式?
缓存:不用开启任何持久方式
双开:因RDB数据不实时,但同时使用两者时服务器只会找AOF文件,所以RDB留作以防万一的手段。
官方的建议是两个同时使用。这样可以提供更可靠的持久化方案。
写入速度快 ------------AOF
写入速度慢 ------------RDB
(2)RDB操作
1、RDB默认开启:
[root@redis-master redis]# vim redis.conf
#dbfilename:持久化数据存储在本地的文件
dbfilename dump.rdb
#dir:持久化数据存储在本地的路径
dir /data/application/redis/data
##snapshot触发的时机,save <seconds> <changes>
##如下为900秒后,至少有一个变更操作,才会snapshot
##对于此值的设置,需要谨慎,评估系统的变更操作密集程度
##可以通过save “”来关闭snapshot功能
#save时间,以下分别表示更改了1个key时间隔900s进行持久化存储;更改了10个key300s进行存储;更改10000个key60s进行存储。
save 900 1
save 300 10
save 60 10000
##yes代表当使用bgsave命令持久化出错时候停止写RDB快照文件,no表明忽略错误继续写文件,“错误”可能因为磁盘已满/磁盘故障/OS级别异常等
stop-writes-on-bgsave-error yes
##是否启用rdb文件压缩,默认为“yes”,压缩往往意味着“额外的cpu消耗”,同时也意味着较短的网络传输时间
rdbcompression yes
注意:每次快照持久化都是将内存数据完整写入到磁盘一次,如果数据量大的话,而且写操作比较多,必然会引起大量的磁盘io操作,可能会严重影响性能。
如果想禁用RDB持久化的策略,只要不设置任何save指令就可以
2.使用rdb恢复
[root@redis-master redis]# vim redis.conf
dbfilename 备份文件名
dir 备份文件存储路径
#直接启动就行
(3)AOF操作
1、AOF默认关闭--开启
[root@redis-master redis]# vim redis.conf
appendonly yes #开启
appendfilename appendonly.aof #指定文件名
appendfsync everysec #同步策略,默认everysec
always #每次有数据修改发生时都会写入AOF文件
everysec #每秒钟同步一次,该策略为AOF的缺省策略/默认策略
no #从不同步。高效但是数据不会被持久化
三、Redis集群
1.Redis主从
像MySQL一样,redis是支持主从同步的,而且也支持一主多从以及多级从结构。
主从结构,一是为了纯粹的冗余备份,二是为了提升读性能,比如很消耗性能的操作就可以由从服务器来承担。
redis的主从同步是异步进行的,这意味着主从同步不会影响主逻辑,也不会降低redis的处理性能。
主从架构中,可以考虑关闭主服务器的数据持久化功能,只让从服务器进行持久化,这样可以提高主服务器的处理性能。
(1)主从原理
主从 – 同步原理
从服务器会向主服务器发出SYNC指令,当主服务器接到此命令后,就会调用BGSAVE指令来创建一个子进程专门进行数据持久化工作,也就是将主服务器的数据写入RDB文件中。在数据持久化期间,主服务器将执行的写指令都缓存在内存中。
在BGSAVE指令执行完成后,主服务器会将持久化好的RDB文件发送给从服务器,从服务器接到此文件后会将其存储到磁盘上,然后再将其读取到内存中。这个动作完成后,主服务器会将这段时间缓存的写指令再以redis协议的格式发送给从服务器。
另外,要说的一点是,即使有多个从服务器同时发来SYNC指令,主服务器也只会执行一次BGSAVE,然后把持久化好的RDB文件发给多个从服务器。
而在2.8版本之后,redis支持了效率更高的增量同步策略,这大大降低了连接断开的恢复成本。主服务器会在内存中维护一个缓冲区,缓冲区中存储着将要发给从服务器的内容。从服务器在与主服务器出现网络瞬断之后,从服务器会尝试再次与主服务器连接,一旦连接成功,主服务器就会向从服务器发送增量内容。
增量同步功能,需要服务器端支持全新的PSYNC指令。这个指令,只有在redis-2.8之后才具有。
BGSAVE指令:
在后台异步(Asynchronously)保存当前数据库的数据到磁盘。
BGSAVE 命令执行之后立即返回 OK ,然后 Redis fork 出一个新子进程,原来的 Redis 进程(父进程)继续处理客户端请求,而子进程则负责将数据保存到磁盘,然后退出。
(2)主从操作
主从节点关闭加密保护 rotected-mode no 如果网络环境不安全,可以加密,设置密码用 requirepass
从节点 slaveof 主节点地址 6379
2.Redis哨兵模式
Sentinel(哨兵)是用于监控Redis集群中Master状态的工具
(1)工作模式
1):每个Sentinel以每秒钟一次的频率向它所知的Master,Slave以及其他 Sentinel 实例发送一个 PING 命令
2):如果一个实例(instance)距离最后一次有效回复 PING 命令的时间超过 down-after-milliseconds 选项所指定的值, 则这个实例会被 Sentinel 标记为主观下线。
3):如果一个Master被标记为主观下线,则正在监视这个Master的所有 Sentinel 要以每秒一次的频率确认Master的确进入了主观下线状态。
4):当有足够数量的 Sentinel(大于等于配置文件指定的值)在指定的时间范围内确认Master的确进入了主观下线状态, 则Master会被标记为客观下线
(2)哨兵操作
1.修改配置文件
[root@redis-master redis]# vim sentinel.conf
sentinel monitor mymaster 192.168.43.100 6379 2 #当集群中有2个sentinel认为master死了时,才能真正认为该master已经不可用了。 (slave上面写的是master的ip,master写自己ip)
sentinel down-after-milliseconds mymaster 3000 #单位毫秒
sentinel failover-timeout mymaster 10000 #若sentinel在该配置值内未能完成failover(故障转移)操作(即故障时master/slave自动切换),则认为本次failover失败。
protected-mode no #关闭加密模式--新添加到sentinel配置文件中
2.每台机器启动哨兵服务:
[root@redis-master redis]# ./src/redis-sentinel sentinel.conf
注意:在生产环境下将哨兵模式启动放到后台执行: nohup ./src/redis-sentinel sentinel.conf &
3.Redis Cluster去中心化集群
(1)为什么要用cluster集群
1.首先Redis单实例主要有单点,容量有限,流量压力上限的问题。
Redis单点故障,可以通过主从复制replication,和自动故障转移sentinel哨兵机制。但Redis单Master实例提供读写服务,仍然有容量和压力问题,因此需要数据分区,构建多个Master实例同时提供读写服务(不仅限于从replica节点提供读服务)。
2.并发问题
redis官方声称可以达到 10万/s,每秒执行10万条命令
假如业务需要每秒100万的命令执行呢?
解决方案如下
1.正确的应该是考虑分布式,加机器,把数据分到不同的位置,分摊集中式的压力,一堆机器做一件事.还需要一定的机制保证数据分区,并且数据在各个主Master节点间不能混乱,当然最好还能支持在线数据热迁移的特性。
(2)cluster集群原理
Redis 集群使用数据分片(sharding)来实现:Redis 集群中内置了 16384 个哈希槽,当需要在 Redis 集群中放置一个 key-value 时,redis 先对 key 使用 crc16 算法算出一个结果,然后把结果对 16384 求余数(集群使用公式 CRC16(key) % 16384),这样每个key 都会对应一个编号在 0-16383 之间的哈希槽,那么redis就会把这个key 分配到对应范围的节点上了。同样,当连接三个节点任何一个节点想获取这个key时,也会这样的算法,然后内部跳转到存放这个key节点上获取数据。
这种将哈希槽分布到不同节点的做法使得用户可以很容易地向集群中添加或者删除节点。 比如说:
- 如果用户将新节点 D 添加到集群中, 那么集群只需要将节点 A 、B 、 C 中的某些槽移动到节点 D 就可以了。
- 如果用户要从集群中移除节点 A , 那么集群只需要将节点 A 中的所有哈希槽移动到节点 B 和节点 C , 然后再移除空白(不包含任何哈希槽)的节点 A 就可以了。
因为将一个哈希槽从一个节点移动到另一个节点不会造成节点阻塞, 所以无论是添加新节点还是移除已存在节点, 又或者改变某个节点包含的哈希槽数量, 都不会造成集群下线。
(3)Redis Cluster主从模式
redis cluster 为了保证数据的高可用性,加入了主从模式,一个主节点对应一个或多个从节点,主节点提供数据存取,从节点则是从主节点拉取数据备份,当这个主节点挂掉后,就会有这个从节点选取一个来充当主节点,从而保证集群不会挂掉.
1.主从切换机制
选举过程是集群中所有master参与,如果半数以上master节点与故障节点通信超过(cluster-node-timeout),认为该节点故障,自动触发故障转移操作. #故障节点对应的从节点自动升级为主节点
2.什么时候整个集群就不能用了?
如果集群任意一个主节点挂掉,且当前主节点没有从节点,则集群将无法继续,因为我们不再有办法为这个节点承担范围内的哈希槽提供服务。但是,如果这个主节点和所对应的从节点同时失败,则Redis Cluster无法继续运行。
(4)操作
- 搭建集群 ```shell 修改集群每个redis配置文件。(主要是端口、ip、pid文件,三台机器相同操作),修改如下: [root@redis-cluster1 cluster]# cd 7000/ [root@redis-cluster1 7000]# vim redis.conf #修改如下 bind 192.168.116.172 #每个实例的配置文件修改为对应节点的ip地址 port 7000 #监听端口,运行多个实例时,需要指定规划的每个实例不同的端口号 daemonize yes #redis后台运行 pidfile /var/run/redis_7000.pid #pid文件,运行多个实例时,需要指定不同的pid文件 logfile /var/log/redis_7000.log #日志文件位置,运行多实例时,需要将文件修改的不同。 dir /data/redis/data #存放数据的目录 appendonly yes #开启AOF持久化,redis会把所接收到的每一次写操作请求都追加到appendonly.aof文件中,当redis重新启动时,会从该文件恢复出之前的状态。 appendfilename “appendonly.aof” #AOF文件名称 appendfsync everysec #表示对写操作进行累积,每秒同步一次 以下为打开注释并修改 cluster-enabled yes #启用集群 cluster-config-file nodes-7000.conf #集群配置文件,由redis自动更新,不需要手动配置,运行多实例时请注修改为对应端口 cluster-node-timeout 5000 #单位毫秒。集群节点超时时间,即集群中主从节点断开连接时间阈值,超过该值则认为主节点不可以,从节点将有可能转为master cluster-replica-validity-factor 10 #在进行故障转移的时候全部slave都会请求申请为master,但是有些slave可能与master断开连接一段时间了导致数据过于陈旧,不应该被提升为master。该参数就是用来判断slave节点与master断线的时间是否过长。(计算方法为:cluster-node-timeout cluster-replica-validity-factor,此处为:5000 10 毫秒) cluster-migration-barrier 1 #一个主机将保持连接的最小数量的从机,以便另一个从机迁移到不再被任何从机覆盖的主机 cluster-require-full-coverage yes #集群中的所有slot(16384个)全部覆盖,才能提供服务
注:
所有节点配置文件全部修改切记需要修改的ip、端口、pid文件…避免冲突。确保所有机器都修改。
```shell
#创建集群:在其中一个节点操作就可以
[root@redis-cluster1 src]# ./redis-cli --cluster create --cluster-replicas 1 192.168.116.172:7000 192.168.116.172:7001 192.168.116.173:7002 192.168.116.173:7003 192.168.116.174:7004 192.168.116.174:7005
#查看集群信息
192.168.116.173:7002> cluster info
#添加结点
[root@redis-cluster4 src]# ./redis-cli --cluster add-node 192.168.116.175:7006 192.168.116.172:7000
#查看集群节点信息
192.168.116.172:7000> CLUSTER nodes
#查看每个节点槽的数量
[root@redis-cluster1 src]# ./redis-cli --cluster info 192.168.116.172:7000
#将槽迁移到其他节点:
ip+port:要移除的节点
cluster-from:移除节点的id
cluster-to:接受槽主节点的id,需要将4096平均移动到不同的主节点,需要写不同接受槽的主节点id
cluster-slots:移除槽的数量
[root@redis-cluster1 src]# ./redis-cli --cluster reshard 192.168.116.175:7006 --cluster-from 308320db4284c9b203aff1d3d9a145616856f681 --cluster-to e0370608cd33ddf5bb6de48b5627799e181de3b6 --cluster-slots 1365 --cluster-yes
#平衡各个主节点的槽:
[root@redis-cluster1 src]# ./redis-cli --cluster rebalance --cluster-threshold 1 192.168.116.172:7000
四、面试问题
1.如何解决Redis,mysql双写一致性?
1.最经典的缓存+数据库读写的模式:
读的时候,先读缓存,缓存没有的话,就读数据库,然后取出数据后放入缓存,同时返回响应。
更新的时候,先更新数据库,然后再删除缓存。
2.给缓存设置过期时间,这种方案下,可以对存入缓存的数据设置过期时间,所有的写操作以数据库为准,也就是说如果数据库写成功,缓存更新失败,那么只要到达过期时间,则后面的读请求自然会从数据库中读取新值然后回填缓存。
2.缓存雪崩
数据未加载到缓存中,或者缓存同一时间大面积的失效,从而导致所有请求都去查数据库,导致数据库CPU和内存负载过高,甚至宕机。
产生雪崩的简单过程:
1、redis集群大面积故障
2、缓存失效,但依然大量请求访问缓存服务redis
3、redis大量失效后,大量请求转向到mysql数据库,mysql的调用量暴增,很快就扛不住了,甚至直接宕机
4、由于大量的应用服务依赖mysql和redis的服务,这个时候很快会演变成各服务器集群的雪崩,最后网站彻底崩溃。
#解决:
1.缓存的高可用性
缓存层设计成高可用,防止缓存大面积故障。即使个别节点、个别机器、甚至是机房宕掉,依然可以提供服务,例如 Redis Sentinel 和 Redis Cluster 都实现了高可用。
2.缓存降级
可以利用ehcache等本地缓存(暂时支持),主要还是对源服务访问进行限流、资源隔离(熔断)、降级等。
当访问量剧增、服务出现问题仍然需要保证服务还是可用的。系统可以根据一些关键数据进行自动降级,也可以配置开关实现人工降级,这里会涉及到运维的配合。
降级的最终目的是保证核心服务可用,即使是有损的。
在进行降级之前要对系统进行梳理,比如:哪些业务是核心(必须保证),哪些业务可以容许暂时不提供服务(利用静态页面替换)等,以及配合服务器核心指标,来后设置整体。
3.Redis备份和快速预热
1)Redis数据备份和恢复
2)快速缓存预热
4.提前演练
最后,建议还是在项目上线前,演练缓存层宕掉后,应用以及后端的负载情况以及可能出现的问题,对高可用提前预演,提前发现问题。
3.缓存穿透
缓存穿透是指查询一个一不存在的数据。例如:从缓存redis没有命中,需要从mysql数据库查询,查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到数据库去查询,造成缓存穿透。
解决:
如果查询数据库也为空,直接设置一个默认值存放到缓存,这样第二次到缓冲中获取就有值了,而不会继续访问数据库。设置一个过期时间或者当有值的时候将缓存中的值替换掉即可。
4.缓存并发
这里的并发指的是多个redis的client同时set key引起的并发问题。其实redis自身就是单线程操作,多个client并发操作,按照先到先执行的原则,先到的先执行,其余的阻塞。
5.缓存预热
缓存预热就是系统上线后,将相关的缓存数据直接加载到缓存系统。
这样就可以避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题!用户直接查询事先被预热的缓存数据!
解决:
1、直接写个缓存刷新页面,上线时手工操作下;
2、数据量不大,可以在项目启动的时候自动进行加载;
目的就是在系统上线前,将数据加载到缓存中。
