Redis 本质上是一个 Key-Value 类型的内存数据库,整个数据库统统加载在内存当中进行操作,定期通过异步操作把数据库数据 flush 到硬盘上进行保存。
优点:因为是纯内存操作,Redis 的性能非常出色,每秒可以处理超过 10 万次读写操作,是已知性能最快的 Key-Value DB。 Redis 的出色之处不仅仅是性能,Redis 最大的魅力是支持保存多种数据结构。
缺点:数据库容量受到物理内存的限制,不能用作海量数据的高性能读写,因此 Redis 适合的场景主要局限在较小数据量的高性能操作和运算上
memcached 所有的值均是简单的字符串,redis 作为其替代者,支持更为丰富的数据类型
redis 的速度比 memcached 快很多
redis 可以持久化其数据 redis 可以持久化其数据
字符串 String、 哈希Hash、列表List、集合 Set、有序集合 ZSet。如果是高级用户,还需要加上下面几种数据结构 HyperLogLog、 Geo、Pub/Sub。
1. noeviction: 不删除数据(但redis还会根据引用计数器进行释放),这时如果内存不够时,会直接返回错误。
2. allkeys-lru: 尝试回收最少使用的键(LRU),使得新添加的数据有空间存放。
3. volatile-lru: 尝试回收最少使用的键(LRU),但仅限于在过期集合的键,使得新添加的数据有空间存放。
4. allkeys-random: 回收随机的键使得新添加的数据有空间存放。
5. volatile-random: 回收随机的键使得新添加的数据有空间存放,但仅限于在过期集合的键。
6. volatile-ttl: 回收过期集合的键,并且优先回收存活时间(TTL)较短的键,使得新添加的数据有空间存放。
512M
Redis 为了达到最快的读写速度将数据都读到内存中,并通过异步的方式将数据写入磁盘。 所以 redis 具有快速和数据持久化的特征,如果不将数据放在内存中,磁盘 I/O 速度为严重影响 redis 的 性能。
Twemproxy
codis
Redis-cluster(本身提供了自动将数据分散到 Redis Cluster 不同节点的能力,整个数据集 合的某个数据子集存储在哪个节点对于用户来说是透明的)
redis-cluster 分片原理:Cluster 中有一个 16384 长度的槽(虚拟槽),编号分别为 0-16383。 每个 Master 节点都会负责一部分的槽,当有某个 key 被映射到某个 Master 负责的槽,那 么这个 Master 负责为这个 key 提供服务,至于哪个 Master 节点负责哪个槽,可以由用户 指定,也可以在初始化的时候自动生成,只有 Master 才拥有槽的所有权。Master 节点维 护着一个 16384/8 字节的位序列,Master 节点用 bit 来标识对于某个槽自己是否拥有。比 如对于编号为 1 的槽,Master 只要判断序列的第二位(索引从 0 开始)是不是为 1 即可。 这种结构很容易添加或者删除节点。比如如果我想新添加个节点 D, 我需要从节点 A、B、 C 中得部分槽到 D 上。
有 A,B,C 三个节点的集群,在没有复制模型的情况下,如果节点 B 失败了,那么整个集群就会以为缺少 5501-11000 这个范围的槽而不可用。
Redisson、Jedis、lettuce 等等,官方推荐使用 Redisson。
Redis 集群没有使用一致性 hash,而是引入了哈希槽的概念,Redis 集群有 16384 个哈希槽,每个 key 通 过 CRC16 校验后对 16384 取模来决定放置哪个槽,集群的每个节点负责一部分 hash 槽。
为了使在部分节点失败或者大部分节点无法通信的情况下集群仍然可用,所以集群使用了主从复制模型, 每个节点都会有 N-1 个复制品.
Redis 并不能保证数据的强一致性,这意味在实际中集群在特定的条件下可能会丢失写操作。
16384 个
事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行,事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
事务是一个原子操作:事务中的命令要么全部被执行,要么全部都不执行
Redis 事务相关的命令有哪几个?
MULTI、EXEC、DISCARD、WATCH
Redis key 的过期时间和永久有效分别怎么设置?
EXPIRE 和 PERSIST 命令
Redis 如何做内存优化?
尽可能使用散列表(hashes),散列表使用的内存非常小
Redis 回收进程如何工作的?
一个客户端运行了新的命令,添加了新的数据。 Redis检查内存使用情况,如果大于 maxmemory 的限制, 则根据设定好的策略进行回收。
使用过 Redis 分布式锁么,它是怎么实现的?
先拿 setnx 来争抢锁,抢到之后,再用 expire 给锁加一个过期时间防止锁忘记了释放。 如果在 setnx 之后执行 expire 之前进程意外 crash 或者要重启维护了,那会怎么样? set 指令有非常复杂的参数,这个应该是可以同时把 setnx 和 expire 合成一条指令来用的!
什么是缓存穿透?如何避免?什么是缓存雪崩?何如避免?
缓存穿透: 一般的缓存系统,都是按照 key 去缓存查询,如果不存在对应的 value,就会去DB查找。一些恶意的请求会故意查询不存在的 key,请求量很大,就会对后端系统造成很大的压力。这就叫做缓存穿透。
如何避免:
对查询结果为空的情况进行缓存,缓存时间设置短一点,或该 key 对应的数据 insert 了之后清理缓存。
对一定不存在的 key 进行过滤。可以把所有的可能存在的 key 放到一个大的 Bitmap 中,查询时通过该 bitmap 过滤。
**缓存雪崩:**当缓存服务器重启或者大量缓存集中在某一个时间段失效,这样在失效的时候,会给后端系统带来很大压力。导致系统崩溃。
如何避免:
在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个 key 只允许一个线程查询数据和写缓存,其他线程等待。
做二级缓存,A1 为原始缓存,A2 为拷贝缓存,A1 失效时,可以访问 A2,A1 缓存失效时间设置为 短期,A2 设置为长期
不同的 key,设置不同的过期时间,让缓存失效的时间点尽量均匀
主从复制实现:主节点将自己内存中的数据做一份快照,将快照发给从节点,从节点将数据恢复到内存中。之后再每次增加新数据的时候,主节点以类似于 mysql 的二进制日志方式将语句发送给从节点,从节点拿到主节点发送过来的语句进行重放。
寻址计算:
hash 算法:来了一个 key,首先计算 hash 值,然后对节点数取模。然后打在不同的 master 节点上。一旦某一个 master 节点宕机,所有请求过来,都会基于最新的剩余 master 节点数去取模,尝试去取数据。这会导致大部分的请求过来,全部无法拿到有效的缓存,导致大量的流量涌入数据库。
一致性hash算法:一致性 hash 算法将整个 hash 值空间组织成一个虚拟的圆环,整个空间按顺时针方向组织,下一步将各个 master 节点(使用服务器的 ip 或主机名)进行 hash。这样就能确定每个节点在其哈希环上的位置。
来了一个 key,首先计算 hash 值,并确定此数据在环上的位置,从此位置沿环顺时针“行走”,遇到的第一个 master 节点就是 key 所在位置。
在一致性哈希算法中,如果一个节点挂了,受影响的数据仅仅是此节点到环空间前一个节点(沿着逆时针方向行走遇到的第一个节点)之间的数据,其它不受影响。增加一个节点也同理。
然而,一致性哈希算法在节点太少时,容易因为节点分布不均匀而造成缓存热点的问题。为了解决这种热点问题,一致性 hash 算法引入了虚拟节点机制,即对每一个节点计算多个 hash,每个计算结果位置都放置一个虚拟节点。这样就实现了数据的均匀分布,负载均衡。
缓存与数据库不一致怎么办?
**描述:**假设采用的主存分离,读写分离的数据库, 如果一个线程 A 先删除缓存数据,然后将数据写入到主库当中,这个时候,主库和从库同步没有完成,线程 B 从缓存当中读取数据失败,从从库当中读取到旧数据,然后更新至缓存,这个时候,缓存当中的就是旧的数据。 发生上述不一致的原因在于,主从库数据不一致问题,加入了缓存之后,主从不一致的时间被拉长了
**处理思路:**在从库有数据更新之后,将缓存当中的数据也同时进行更新,即当从库发生了数据更新之后,向缓存发出删除,淘汰这段时间写入的旧数据。
**场景描述:**对于主从库,读写分离,如果主从库更新同步有时差,就会导致主从库数据的不一致
解决思路:
忽略这个数据不一致,在数据一致性要求不高的业务下,未必需要时时一致性
强制读主库,使用一个高可用的主库,数据库读写都在主库,添加一个缓存,提升数据读取的性能。
选择性读主库,添加一个缓存,用来记录必须读主库的数据,将哪个库,哪个表,哪个主键,作为缓存的 key,设置缓存失效的时间为主从库同步的时间,如果缓存当中有这个数据,直接读取主库,如果缓存当中没有这个主键,就到对应的从库中读取。
发现:
凭借业务经验,进行预估哪些是热key
在客户端进行收集
redis-faina 工具, 但是该命令在高并发的条件下,有内存增暴增的隐患,还会降低redis的性能。
hotkeys参数
自己抓包评估
防止:
利用二级缓存
备份热key,在多个redis上都存一份不就好了