什么是Nosql
nosql=not only sql(不仅仅是sql)
泛指非关系型数据库,随着web2.0互联网的诞生,传统关系型数据库解决不了,尤其是大规模的高并发的问题。
Redis(Remote Dictionary Server )
即远程字典服务,是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。Redis 是一个高性能的key-value数据库。
数据类型
- 字符串(string): 可以用来做最简单的数据缓存,可以缓存某个简单的字符串,也可以缓存某个json格式的字符串,Redis分布式锁的实现就利用了这种数据结构,还包括可以实现计数器、Session共享、分布式ID。
- 哈希表(hash): 可以用来存储一些key-value键值对,更适合用来存储对象。
- 列表(list): Redis的列表通过命令的组合,即可以当作栈,又可以当作队列来使用,可以用来缓存类似微信公众号,微博等消息流数据。
- 集合(set): 和列表类似,也可以存储多个元素,但是不能重复,集合可以进行交集,并集和差集操作,从而可以实现类似:我和某人共同关注的人、朋友圈点赞等。
- 有序集合(zset): 集合是无序的,有序集合可以设置顺序,可以用来实现排行榜功能。
事物
-
概念:
是一个单独隔离操作:事务中的所有命令都会序列化,按顺序的执行,事物在执行过程中,不会被其他客户发送来的命令请求所打断。 -
作用:
串联多个命令,防止别的命令插队。 -
命令:
从输入Multi命令开始,输入的命令都会依次进入命令队列中,但不会执行,直到输入Exec后,Redis会将之前的命令队列中的命令依次执行。组队的过程中可以通过discard放弃执行。- Multi:组队 - Exec:执行 - discard:放弃执行
事物冲突
-
例子:
双十一小明有10000元,但是有三个女朋友abc- a要求买一个8000的口红 - b要求买一个5000的包包 - c要求买一个1000的手链
-
解决方法:
- 悲观锁 - 乐观锁
- watch监视:如果命令在执行前被改的,那么事物会被打断。 watch(监视) exec(执行) discard(放弃)
三大特征
- 单独的隔离操作
- 事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断
- 没有隔离级别的概念
- 队列中的命令没有提交之前都不会实际被执行,因为事务提交前任何指令都不会被实际执行
- 不保证原子性
- 事务中如果有一条命令执行失败,其后的命令仍然会被执行,没有回滚
持久化
Redis对数据的操作都是基于内存的,当遇到了进程退出、服务器宕机等意外情况,如果没有持久化机制,那么Redis中的数据将会丢失无法恢复。有了持久化机制,Redis在下次重启时可以利用之前持久化的文件进行数据恢复。
RDB(Redis DataBase)
在指定的时间间隔内将内存中的数据集快照写入磁盘。
- 手动触发
-
save
执行save命令会手动触发RDB持久化,但是save命令会阻塞Redis服务,直到RDB持久化完成。当Redis服务储存大量数据时,会造成较长时间的阻塞,不建议使用。 -
bgsave
执行bgsave命令也会手动触发RDB持久化,和save命令不同是:Redis服务一般不会阻塞。Redis进程会执行fork操作创建子进程,RDB持久化由子进程负责,不会阻塞Redis服务进程。Redis服务的阻塞只发生在fork阶段,一般情况时间很短
1. undefined.执行bgsave命令,Redis进程先判断当前是否存在正在执行的RDB或AOF子线程,如果存在就是直接结束。 2. undefined.Redis进程执行fork操作创建子线程,在fork操作的过程中Redis进程会被阻塞。 3. undefined.Redis进程fork完成后,bgsave命令就结束了,自此Redis进程不会被阻塞,可以响应其他命令。 4. undefined.子进程根据Redis进程的内存生成快照文件,并替换原有的RDB文件。 5. undefined.子进程通过信号量通知Redis进程已完成。
-
自动触发
与RDB不同,AOF持久化是通过记录Redis的所有写操作命令到一个文件中,并在Redis重启时,通过回放这些命令来重建数据。AOF持久化可以配置不同的同步策略,如每秒同步、每写操作同步或根据需要同步。默认情况下,Redis会开启AOF持久化,并将同步策略设置为每秒同步。
AOF(Append Only File)
应用问题
-
缓存穿透
一直访问数据库,缓存没有起到作用
原因:
- 应用服务器突然增加
- redis命中率降低
- 频繁操作数据库
现象:
- redis查询不到数据库
- 出现很多不正常的url访问
问题:
一个一定不存在缓存及查询不到的数据,由于缓存是不命中时被动写的,并且出于容错考虑,如果从存储层查不到数据则不写入缓存,
这将导致这个不存在的数据每次请求都要到存储层去查询,失去了缓存的意义。
解决方案:
-
对空值缓存
如果一个查询返回的数据为空,我们仍然把这个空结果( null )进行缓存,设置空结果的过期时间会很短,最长不超过五分钟
-
设置可访问的名单(白名单)
使用bitmaps(位图)类型定义一个可以访问的名单,名单id作为bitmaps的偏移量,每次访问和 bitmap里面的id进行比较,如果访问id不在bitmaps里面,进行拦截,不允许访问。
-
采用布隆过滤器
(布隆过滤器(Bloom Filter )是1970年由布隆提出的。它实际上是一个很长的二进制向量(位图)和一系列随机映射函数(哈希函数)。
布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都远远超过一般的算法,缺点是有一定的误识别率和删除困难。
将所有可能存在的数据哈希到一个足够大的bitmaps中,一个一定不存在的数据会被这个bitmaps拦截掉,从而避免了对底层存储系统的查询压力。
-
进行实时监控
当发现 Redis 的命中率开始急速降低,需要排查访问对象和访问的数据,和运维人员配合,可以设置黑名单限制服务
-
缓存击穿
给某一个key设置了过期时间,当key过期的时候,恰好这时间点对这个key有大量的并发请求过来缓存击穿问题也叫热点key问题,就是一个被高并发访问并且缓存重建业务较复杂的key突然失效了,无数的请求访问会在瞬间给数据库带来巨大的冲击。
现象:
- 数据库访问压力瞬间增加
- redis里面没有出现大量key过期
- redis正常运行
解决方案:
-
互斥锁
-
监控数据,适时调整
类似于新闻、某博等软件都需要对热点数据进行预先设置在redis中
-
提前对热点数据进行设置
监控哪些数据是热门数据,实时的调整key的过期时长
-
缓存雪崩
在某个时间段,缓存中的大部分数据过期或失效,导致大量请求直接访问数据库,造成数据库压力剧增,甚至导致数据库崩溃。
现象:
- 大量Redis缓存数据同时过期,导致所有的发送到Redis请求都无法命中数据,只能到数据库中进行查询。
- Redis服务器宕机,所有请求都无法经Redis来处理,只能转向数据库查询数据。
解决方案:
1.事发前
- 设置随机过期时间
为每个缓存设置一个随机的过期时间,避免大量缓存在同一时间失效,减少缓存雪崩的风险。 - 数据预热
在系统低峰期将数据预先加载到缓存中,避免在高峰期因为大量请求导致缓存雪崩问题的发生。 - 缓存高可用
使用多级缓存架构,将缓存服务进行集群化,避免单点故障,确保缓存服务的高可用性。
a. 第一级缓存(本地缓存):第一级缓存通常是位于应用程序内部的本地缓存,例如使用内存数据结构进行存储(如HashMap、ConcurrentHashMap等)。本地缓存速度非常快,对于短时间内频繁访问的数据能够提供快速响应。但由于是应用程序内部的缓存,其生命周期通常与应用程序的生命周期相同,不具备高可用性。
b. 第二级缓存(分布式缓存):第二级缓存是在多台服务器节点上运行的分布式缓存服务,例如Redis、Memcached等。分布式缓存能够存储大量数据,并提供高性能的读写操作。通过将缓存数据分片存储在不同的节点上,可以实现负载均衡和数据冗余,以提高系统的可用性和可靠性。
c. 第三级缓存(持久化存储):第三级缓存可以是持久化存储,如关系型数据库(如MySQL)或分布式文件系统(如Hadoop HDFS)。第三级缓存主要用于存储不常访问的数据,数据从第二级缓存中淘汰后仍然可以从第三级缓存中恢复。这样可以在第二级缓存故障或不命中时,仍然能够从第三级缓存中获取数据。
2.事发中
设置本地缓存(ehcache)+限流(hystrix)&降级,尽量避免数据库被干掉(起码能保证我们的服务还是能正常工作的)
3.事发后
redis持久化,重启后自动从磁盘上加载数据,快速恢复缓存数据。