1、纯内存操作
2、单线程可以省去多线程时CPU上下文会切换的时间
3、渐进式ReHash、缓存时间戳
数组需要扩容的时候,他会维护两张hash表,比如第一张的数组长度为6,另一张的数组长度为12,在set和get的时候他只会把这一个节点的数据hash到长度为12的表中。
Redis用到时间的时候不会使用Thread.currentTimeMiles()方法,这个会方法会调用操作系统,比较耗时,Redis会把时间缓存到内存中。
1、分布式缓存
2、分布式锁
3、计数器
4、排行榜(使用有序集合ZSet)
1、使用Redis、CPU 不是瓶颈,受制 内存、网络
2、提高Redis, Pipeline(命令批量) 每秒100万个请求
3、单线程,内部维护比较低
4、如果是多线程(线程切换、加锁\解锁、导致死锁问题)
5、惰性Rehash(渐进性式的Rehash)
所以,一般的公司,单线程Redis 就够了。
1、Redis服务器可以处理8万到10万 QPS,对于80%的公司来说,单线程的Redis已经足够使用。
2、提高Redis, Pipeline(命令批量) 每秒100万个请求
3、但随着越来越复杂的业务场景,有些公司动不动就上亿的交易量,因此需要更大的QPS。
4、常见的解决方案是在分布式架构中对数据进行分区并采用多个服务器,有非常大的缺陷:
管理的Redis服务器太多,维护代价大
某些适用于单个Redis服务器的命令不适用于数据分区
数据分区无法解决热点读/写问题
5、指的是IO的多线程(内部执行命令还是单线程)
多线程任务 分摊到 Redis 同步IO中读写 负载。
相当于多核服务器,一个核来处理IO操作。
Redis的慢查询
许多存储系统(例如 MySQL)提供慢查询日志帮助开发和运维人员定位系统存在的慢操作。所谓慢查询日志就是系统在命令执行前后计算每条命令的执行时间,当超过预设调值,就将这条命令的相关信息(例如:发生时间,耗时,命令的详细信息)记录下来,Redis也提供了类似的功能。
Pipeline 批量处理
事务、
数据持久化(RDB和AOF)、
使用哨兵实现集群、
分布式锁
为了高性能、高并发。
Memcached是一种基于内存的key-value存储,用来存储小块的任意数据(字符串、对象)。
事务是一个原子操作:事务中的命令要么全部被执行,要么全部都不执行。
Redis提供了简单的事务功能,将一组需要一起执行的命令放到multi和exec两个命令之间。
multi 命令代表事务开始,exec命令代表事务结束。另外discard命令是回滚。
注意:
1、Redis的事务功能很弱。在事务回滚机制上,Redis只能对基本的语法错误进行判断。
2、运行时错误
例如:事务内第一个命令简单的设置一个string类型,第二个对这个key进行sadd命令,这种就是运行时命令错误,因为语法是正确的:
可以看到Redis并不支持回滚功能,第一个set命令已经执行成功,需要自己手动修复这类问题。
redis采用的是 定期删除+惰性删除 策略。
1、为什么不用定时删除策略?
定时删除,用一个定时器来负责监视key,过期则自动删除。虽然内存及时释放,但是十分消耗CPU
资源。在大并发请求下,CPU要将时间应用在处理请求,而不是删除key,因此没有采用这一策略.
2、定期删除 + 惰性删除是如何工作的呢?
定期删除,redis默认每个100ms检查,是否有过期的key,有过期key则删除。需要说明的是,redis
不是每个100ms将所有的key检查一次,而是随机抽取进行检查,如果这块区域超过25%过期,会
对这块区域再次进行删除。因此,如果只采用定期删除策略,会导致很多key到时间没有删除。 于
是,惰性删除派上用场。也就是说在你获取某个key的时候,redis会检查一下,这个key如果设置
了过期时间那么是否过期了?如果过期了此时就会删除。
缓存穿透:指查询一个不存在的数据,如果从存储层查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到 DB 去查询,可能导致 DB 挂掉。
解决方案:
1.查询返回的数据为空,仍把这个空结果null进行缓存,但过期时间会比较短;
2.布隆过滤器:将所有可能存在的数据哈希到一个足够大的 bitmap 中,一个一定不存在的数据会被这个 bitmap 拦截掉,从而避免了对 DB 的查询。
缓存雪崩:设置缓存时采用了相同的过期时间,导致缓存在某一时刻同时失效,请求全部转发到
DB,DB 瞬时压力过重雪崩。与缓存击穿的区别:雪崩是很多 key,击穿是某一个key 缓存。
基于 Redis 实现的分布式锁,使用到的是 SETNX 命令,有可能会出现死锁现象,锁过期时间不好评估,如果key的过期时间太短,拿到锁的线程还没有执行完业务流程,其他的线程就再次拿到锁了,如果key的过期时间太长,拿到锁的线程如果发生了异常,其他的线程就必须要等锁过期了才能拿到锁,这时候就需要我们开启一 个「守护线程」,定时去检测这个锁的失效时间,如果锁快要过期了,操作共享资源还末完成,那么就自动对锁进行 「续期」,重新设置过期时间。这个守护线程我们一般也把它叫做「看门狗」线程。
1、bigkey是指key对应的value所占的内存空间比较大。
如果按照数据结构来细分的话,一般分为字符串类型bigkey和非字符串类型bigkey。
字符串类型:体现在单个value值很大,一般认为超过10KB就是bigkey
非字符串类型:哈希、列表、集合、有序集合,体现在元素个数过多。
2、bigkey的危害体现在三个方面:
内存空间不均匀
操作bigkey比较耗时
网络拥塞:每次获取bigkey产生的网络流量较大
3、如何发现bigkey
可以通过redis-cli--bigkeys命令统计bigkey的分布。
1、业务隔离。
2、通过添加前缀的方式对key进行设计。
3、“Redis 并发竞争” 问题就是高并发写同一个key时导致的值错误。
常用的解决方法:
watch
命令可以方便的实现乐观锁。注意不要在分片集群中使用。1、缓存预加载(预热)
2、增加缓存的存储空间,提高缓存的数据、提高命中率
3、调整缓存的存储类型
4、提升缓存的更新频次
我们一般采用RDB、AOF、混合持久化。
RDB的优缺点:
优点:RDB持久化文件,速度比较快,而且存储的是一个二进制文件,传输起来很方便。
缺点:RDB无法保证数据的绝对安全,有时候就是1s也会有很大的数据丢失。
AOF的优缺点:
优点:AOF相对RDB更加安全,一般不会有数据的丢失或者很少,官方推荐同时开启AOF和RDB。
缺点:AOF持久化的速度,相对于RDB较慢,存储的是一个文本文件,到了后期文件会比较大,传输困难。
Redis为了达到最快的读写速度,将数据都读到内存中,并通过异步的方式将数据写入磁盘,所以Redis
具有快速和数据持久化的特征。如果不将数据放在内存中,磁盘I/O速度为严重影响Redis的性能。
第一种方案:采用延时双删策略
具体的步骤就是:
先删除缓存;
再写数据库;
休眠500毫秒;
再次删除缓存。
第二种方案:异步更新缓存(基于订阅binlog的同步机制)
技术整体思路:
MySQL binlog增量订阅消费+消息队列+增量数据更新到redis
1. Redis Sentinel(哨兵),体量较小时,选择 Redis Sentinel,单主 Redis 足以支撑业务。
哨兵模式基于主从复制模式。
哨兵顾名思义,就是来为Redis集群站哨的,一旦发现问题能做出相应的应对处理。其功能包括
2、Redis Cluster Redis 官方提供的集群化方案,体量较大时,选择 Redis Cluster,通过分片,使用更多内存。
哨兵模式解决了主从复制不能自动故障转移,达不到高可用的问题,但还是存在难以在线扩容,
Redis容量受限于单机配置的问题。Cluster模式实现了Redis的分布式存储,即每台节点存储不同
的内容(将16383个槽进行平分),来解决在线扩容的问题。
Cluster模式集群节点最小配置6个节点(3主3从,因为需要半数以上),其中主节点提供读写操作,从节点作为备用节点,不提供请求,只作为故障转移使用。
所有的redis节点彼此互联(PING-PONG机制)。
3、Twemprox (了解)
Twemprox是Twtter开源的一个 Redis和 Memcached 代理服务器,主要用于管理 Redis和Memcached 集群,减少与Cache 服务器直接连接的数量。
4. Codis (了解)
Codis是一个代理中间件,当客户端向Codis发送指令时,Codis负责将指令转发到后面的Redis来执行,并将结果返回给客户端。一个Codis实例可以连接多个Redis实例,也可以启动多个Codis实例来支撑,每个Codis节点都是对等的,这样可以增加整体的QPS需求,还能起到容灾功能。
1、当访问一个 Master 和 他的Slave 节点都挂了的时候,会报 槽无法获取。如果我们配置了一个
参数,即使有槽没有分配也是可以使用的。
2、当集群 Master 节点个数小于 3 个的时候,或者集群可用节点个数为偶数的时候,基于 fail 的
这种选举机制 的自动主从切换过程 可能会不能正常工作。
slot:称为哈希槽
Redis 集群中内置了 16384 个哈希槽,当需要在 Redis 集群中放置一个 key-value时,redis 先对 key 使用 crc16 算法算出一个结果,然后把结果对 16384 求余数,这样每个 key 都会对应一个编号在 0-16383 之间的哈希槽,redis 会根据节点数量大致均等的将哈希槽映射到不同的节点。
使用哈希槽的好处就在于可以方便的添加或移除节点。
当需要增加节点时,只需要把其他节点的某些哈希槽挪到新节点就可以了;
当需要移除节点时,只需要把移除节点上的哈希槽挪到其他节点就行了;
1、过期 key 被清理
2、最大内存不足,导致 Redis 自动清理部分 key 以节省空间
3、主库故障后自动重启,从库 异步进行同步
4、单独的主备方案,网络不稳定触发 哨兵的自动切换主从节点,切换期间会有数据丢失。
1、持久化性能问题
主从 主节点(master)不要做持久化,从节点(slave)做持久化
2、如果数据比较重要,从节点(slave)开启AOF, 策略每秒同步一次
3、在同一个局域网内,主从复制会流畅。
4、 尽量避免 主库 压力很大的情况下,增加从库
5、主从复制 不要采用网状结构、要采用线性结构
1、热数据
比如点赞数、评论数这些不断变化的数据,考虑使用缓存, 同步到redis.
2、冷数据
经常不访问的数据
3、如何判断是冷数据还是热数据
数据更新之前至少读取2次----才能放缓存
1、客户端使用了可能造成阻塞的命令
keys * 、Hgetall 、smembers 等,
2、Bigkey删除, 比如zset (删除100万的元素 需要2S)
3、清空库的时候,比如 flushdb flushall
4、AOF日志同步写,记录AOF日志大量的写操作,1个同步写磁盘耗时需要1 ~2ms。
5、 从库加载RDB文件的时候
以下三个都是设置了过期时间的:
volatile-lru: 尝试回收最少使用的键(LRU)
volatile-ttl: 优先回收存活时间(TTL)较短的键,
volatile-random: 回收随机的键
以下两个是针对所有的键:
allkeys-lru: 尝试回收最少使用的键(LRU)
allkeys-random: 回收随机的键
LRU算法
类似于mybatis的管理page的链表,也是用到了LRU算法。