简介
redis是一种高级的key.value存储系统,
五种数据类:
字符串
字符串列表lists( 1.在底层是链表:易于插入删除,不易查询 2.用lists实现消息队 列,可以确保先后顺序,不必像Mysql通过ORDER BY 3.用LRANGE可以方便实现 分页)
字符串集合sets(无序)
有序字符串集合 sorted sets(有序的依据:集合中的每个元素都关联一个序号score 很多操作z开头(zrange、zadd、zrevrange)也称为zsets)
哈希 hashes(字符串和字符串之间的映射)
特点:高性能高并发
key的注意点: key不要太长,会消耗内存降低查找效率
不要太短,key的可读性会降低
在一个项目中key最好使用同一的命名模式
使用场景
计数器:可以对String进行自增自减运算,从而实现计数器功能
Redis这种内存型数据库的读写性能非常高,很适合存储频繁读写的计数量
缓存
查找表:例如 DNS 记录非常适合使用Redis进行存储
查找表和缓存类似,也是利用了Redis快速查找的特性,但是查找表的内容不能失效,而 缓存的内容可以失效,因为缓存不作为可靠的数据来源
消息队列:List是一个双向链表,可以通过lpush和lpop写入和读取消息
在消息队列中最好是使用Kafka,RabbitMQ等消息中间件
会话缓存:可以使用Redis来统一存储多台应用服务器的会话信息
当应用服务器不再存储用户的会话信息,也就不再具有状态,一个用户可以请求任意 一个应用服务器,从而更容易实现高可用性以及可伸缩性
分布式锁实现
set && zset:Set 可以实现交集、并集(retainAll,removeAll,addAll)等操作,从而实现共同好友等 功能
ZSet 可以实现有序性操作,从而实现排行榜等功能
redis持久化
RDB(redis database)
在不同收到时间点,将redis存储的数据生成快照并存储到磁盘等介质上
数据持久化过程中,先将数据写入到一个临时文件中,持久化结束后,才会用这个临时文件替换上次持久化好的文件
优点:可以随时备份,快照文件完整可用
RDB—先创建(fork)一个子进程进行持久化,而住进程是不会进行任何IO操作,确保 redis 的高性能
需要大规模数据恢复,且对于完整性不是非常敏感,rdb好于aof
缺点:当对于数据完整性敏感时不适合,即使5分钟持久化一次当redis故障时,仍有近五分钟的数 据丢失
AOF(Append Only file)
将redis执行过的所有写指令记录下来,在下次redis重新启动时,只需要把这些写指令从前到后再重复执行一遍,实现数据恢复
每秒fsync(把缓存重点写指令记录到磁盘)
优点:AOF重写时,仍然是采用先写临时文件,全部完成后再替换的流程,所以断电、磁盘满等 问题都不影响AOF可用性
FLUSHALL后只要AOF文件没有被重写,可以暂停redis编辑AOF文件,将最后一行的 FLUSHALL删除,重启redis可以恢复redis的所有数据
缺点:AOF文件大,恢复速度慢
AOF文件写坏:备份写坏的AOF文件
运行redis—check—aof—fix
用diff—u来看下两个文件差异,确认问题点
重启redis,加载修复后的文件
AOF重写
重写开始之际redis会创建(fork)一个’重写子进程‘这个子进程会首先读现有的AOF文件,并将其包含的指令进行分析压缩并写入到一个临时文件
同时:主工作进程会将新接收到的写指令一边累积到内存缓冲区中,一边继续写入到原有的AOF文件,保证AOF文件的可用性,避免在重写的过程中出现意外
重写子进程完成重写工作后,给父进程发信号,父进程收到后会将内存中缓存的写指令追加到新的AOF中
追加结束:redis用新的AOF文件代替旧的,之后再有新的写指令,就都追加到新的AOF中
主从
用法
支持一主多从及多级从结构 redis 的主从异步进行,主从同步不影响主逻辑,不会降低处理性能
主从结构:1.粹的冗余备份 2.提升读性能
主从架构中,可考虑关闭主服务器的数据持久化,只让从服务器进行持久化——提高主服务器的处理性能
主从架构中,从服务器一般为只读模式,避免从服务器的数据被误修改,但是仍然可以接收config命令,所以不应该将从服务器暴露到不安全的网络环境中(必须如此的话要给命令重命名)
同步原理
从服务器会向主服务器发出SYNC指令,主服务器收到后会调用BGSAVE指令来创建一个子进程来专门进行数据持久化工作(将主服务器的数据写入RDB文件中)在数据持久化期间,主服务器将执行的写指令都缓存在内存中
在BGSAVE指令执行完成后,主服务器会将持久化好的RDB文件发送给从服务器,从服务器接到此文件后会将其存储在磁盘上,然后将其读取到内存。完成后,主服务器会将这段时间缓存的写指令再以redis协议的格式发送给从服务器。
即使多个从服务器同时发来SYNC指令,主服务器也只会执行一次BFSAVE,然后把持久化好的RDB文件发给多个下游
主服务器会在内存中维护一个缓冲区,缓冲区存储将要发给从服务器的内容,从服务器在与主服务器出现网络瞬断后,从服务器会尝试再次和主服务器连接,一旦成功,从服务器会把“希望同步的主服务器id”和“希望请求的数据偏移设置”发送。主服务器接收到这样的同步请求后,首先会验证主服务器id是否和自己的id匹配,其辞汇检查“请求的偏移设置”是否存在与自己的缓冲区中,都满足则主服务器向从服务器发送增内容
事务处理
MULTI 组装
EXEC 执行
DISCARD 取消
WATCH 监视(事务执行前改变则取消事务的执行)
缓存穿透、雪崩、击穿
缓存穿透
缓存穿透是指缓存和数据库中都没有的数据,而用户不断发起请求
缓存雪崩
缓存雪崩,是指在某一个时间段,缓存集中过期失效,换句话说指缓存中数据大批量到过期时间,而查询数据量巨大,引起数据库压力过大甚至down机
解决:缓存数据的过期时间设置随机,加上一个随机因子,这样能尽可能分散缓存过期时间,防止同一时间大量数据过期现象发生
缓存击穿
缓存击穿和缓存雪崩不同,缓存击穿指并发查同一条数据,缓存雪崩是大量不同数据都过期;缓存击穿,是指一个key非常热点,大并发集中对这一个点进行访问,当这个key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库,从而导致数据库服务器崩溃
解决: 1. 设置热点数据不过期 2.互斥锁
几个问题
如何解决 Redis 的并发竞争 Key 问题?
使用分布式锁
每个客户端对某个方法加锁时,在zookeeper上的与该方法对应的指定节点的目录下,生成一个唯一的瞬时有序节点。 判断是否获取锁的方式很简单,只需要判断有序节点中序号最小的一个。 当释放锁的时候,只需将这个瞬时节点删除即可。同时,其可以避免服务宕机导致的锁无法释放,而产生的死锁问题。完成业务流程后,删除对应的子节点释放锁
如何保证缓存与数据库双写时的数据一致性?
先删缓存后删数据库:在多线程环境下,当一个线程把缓存删掉后,另一个线程读缓存,读不到缓存就读数据库,读到数据后就会更新缓存,这样先前的线程就会造成缓存脏读( 1.产生了长时间的脏读 2.造成缓存穿透或者缓存击穿,导致数据库服务器压力过大而崩溃)
先删数据库后删缓存:多线程情况下,当一个线程删除数据库,另一个线程读数据,读的是缓存数据,当先前一个线程删完数据库后就会更新缓存,这时缓存正常,产生一次脏读(1.短时间的脏读 2.其他线程会读取缓存数据,不会对数据库造成压力,但是更新数据库之后,缓存立刻就更新了)
读请求和写请求串行化
(这是本人在学习java基础的一些理解,有对于本站上一些大佬的资料的收集,如有不当欢迎大家积极指正)