Redis
1.NoSQL
泛指非关系型数据库
特点:
1.非关系型数据库,不依赖业务逻辑数据库存储,以简单key-value存储。因此大大的增加了数据库的扩展能力
2.不遵循SQL标准
3.不支持ACID
适用于:高并发读写、海量数据读写、数据可扩展
不适用于:事务存储、复杂数据库
优点:
1.缓存数据库,完全在内存中,速度快,数据结构简单
2.减少io操作,数据库和表拆分,虽然破坏业务逻辑,即外加一个缓存数据库,提高数据库速度,也可以用专门的存储方式,以及针对不同的数据结构存储
2.Redis
默认6379端口号,支持多数据类型,持久化,单线程+多路io口复用
key值键位:
string字符串:
一个key对应一个value、二进制安全的,即可包含任何数据、value最多可以是512m
list列表:
set集合:字典,哈希表,自动排重且为无序的
hash哈希:键值对集合,特别适合用于存储对象类型
Zset有序集合:没有重复元素的字符串集合,按照相关的分数进行排名,排名从低到高,排名可重复
发布和订阅:
客户端可以订阅任意频道的消息,打开一个客户端进行订阅----subscribe channel1
打开另一个客户端进行发布----publish channel1 hello
Bitmaps:
1.合理使用操作位可以有效地提高内存使用率和开发使用率
2.本身是一个字符串,不是数据类型,数组的每个单元只能存放0和1,数组的下标在Bitmaps叫做偏移量
3.节省空间,一般存储活跃用户比较多
设置值:setbit key offset value
取值:getbit key offset
统计数值:bitcount key (start end)
bitop:复合操作,交并非异或,结果保存在destkey:bitop and(or/not/xor)destkey key
HyperLogLog:
1.统计网页中页面访问量
2.只会根据输入元素来计算基数,而不会储存输入元素本身,不能像集合那样,返回输入的各个元素
3.基数估计是在误差可接受的范围内,快速计算(不重复元素的结算)
添加指定的元素到hyperloglog中:pfadd key element
计算key的近似基数:pfcount key
一个或多个key合并后的结果存在另一个key:pfmerge destkey sourcekey sourcekey
Geographic:提供经纬度设置,查询范围,距离查询等
3.Jedis操作Redis
4.Spring Boot整合Redis
5.事务操作
- 单独的隔离操作
- 事务中的所有命令都会序列化
- 按顺序执行
- 执行的过程中,不会被其他命令请求所打断
Redis事务三大特性:- 单独的隔离操作(不会被打断)
- 没有隔离级别
- 不保证原子性
串联多个命令防止别的命令插队:主要的三个命令:multi、exec和discard
事务的错误处理:
1. 组队的时候失败,即执行的时候也是失败
没有给予赋值value
2. 组队的时候成功,执行的会出错
但个别指令如果出错,只有这个指令出错执行不了
组队的时候value的时候应该是一个值,而incr加不了
悲观锁与乐观锁:
* 悲观锁:不能同时进行多人,执行的时候先上锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁
* 乐观锁:通过版本号一致与否,即给数据加上版本,同步更新数据以及加上版本号。不会上锁,判断版本号,可以多人操作,类似生活中的抢票。每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量。Redis就是利用这种check-and-set机制实现事务的
6.持久化
具体Redis 提供了2个不同形式的持久化方式:RDB(Redis DataBase) 、AOF(Append Of File)
RDB:在指定的时间间隔内将内存中的数据集快照写入磁盘
具体RDB的备份:
因为是临时文件,如果redis关闭之后,rdb的东西就会不见
所以通过mv 更改其名字之后mv dump.rdb d.rdb
在启动之前 又更改回来名字即可,mv d.rdb dump.rdb`(启动Redis, 备份数据会直接加载)
优点:适合大规模的数据恢复、对数据完整性和一致性要求不高更适合使用、节省磁盘空间、回复速度快
缺点:Fork的时候,内存中数据被克隆了一份,大致2倍的膨胀性需要考虑、虽然Redis在fork时使用了写时拷贝技术,但是如果数据庞大时还是比较消耗性能、在备份周期在一定间隔时间做一次备份,所以如果Redis意外down掉的话,就会丢失最后一次快照后的所有修改。
AOF:以日志的形式来记录每个写操作(增量保存),将Redis执行过的所有写指令记录下来(读操作不记录), 只许追加文件但不可以改写文件
- redis启动之初会读取该文件重新构建数据,换言之,redis 重启的话就根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作
关于Rewrite压缩:AOF采用文件追加方式,文件会越来越大为避免出现此种情况,新增了重写机制, 当AOF文件的大小超过所设定的阈值时,Redis就会启动AOF文件的内容压缩, 只保留可以恢复数据的最小指令集.可以使用命令bgrewriteaof
优点:备份机制更稳健,丢失数据概率更低、可读的日志文本,通过操作AOF稳健,可以处理误操作
缺点:比起RDB占用更多的磁盘空间。恢复备份速度要慢。每次读写都同步的话,有一定的性能压力。存在个别Bug,造成恢复不能
7.主从复制
主机数据更新后根据配置和策略, 自动同步到备机的master/slaver机制,Master以写为主,Slave以读为主
实现一主两从:
**哨兵模式:**主要是为了监控主机宕机之后,从机可以立马变为主机,就和上面的反客为主一样,不用手动设置
能够后台监控主机是否故障,如果故障了根据投票数自动将从库转换为主库。
在目录中新建一个文件sentinel.conf
,文件格式不能出错,文件内容为:
sentinel monitor mymaster 127.0.0.1 6379 1
代码的含义为 sentinel
哨兵,监控,一个id(别名),ip加端口号。其中mymaster为监控对象起的服务器名称, 1 为至少有多少个哨兵同意迁移的数量。
启动哨兵模式通过redis的bin目录下,命令:redis-sentinel /sentinel.conf
8.集群
容量不够,并发写操作等问题,通过引入集群,也就是可以多个主机可以操作,另外,主从模式,薪火相传模式,主机宕机,导致ip地址发生变化,应用程序中配置需要修改对应的主机地址、端口等信息。之前通过代理主机来解决,但是redis3.0中提供了解决方案。就是无中心化集群配置。
所谓的代理主机:
无中心化集群:、
集群的定义:
Redis 集群实现了对Redis的水平扩容,即启动N个redis节点,将整个数据库分布存储在这N个节点中,每个节点存储总数据的1/N。
Redis 集群通过分区(partition)来提供一定程度的可用性(availability): 即使集群中有一部分节点失效或者无法进行通讯, 集群也可以继续处理命令请求
搭建集群的步骤:
1. 将rdb,aof文件都删除掉
2. 配置基本的redis.conf文件
文件定义大致如下:
再打开vi或者vim的时候可以输入这种命令 进行一键修改%s/6379/6380
,将其6379修改为6380
1. 将其服务都启动,会有节点node出现,确保所有的节点都出现后,配置其集群
2. 到redis的src目录中
执行该命令–redis-cli --cluster create --cluster-replicas 1 192.168.242.110:6379 192.168.242.110:6380 192.168.242.110:6381 192.168.242.110:6389 192.168.242.110:6390 192.168.242.110:6391
ip一定要真实ip,不能是localhost或者127.0.0.1 --replicas 1 采用最简单的方式配置集群,一台主机,一台从机,正好三组。
集群的启动要多加一个- c的参数,而且哪一个启动都可以,redis-cli -c -p 6379
如果以普通方式登可能直接进入读主机,存储数据时,会出现MOVED重定向操作。所以,应该以集群方式登录登录进入之后,可以通过查看集群信息,cluster nodes
redis cluster 如何分配这六个节点
一个集群至少要有三个主节点。
选项 --cluster-replicas 1 表示我们希望为集群中的每个主节点创建一个从节点。
分配原则尽量保证每个主数据库运行在不同的IP地址,每个从库和主库不在一个IP地址上。
slots:
优势:实现扩容、分摊压力、无中心配置相对简单
劣势:多键操作是不被支持的、多键的Redis事务是不被支持的。lua脚本不被支持、由于集群方案出现较晚,很多公司已经采用了其他的集群方案,而代理或者客户端分片的方案想要迁移至redis cluster,需要整体迁移而不是逐步过渡,复杂度较大。
9.应用问题
缓存穿透:访问的数据缓存找不到,一直转而发送到数据库
解决方案:
一个一定不存在缓存及查询不到的数据,由于缓存是不命中时被动写的,并且出于容错考虑,如果从存储层查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到存储层去查询,失去了缓存的意义
(1)对空值缓存:如果一个查询返回的数据为空(不管是数据是否不存在),我们仍然把这个空结果(null)进行缓存,设置空结果的过期时间会很短,最长不超过五分钟
(2)设置可访问的名单(白名单):使用bitmaps类型定义一个可以访问的名单,名单id作为bitmaps的偏移量,每次访问和bitmap里面的id进行比较,如果访问id不在bitmaps里面,进行拦截,不允许访问。
(3)采用布隆过滤器:(布隆过滤器(Bloom Filter)是1970年由布隆提出的。它实际上是一个很长的二进制向量(位图)和一系列随机映射函数(哈希函数)。布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都远远超过一般的算法,缺点是有一定的误识别率和删除困难。)
将所有可能存在的数据哈希到一个足够大的bitmaps中,一个一定不存在的数据会被 这个bitmaps拦截掉,从而避免了对底层存储系统的查询压力。
(4)进行实时监控:当发现Redis的命中率开始急速降低,需要排查访问对象和访问的数据,和运维人员配合,可以设置黑名单限制服务
缓存击穿:也就是一个key过期,一直访问数据库
解决方案:key可能会在某些时间点被超高并发地访问,是一种非常“热点”的数据。这个时候,需要考虑一个问题:缓存被“击穿”的问题。
(1)预先设置热门数据:在redis高峰访问之前,把一些热门数据提前存入到redis里面,加大这些热门数据key的时长
(2)实时调整:现场监控哪些数据热门,实时调整key的过期时长
(3)使用锁:先判断值是否为空再让他进来与否
缓存雪崩:key对应的数据存在,但在redis中过期,此时若有大量并发请求过来,这些请求发现缓存过期一般都会从后端DB加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把后端DB压垮。
解决方案:
(1)构建多级缓存架构:nginx缓存 + redis缓存 +其他缓存(ehcache等)
(2)使用锁或队列:用加锁或者队列的方式保证来保证不会有大量的线程对数据库一次性进行读写,从而避免失效时大量的并发请求落到底层存储系统上。不适用高并发情况
(3)设置过期标志更新缓存:记录缓存数据是否过期(设置提前量),如果过期会触发通知另外的线程在后台去更新实际key的缓存。
(4)将缓存失效时间分散开:比如我们可以在原有的失效时间基础上增加一个随机值,比如1-5分钟随机,这样每一个缓存的过期时间的重复率就会降低,就很难引发集体失效的事件。
分布式锁:
由于分布式系统多线程、多进程并且分布在不同机器上,这将使原单机部署情况下的并发控制锁策略失效,单纯的Java API并不能提供分布式锁的能力。为了解决这个问题就需要一种跨JVM的互斥机制来控制共享资源的访问
也就是在这个机器上了锁,另外一个机器也要可以识别到这个锁,也就是共享锁,都是同一把锁
UUID防止误删:操作中可能会出现,在解锁的时候刚好设置的时间结束了而导致锁解错了,为此应该多一个判断是否是你的锁,虽然是共享锁,都是一样的,但是可以上锁之后在设置时间,还要给每个用户的这把锁都来一个uuid
lua脚本保证原子性:如果在判断它的uuid相等之后,正准备解锁,发现又误解他人锁,所以引入lua脚本保证它的原子性
总结: