Java中间件学习之Redis

1、Redis是什么

Redis是一个基于内存的高性能key-value结构的非关系型数据库。

Redis 优势:

  • 性能极高 – Redis能读的速度是110000次/s,写的速度是81000次/s 。
  • 丰富的数据类型 – Redis支持二进制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 数据类型操作。
  • 原子–Redis的所有操作都是原子性的,意思就是要么成功执行要么失败完全不执行。单个操作是原子性的。多个操作也支持事务,即原子性,通过MULTI和EXEC指令包起来。
  • 丰富的特性 – Redis还支持 publish/subscribe, 通知, key 过期等等特性。

2、Redis的数据结构

1、String:
String 是 redis 最基本的类型,一个 key 对应一个 value。string 类型是二进制安全的,意思是 redis 的 string 可以包含任何数据,比如jpg图片或者序列化的对象。string 类型是 Redis 最基本的数据类型,string 类型的值最大能存储 512MB。
2、List:
list 是有序可重复列表,和 Java 的 LinkedList 比较像,可以通过索引查询;插入删除速度快。
3、Hash:
是一个键值(key=>value)对集合(key =>(key => value)),特别适合用于存储对象。例如:set u001 "李三,18,20010101"
4、Set:
Redis的Set是string类型的无序集合。使用场景:1.共同好友、二度好友。
5、Sorted Set:
zset是Redis中比较有特色的数据类型,它和set一样是不可重复的,区别在于多了score值,用来代表排序的权重。也就是当你需要一个有序的,不可重复的集合列表时,就可以考虑使用这种数据类型。

3、Redis持久化机制

为什么要持久化

Redis是基于内存的,如果Redis服务器挂了,数据就会丢失。为了避免数据丢失了,Redis提供了两种持久化方式,RDB和AOF。

1、RDB(默认)

RDB持久化机制,是对 redis 中的数据执行周期性的持久化。

优点:
冷热备:RDB会生成多个数据文件,每个数据文件都代表了某一个时刻中redis的数据,这种多个数据文件的方式,非常适合做冷热备。
高性能:RDB对redis的对外读写能力影响有限,可以让redis保持高性能,因为redis主进程只需要fork一个子进程,让子进程执行磁盘IO来进行RBD持久化即可。
快速恢复:相对AOF持久化机制来说,直接基于RDB的数据恢复更加快速。因为RDB文件就是redis的数据备份。

缺点:
数据丢失:如果采用RDB进行数据的备份,数据完整性没有AOF表现的那么好。一般来说RDB数据快照文件是每个5分钟进行一次备份。所以可能会造成5分钟的数据丢失。
海量数据阻塞主进程:尽管RDB仅仅是fork一个子进程来执行RDB快照数据文件的备份,如果数据文件特别大,可能导致主进程被阻塞数毫秒甚至是数秒。

2、AOF

AOF 机制对每条写入命令作为日志,以 append-only 的模式写入一个日志文件中,在 redis 重启的时候,可以通过回放 AOF 日志中的写入指令来重新构建整个数据集。
AOF持久化配置:
AOF默认是关闭的,可以通过appendonly yes来开启。

appendfsync配置:

no:不使用fsync方法同步,而是交给操作系统write函数去执行同步操作,在linux操作系统中大约每30秒刷一次缓冲。这种情况下,缓冲区数据同步不可控,并且在大量的写操作下,aof_buf缓冲区会堆积会越来越严重,一旦redis出现故障,数据丢失严重。

always:表示每次有写操作都调用fsync方法强制内核将数据写入到aof文件。这种情况下由于每次写命令都写到了文件中, 虽然数据比较安全,但是因为每次写操作都会同步到AOF文件中,所以在性能上会有影响,同时由于频繁的IO操作,硬盘的使用寿命会降低。

everysec:数据将使用调用操作系统write写入文件,并使用fsync每秒一次从内核刷新到磁盘。 这是折中的方案,兼顾性能和数据安全,所以redis默认推荐使用该配置。

优点:
数据丢失更少:AOF日志文件每隔一秒会通过fsync刷新到磁盘。所以在默认情况下只会丢失一秒的数据。
性能开销小:AOF日志文件即使过大,出现后台重写操作,也不会影响客户端的读写。因为在rewrite log的时候,会对其中的指令进行压缩。创建出一份需要恢复的数据的最小日志出来。在创建新日志文件的时候,老的日志文件还是照常写入。当新的merge后的日志文件ready的时候,在交换新老日志文件即可。
灾难性误删除紧急恢复:AOF日志文件的命令通过非常可读的方式进行记录,这个特性非常适合做灾难性的误删除的紧急恢复。比如某人不小心用flushall命令清空了所有数据,只要后台rewrite还没发生,那么就可以立即拷贝AOD文件,将最后一条flushall命令删除,然后再将该AOF文件放回去,然后恢复所有数据。

缺点:
性能较低:虽然AOF日志文件采用的是append-only模式写入,所以没有任何磁盘寻址的开销,写入性能非常高,但是相对于RDB持久化来说对redis性能影响还是较高的。每隔一秒执行一次fsync操作,这个开销还是很高的。(联想elasticsearch近实时性)
数据恢复慢:因为AOF是基于日志文件的数据恢复,所以相较于RDB数据备份来说数据恢复慢。

选择:

如果数据不能丢失,RDB和AOF混用
如果只作为缓存使用,可以承受几分钟的数据丢失的话,可以只使用RDB。
如果只使用AOF,优先使用everysec的写回策略。

4、Redis单线程还是多线程

Redis工作线程是单线程的,但是,Redis内部运行是多线程的。

Redis单线程为什么快

(1) 基于内存操作:Redis 的所有数据都存在内存中,因此所有的运算都是内存级别的,所以他的性能比较高;

(2) 数据结构简单:Redis 的数据结构是专门设计的,而这些简单的数据结构的查找和操作的时间大部分复杂度都是 O(1),因此性能比较高;

(3) 多路复用和非阻塞 I/O:Redis使用 I/O多路复用功能来监听多个 socket连接客户端,这样就可以使用一个线程连接来处理多个请求,减少线程切换带来的开销,同时也避免了 I/O 阻塞操作;

(4) 避免上下文切换:因为是单线程模型,因此就避免了不必要的上下文切换和多线程竞争,这就省去了多线程切换带来的时间和性能上的消耗,而且单线程不会导致死锁问题的发生;

I/O 多路复用

简单来说就是通过监测文件的读写事件再通知线程执行相关操作,保证 Redis 的非阻塞 I/O 能够顺利执行完成的机制。

多路指的是多个socket连接,

复用指的是复用一个线程。多路复用主要有三种技术:select,poll,epoll。

epoll是最新的也是目前最好的多路复用技术。采用多路 I/O 复用技术可以让单个线程高效的处理多个连接请求(尽量减少网络IO的时间消耗),且Redis在内存中操作数据的速度非常快(内存内的操作不会成为这里的性能瓶颈),主要以上两点造就了Redis具有很高的吞吐量。

开启多线程

1、设置io-thread-do-reads配置项为yes,表示启动多线程。

2、设置线程个数。关于线程数的设置,官方的建议是如果为 4 核的 CPU,建议线程数设置为2或3,如果为8核CPU建议线程数设置为6,线程数一定要小于机器核数,线程数并不是越大越好。

5、Redis事务

Redis 事务的本质是一组命令的集合。事务支持一次执行多个命令,一个事务中所有命令都会被序列化。在事务执行过程,会按照顺序串行化执行队列中的命令,其他客户端提交的命令请求不会插入到事务执行命令序列中。

总结说:redis事务就是一次性、顺序性、排他性的执行一个队列中的一系列命令。

特点:

1、没有隔离级别:批量操作在发送 EXEC 命令前被放入队列缓存,并不会被实际执行,也就不存在事务内的查询要看到事务里的更新,事务外查询不能看到。

2、不能保证原子性:Redis中,单条命令是原子性执行的,但事务不保证原子性,且没有回滚。事务中任意命令执行失败,其余的命令仍会被执行。

6、Redis实现分布式锁

第一版:SetNX:获取锁的时候,使用 setnx(SETNX key value:当且仅当 key 不存在时,set 一个 key 为 value 的字符串,返回 1;若 key 存在,则什么都不做,返回 0)加锁,锁的 value 值为一个随机生成的 UUID,在释放锁的时候进行判断。

第二版:SetNX+过期时间(避免死锁)

第三版:延时过期时间(防止程序还没执行完key过期)

第四版:判断是否为当前线程(通过线程名、通过uuid判断)

第五版:Redison&Redlock

Redison
        String lockKey = "123";
        RLock lock = redisson.getLock(lockKey);
        try {
            lock.lock();
            System.out.println("获取成功");
        }finally {
            lock.unlock();
        }

Lock:底层是lua脚本,先加锁和过期时间(默认30S),然后通过threadId判断是否当前线程,如果是当前线程且程序没执行结束则续命。

7、Redis内存淘汰机制

  • noeviction:添加数据时,如果redis判断该操作会导致占用内存大小超过内存限制,就返回error,然后啥也不干
  • allkeys-lru:添加数据时,如果redis判断该操作会导致占用内存大小超过内存限制,就会扫描所有的key,淘汰一些最近未使用的key
  • volatile-lru:添加数据时,如果redis判断该操作会导致占用内存大小超过内存限制,扫描那些设置里过期时间的key,淘汰一些最近未使用的key
  • allkeys-random:添加数据时,如果redis判断该操作会导致占用内存大小超过内存限制,就会扫描所有的key,随机淘汰一些key
  • volatile-random:添加数据时,如果redis判断该操作会导致占用内存大小超过内存限制,扫描那些设置里过期时间的key,随机淘汰一些key
  • volatile-ttl:添加数据时,如果redis判断该操作会导致占用内存大小超过内存限制,扫描那些设置里过期时间的key,淘汰一些即将过期的key
  • volatile-lfu:添加数据时,如果redis判断该操作会导致占用内存大小超过内存限制,就会淘汰一些设置了过期时间的,并且最近最少使用的key
  • allkeys-lfu:添加数据时,如果redis判断该操作会导致占用内存大小超过内存限制,就会扫描所有的key,淘汰一些最近最少使用的key

如何设置淘汰策略:

1、查看最大内存配置:config get maxmemory(默认0,表示不限制)

2、修改淘汰策略:maxmemory-policy(默认neoviction)

补充:
1、LRU算法:LRU是Least Recently Used的缩写,也就是表示最近很少使用,也可以理解成最久没有使用。也就是说当内存不够的时候,每次添加一条数据,都需要抛弃一条最久时间没有使用的旧数据。标准的LRU算法为了降低查找和删除元素的时间复杂度,一般采用Hash表和双向链表结合的数据结构,hash表可以赋予链表快速查找到某个key是否存在链表中,同时可以快速删除、添加节点。

2、LFU算法:LFU(Least Frequently Used),表示最近最少使用,它和key的使用次数有关,其思想是:根据key最近被访问的频率进行淘汰,比较少访问的key优先淘汰,反之则保留。LFU的原理是使用计数器来对key进行排序,每次key被访问时,计数器会增大,当计数器越大,意味着当前key的访问越频繁,也就是意味着它是热点数据。 它很好的解决了LRU算法的缺陷:一个很久没有被访问的key,偶尔被访问一次,导致被误认为是热点数据的问题。

3、TTL算法:就是在设置了过期时间的键里面呢,去找到更早过期的时间key进行有限的移除。

8、Redis如何处理过期数据

定时删除:

Redis会周期性的随机测试一批设置了过期时间的key并进行处理,测试到的已过期的key将被删除。
具体的算法如下:

  1. Redis配置项hz定义了serverCron任务的执行周期,默认为10,代表了每秒执行10次;
  2. 每次过期key清理的时间不超过CPU时间的25%,比如hz默认为10,则一次清理时间最大为25ms;
  3. 清理时依次遍历所有的db;
  4. 从db中随机取20个key,判断是否过期,若过期,则逐出;
  5. 若有5个以上key过期,则重复步骤4,否则遍历下一个db;
  6. 在清理过程中,若达到了25%CPU时间,退出清理过程;

惰性删除:

惰性删除不会去主动删除数据,而是在访问数据的时候,再检查当前键值是否过期,如果过期则执行删除并返回 null 给客户端,如果没有过期则返回正常信息给客户端。
它的优点是简单,不需要对过期的数据做额外的处理,只有在每次访问的时候才会检查键值是否过期,缺点是删除过期键不及时,造成了一定的空间浪费。

9、缓存穿透、缓存击穿、缓存雪崩

缓存穿透:

产生的原因:指在redis缓存中不存在数据,这个时候只能去访问持久层数据库,当用户很多时,缓存都没有命中就会照成很大压力.
解决办法:
1、根据IP判断是否正常访问,如果不是则拒绝查询;
2、给该key设置一个null值,指定过期时间。

缓存击穿:

产生的原因:指在同一个时间内访问一个请求的请求数过多,而在这个时候缓存某个key失效了,这个时候就会冲向数据库照成缓存击穿。
解决办法:
1、不给给该值设置过期时间;
2、设置一个分布式锁,当key失效获取不到数据时,只有第一个线程获取到锁,然后去数据库查寻,查询到后加入到redis中并释放锁。

缓存雪崩:

产生的原因:在某个时间段,大部分key失效,缓存集体过期;或者redis宕机。
解决办法:
1、批量存数据的时候给这些数据设置随机的过期时间;
2、如果预测可能发生在热点数据的话可以不设置过期时间

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值