共计 14271 个字符,预计需要花费 36 分钟才能阅读完成。
1、缓存定义
高速数据存储层,进步程序性能
2、为什么要用缓存(读多写少,高并发)
1、进步读取吞吐量
2、晋升应用程序性能
3、升高数据库老本
4、缩小后端负载
5、打消数据库热点
6、可预测的性能
3、缓存分类
3.1、单机缓存(localCache)
实现计划
1、基于 JSR107 标准自研(理解即可):
1、Java Caching 定义了 5 个外围接口,别离是 CachingProvider, CacheManager, Cache, Entry 和 Expiry。
2、CachingProvider 定义了创立、配置、获取、治理和管制多个 CacheManager。一个利用能够在运行期拜访多个 CachingProvider。
3、CacheManager 定义了创立、配置、获取、治理和管制多个惟一命名的 Cache,这些 Cache 存在于 CacheManager 的上下文中。一个 CacheManager 仅被一个 CachingProvider 所领有。
4、Cache 是一个相似 Map 的数据结构并长期存储以 Key 为索引的值。一个 Cache 仅被一个 CacheManager 所领有。
5、Entry 是一个存储在 Cache 中的 key-value 对。
每一个存储在 Cache 中的条目有一个定义的有效期,即 Expiry Duration。
一旦超过这个工夫,条目为过期的状态。一旦过期,条目将不可拜访、更新和删除。缓存有效期能够通过 ExpiryPolicy 设置。
2、基于 ConcurrentHashMap 实现数据缓存
3.2、分布式缓存(redis、Memcached)
4、单机缓存
1、本人实现一个单机缓存
创立缓存类
/**
* @author yinfeng
* @description 本地缓存实现:用 map 实现一个简略的缓存性能
* @since 2022/2/8 13:54
*/
public class MapCacheDemo {
/**
* 在构造函数中,创立了一个守护程序线程,每 5 秒扫描一次并清理过期的对象
*/
private static final int CLEAN_UP_PERIOD_IN_SEC = 5;
/**
* ConcurrentHashMap 保障线程平安的要求
* SoftReference <Object> 作为映射值,因为软援用能够保障在抛出 OutOfMemory 之前,如果短少内存,将删除援用的对象。*/
private final ConcurrentHashMap<String, SoftReference<CacheObject>> cache = new ConcurrentHashMap<>();
public MapCacheDemo() {
// 创立了一个守护程序线程,每 5 秒扫描一次并清理过期的对象
Thread cleanerThread = new Thread(() -> {while (!Thread.currentThread().isInterrupted()) {
try {Thread.sleep(CLEAN_UP_PERIOD_IN_SEC * 1000);
cache.entrySet().removeIf(entry -> Optional.ofNullable(entry.getValue()).map(SoftReference::get).map(CacheObject::isExpired).orElse(false));
} catch (InterruptedException e) {Thread.currentThread().interrupt();}
}
});
cleanerThread.setDaemon(true);
cleanerThread.start();}
public void add(String key, Object value, long periodInMillis) {if (key == null) {return;}
if (value == null) {cache.remove(key);
} else {long expiryTime = System.currentTimeMillis() + periodInMillis;
cache.put(key, new SoftReference<>(new CacheObject(value, expiryTime)));
}
}
public void remove(String key) {cache.remove(key);
}
public Object get(String key) {return Optional.ofNullable(cache.get(key)).map(SoftReference::get).filter(cacheObject -> !cacheObject.isExpired()).map(CacheObject::getValue).orElse(null);
}
public void clear() {cache.clear();
}
public long size() {return cache.entrySet().stream().filter(entry -> Optional.ofNullable(entry.getValue()).map(SoftReference::get).map(cacheObject -> !cacheObject.isExpired()).orElse(false)).count();}
/**
* 缓存对象 value
*/
private static class CacheObject {
private Object value;
private final long expiryTime;
private CacheObject(Object value, long expiryTime) {
this.value = value;
this.expiryTime = expiryTime;
}
boolean isExpired() {return System.currentTimeMillis() > expiryTime;
}
public Object getValue() {return value;}
public void setValue(Object value) {this.value = value;}
}
}
写个 main 办法测试一下
public static void main(String[] args) throws InterruptedException {MapCacheDemo mapCacheDemo = new MapCacheDemo();
mapCacheDemo.add("uid_10001", "{1}", 5 * 1000);
mapCacheDemo.add("uid_10002", "{2}", 5 * 1000);
System.out.println("从缓存中取出值:" + mapCacheDemo.get("uid_10001"));
Thread.sleep(5000L);
System.out.println("5 秒钟过后");
// 5 秒后数据主动革除了
System.out.println("从缓存中取出值:" + mapCacheDemo.get("uid_10001"));
}
2、谷歌 guava cache 缓存框架
2.1、简介
Guava Cache 是一个内存缓存模块,用于将数据缓存到 jvm 内存中,是单个利用运行时的本地缓存,他不将数据放到文件或内部服务器。
2.2 简略应用
/**
* @author yinfeng
* @description guava 测试,https://github.com/google/guava
* @since 2022/2/8 14:13
*/
public class GuavaCacheDemo {public static void main(String[] args) throws ExecutionException {
// 缓存接口这里是 LoadingCache,LoadingCache 在缓存项不存在时能够主动加载缓存
LoadingCache<String, User> userCache
//CacheBuilder 的构造函数是公有的,只能通过其静态方法 newBuilder()来取得 CacheBuilder 的实例
= CacheBuilder.newBuilder()
// 设置并发级别为 8,并发级别是指能够同时写缓存的线程数
.concurrencyLevel(8)
// 设置写缓存后 8 秒钟过期
.expireAfterWrite(8, TimeUnit.SECONDS)
// 设置写缓存后 1 秒钟刷新
.refreshAfterWrite(1, TimeUnit.SECONDS)
// 设置缓存容器的初始容量为 10
.initialCapacity(10)
// 设置缓存最大容量为 100,超过 100 之后就会依照 LRU 最近虽少应用算法来移除缓存项
.maximumSize(100)
// 设置要统计缓存的命中率
.recordStats()
// 设置缓存的移除告诉
.removalListener(notification -> System.out.println(notification.getKey() + "被移除了,起因:" + notification.getCause()))
//build 办法中能够指定 CacheLoader,在缓存不存在时通过 CacheLoader 的实现主动加载缓存
.build(new CacheLoader<String, User>() {
@Override
public User load(String key) {System.out.println("缓存没有时,从数据库加载" + key);
// TODO jdbc 的代码~~ 疏忽掉
return new User("yinfeng" + key, key);
}
}
);
// 第一次读取
for (int i = 0; i < 20; i++) {User user = userCache.get("uid" + i);
System.out.println(user);
}
// 第二次读取
for (int i = 0; i < 20; i++) {User user = userCache.get("uid" + i);
System.out.println(user);
}
System.out.println("cache stats:");
// 最初打印缓存的命中率等 状况
System.out.println(userCache.stats().toString());
}
@Data
@AllArgsConstructor
public static class User implements Serializable {
private String userName;
private String userId;
@Override
public String toString() {return userId + "---" + userName;}
}
}
5、分布式缓存
5.1 redis
5.1.1 介绍
Redis 是一个开源的应用 C 语言缩写、反对网络、可基于内存亦可长久化的日志型,Key-Value 数据库,并提供多种语言的 API。
实质是客户端 - 服务端应用软件程序。
特点是应用简略, 性能强悍, 性能利用场景丰盛。
5.1.2通用命令
命令 | 作用 |
---|---|
DEL key | 用于在 key 存在是删除 key |
DUMP key | 序列化给定的 key,并返回给定的值 |
EXISTS key | 查看给定 key 是否存在 |
EXPIRE key seconds | 为给定 key 设置过期工夫,单位秒 |
TTL key | 以秒为单位,返回给定 key 的残余生存工夫 |
TYPE key | 返回 key 所存储的值的类型 |
5.1.3 数据结构
1. String
定义
String 数据结构是简略的 key-value 类型,value 其实不仅是 String,也能够是数字。
应用场景:微博数,粉丝数(惯例计数)
常用命令
命令 | 作用 |
---|---|
Get | 获取指定 key 的值 |
Set | 设置指定 key 的值 |
Incr | 将 key 中贮存的数字值增一 |
Decr | 将 key 中贮存的数字值减一 |
Mget | 获取所有 (一个或多个) 给定 key 的值 |
2. List
定义
List 就是链表,依赖于链表构造
应用场景:微博的关注列表,粉丝列表
常用命令
命令 | 作用 |
---|---|
Lpush | 将一个或多个值插入到列表头部 |
Rpush | 在列表中增加一个或多个值 |
Lpop | 移出并获取列表的第一个元素 |
Rpop | 移除列表的最初一个元素, 返回值为移除的元素 |
Lrange | 获取所有 (一个或多个) 给定 key 的值 |
3. Set
定义
Set 就是一个汇合,汇合的概念就是一堆不反复值的组合。利用 Reds 提供的 Set 数据结构,能够存储一些汇合性的数据。
应用场景:实现如独特关注,独特爱好,二度好友
常用命令
命令 | 作用 |
---|---|
Lpush | 向汇合中增加一个或多个成员 |
Rpush | 移除并返回汇合中的一个随机元素 |
Lpop | 返回汇合中的所有成员 |
Rpop | 返回所有给定汇合的并集 |
4. Sorted set
定义
Sorted set 的应用场景与 set 相似,区别是 set 不是主动有序的,而 sorted set 能够通过用户额定提供一个优先级(score)的参数来为成员排序,并且是插入有序的,即主动排序。
应用场景:排行榜、依照用户投票和工夫排序
常用命令
命令 | 作用 |
---|---|
Zadd | 向有序汇合增加一个或多个成员, 或者更新已存在成员的分数 |
Zrange | 通过索引区间返回有序汇合中指定区间内的成员 |
Zrem | 移除有序汇合中的一个或多个成员 |
Zcard | 获取有序汇合的成员数 |
5. Hash
定义
Hash 是一个 sting 类型的 field 和 value 的映射表
应用场景:存储局部变更数据,如用户信息
常用命令
命令 | 作用 |
---|---|
Zadd | 获取存储在哈希表中指定字段的值 |
Zrange | 将哈希表 key 中的字段 field 的值设为 value |
Hgetall | 获取在哈希表中指定 key 的所有字段和值 |
6. GEO
定义
GEO3.2 版本开始对 GEO(地理位置)的反对
应用场景:LBS 利用开发
常用命令
命令 | 作用 |
---|---|
GEOADD | 减少地理位置的坐标, 能够批量增加地理位置 |
GEODIST | 获取两个地理位置的间隔 |
GEOHASH | 获取某个地理位置的 geohash 值 |
GEOPOS | 获取指定地位的坐标, 能够批量获取多个地理位置的坐标 |
GEORADIUS | 依据给定地理位置坐标获取指定范畴内的地理位置汇合(留神: 该命令的中心点由输出的经度和结度决定) |
GEORADIUSBYMEMBER | 依据给定成员的地位获取指定范畴内的地位信息汇合(留神: 该命令的中心点足由给定的地位元素决定) |
7. Stream
定义
Stream5.0 版本开始的新构造“流”
应用场景: 消费者生产者场景(相似 MO)
常用命令
命令 | 作用 |
---|---|
XADD | 减少地理位置的坐标, 能够批量增加地理位置 |
XLEN | stream 流中的音讯数量 |
XDEL | 删除流中的音讯 |
XRANGE | 返回流中满足给定 ID 范畴的音讯 |
XREAD | 从一个或者多个流中读取音讯 |
XINFO | 检索对于流和关联的消费者组的不同的信息 |
5.1.4 长久化机制
1. 介绍
redis 的数据都寄存在内存中,如果没有配置长久化,重启后数据就全失落,于是须要开启 redis 的长久化性能,将数据保留到磁盘上,当 redis 重启后, 能够从磁盘中复原数据
2. 长久化形式
- RDB 长久化:RDB 长久化形式可能在指定的工夫距离对你的数据进行快照存储
- AOF 长久化:AOF 长久化形式记录每次对服务器写的操作, 当服务器重后的时候会从新执行这些命令来复原原始的数据
3.RDB 形式
客户端间接通过命令 BGSAVE 或者 SAVE 来创立一个内存快照:
- BGSAVE 调用 fork 来创立一个子过程,子过程负责将快照写入磁盘, 而父过程依然持续解决命令。
- SAVE 执行 SAVE 命令过程中, 不再响应其余命令。
在 redis.conf 中调整 save 配置选项,当在规定的工夫内,redis 产生了写操作的个数满足条件会触发 BGSAVE 命令
#900 秒之内至多一次写操作
save 900 1
#300 秒之内至多产生 10 次写操作
save 300 10
优缺点
长处 | 毛病 |
---|---|
对性能影响最小 | 同步时失落数据 |
RDB 文件进行数据恢复比应用 AOF 要快很多 | 如果数据集十分大且 CPU 不够强(比方单核 CPU),Redis 在 fork 子过程时可能会耗费绝对较长的工夫, 影响 RediS 对外提供服务的能力 |
4. AOF 长久化形式
开启 AOF 长久化
appendonty yes
AOF 策略调整
# 每次有数据批改产生时都会写入 AOF 文件
appendfsync always
#每秒钟同步一次, 该策略为 AOF 的默认策略
appendfsync everysec
#从不同步。高效然而数据不会被长久化
appendfsync no
长处
长处 | 毛病 |
---|---|
最平安 | 文件体积大 |
容灾 | 性能耗费比 RDB 高 |
易读, 可批改 | 数据恢复速度比 RDB 慢 |
5.1.5 内存治理
1、内存调配
不同数据类型的大小限度:
Strings 类型:一个 Strings 类型的 Value 最大能够存储 512M。
Lists 类型:list 的元素个数最多为 2^32- 1 个
Sets 类型:元素个数最多为 2^32- 1 个
Hashes 类型:键值对个数最多为 2^32- 1 个
最大内存管制:
maxmemory 最大内存阈值
maxmemory-policy 达到阈值的执行策略
2、内存压缩
# 配置字段最多 512 个
hash-max-zipmap-entries 512
#配置 value 最大为 64 字节
hash-max-zipmap-value 64
#配置元素个数最多 512 个
lst-max-zipmap-entries 512
#配置 value 最大为 64 字节
list-max-zipmap-value 64
#配置元素个数最多 512 个
set-max-zipmap-entries 512
大小超出压缩范畴,溢出后 redis 将主动将其转换为失常大小
3、过期数据的解决策略
被动解决 (redis 被动触发检测 key 足否过期) 每秒抗行 10 次。过程如下:
- 从具备相干过期的 key 汇合中测试 20 个随机 key
- 删除找到的所有已过期 key
- 如果超过 25% 的 key 已过期, 请从步骤 1 从新开始
被动解决:
- 每次拜访 key 的时候,发现超时后被动过期,清理掉
数据恢复阶段过期数据的解决策略:
RDB 形式:过期的 Key 不会被长久化到文件中。载入时过期的 key,会通过 redis 的被动和被动形式清理掉。
AOF 形式:每次遇到过期的 key,redis 会追加一条 DEL 命令到 AOF 文件,也就是说只有咱们程序载入执行 AOF 命令文件就会删除过期的 key
留神:过期数据的计算和计算机本身的工夫是有间接分割的!
Redis 内存回收策略:
配置文件中设置:maxmemory-poIicy noeviction
命令动静调整:config set maxmemory-policy noeviction
回收策略 | 阐明 |
---|---|
noeviction | 客户端尝试执行会让更多内存被应用的命令间接报错 |
allkeys-lru | 在所有 key 里执行 LRU 算法革除 |
volatile-lru | 在所有曾经过期的 key 里执行 LRU 算法革除 |
allkeys-lfu | 在所有 key 里执行 LFU 算法革除 |
volatile-lfu | 在所有曾经过期的 key 里执行 LFU 算法革除 |
allkeys-random | 在所有 key 里随机回收 |
volatile-random | 在曾经过期的 key 里随机回收 |
volatile-ttl | 回收曾经过期的 key,并且优先回收存活工夫 (TTL) 较短的 key |
4、LRU 算法
LRU(最近起码应用):依据数据的历史拜访记录来进行沟汰数据
核心思想:如果数据最近被拜访过,那么未来被拜访的几率也更高。
留神:Redis 的 LRU 算法并非残缺的实现,残缺的 LRU 实现须要太多的内存。
办法:通过对大量 keys 进行取样(50%),而后回收其中一个最好的 key。
配置形式:maxmemory-samples 5
5、LFU 算法
LFU:依据数据的历史拜访频率来沟汰数据
核心思想:如果数据过来被拜访屡次, 那么未来被拜访的频率也更高。
启用 LFU 算法后,能够应用热点数据分析性能。
5.1.6 主从复制
1、介绍
为什么要主从复制
redis-server 单点故障
单节点 QPS 无限
利用场景剖析
读写拆散场景,躲避 redis 单机瓶颈
故障切换,master 出问题后还有 slave 节点能够应用
2、搭建主从复制
主 Redis Server 以一般模式启动,次要是启动从服务器的形式
-
命令行
# 连贯须要实现从节点的 rediS, 执行上面的命令 slaveof [ip] [port]
-
redis.conf 配置文件
# 配置文件中减少 slaveof [ip] [port] #从服务器是否只读(默认 yes) slave-read-only yes
-
退出主从集群的形式
slaveof no one
3、查看主从复制
#redis 客户端执行
info replication
4、主从复制流程
- 从服务器通过 psync 命令发送服务器已有的同步进度(同步源 ID,同步进度 offset)
- master 收到申请,同步源为以后 master,则依据偏移量增量同步
- 同步源非以后 master,则进入全量同步:maser 生成 rdb,传输到 slave,加载到 slave 内存
5、主从复制外围常识
- Redis 默认应用异步复制,slave 和 master 之间异步地确认解决的数据量
- 一个 master 能够领有多个 slave
- Slave 能够承受其余 slave 的连贯。slave 能够有上级 sub slave
- 主从同步过程在 master 侧是非阻塞的
- slave 首次同步须要删除旧数据,加载新数据,会阻塞到来的连贯申请
6、利用场景
- 主从复制能够用来反对读写拆散
- slave 服务器设定为只读,能够用在数据安全的场景下。
- 能够应用主从复制来防止 master 长久化造成的开销。master 敞开长久化,slave 设置为不定期保留或开启 AOF
- 留神:重新启动的 master 程序将从一个空数据集开始, 如果一个 slave 试图与它同步,那么这个 slave 也会被清空。
7、注意事项
-
读写拆散场景:
数据复制延时导致读到过期数据或者读不到数据(网络起因,slave 阻塞)
从节点故障(多个 client 如何迁徙)
-
全量复制状况下:
第一次建设主从关系或者 runid 不匹配会导致全量复制
故障转移的时候也会呈现全量复制
-
复制风暴:
master 故障重启,如果 slave 节点过多,所有 slave 都要复制,对服务器的性能,网络的压力都有很大影响。
如果一个机器部署了多个 master
-
写能力无限
主从复制还是只有一台 master,提供的写服务能力无限
-
master 故障状况下:
如果是 mater 无长久化,Slave 开启长久化来保留数据的场展,倡议不要配置 redis 主动重启。
启动 redis 主动重启,master 启动后,无备份数据,可能导致集群数据失落的状况
-
带有效期的 key:
Slave 不会让 key 过期,而是期待 master 让 key 过期
在 Lua 脚本执行期间,不执行任何 key 过期操作
5.1.7 哨兵模式
1、哨兵 (Sentinel) 机制核心作用
客户端 == 询问主 redis 地址 ==> redis 哨兵 == 监控、揭示、故障转移(主从切换)==> 主 redis(master)== 主从复制关系 ==> 从 redis(slave)
2、外围运作流程
服务发现和健康检查流程
搭建 redis 主从集群 ==> 启动哨兵(客户端通过哨兵发现 Redis 实例信息) ==> 哨兵通过连贯 master 发现主从集群内的所有实例信息 ==> 哨兵监控 redis 实例的健康状况
故障切换流程
哨兵一旦发现 master 不能失常提供服务,则告诉给其余哨兵 ==> 当肯定数量的哨兵都认为 master 挂了 ==> 选举一个哨兵作为故障转移的执行者 ==> 执行者在 slave 中选取一个作为新的 master ==> 将其余 slave 从新设定为新 master 的隶属
3、哨兵如何晓得 Redis 主从信息
哨兵配置文件中,保留着主从集群中 master 的信息,能够通过 info replication 命令,进行主从信息主动发现。
4、什么是主观下线(sdown)
主观下线:单个哨兵本身认为 redis 实例曾经不能提供服务
检测机制:哨兵向 redis 发送 ping 申请,+PONG,-LOADING,-MASTERDOWN 三种状况视为失常,其余回复均视为有效
对应配置文件的配置项:sentinel down-after-milliseconds mymaster 1000
5、什么是主观下线(odown)
主观下线:肯定数量值的哨兵认为 master 曾经下线。
检测机制:当哨兵主观认为 maser 下线后,则会通过 SENTINEL is-master-down-by-addr 命令询问其余哨兵是否认为 master 曾经下线,如果达成共识(达到 quorum 个数),就会认为 master 节点主观下线,开始故障转移流程
对应配置文件的配置项:sentinel monitor mymaster 1.0.0.1 6380 2
6、哨兵之间如何通信
- 哨兵之间的主动发现:公布本人的信息,订阅其余哨兵音讯(pub/sub)
- 哨兵之间通过命令进行通信:直连发送命令
- 哨兵之间通过订阅公布进行通信:互相订阅指定主题(pub/sub)
7、哨兵领导选举机制
基于 Raft 算法实现的选举机制,流程简述如下:
- 拉票阶段:每个哨兵节点心愿本人成为领导者;
- Sentinel 节点收到拉票命令后,如果没有收到或批准过其余 sentinel 节点的申请,就批准该 sentinel 节点的申请(每个 sentinel 只持有一个批准票数)
- 如果 sentinel 节点发现自己的票数曾经超过一半的数值,那么它将成为领导者,去执行故障转移
- 投票完结后,如果超过 failover-timeout 的工夫内,没进行理论的故障转移操作,则从新拉票选举。
8、slave 选举计划
slave 节点状态 > 优先级 > 数据同步状况 > 最小的 run id
9、最终主从切换的过程
针对行将成为 master 的 slave 节点,将其撒出主从集群,主动执行:slaveof NO ONE
针对其余 slave 节点,使它们成为新 master 的隶属,主动执行:slaveof new_master_host new_master_port
10、哨兵服务部署计划
不举荐:一主一从,两个哨兵
举荐:一主两从,三个哨兵
redis 集群非强统一:一主两从, 网络分区下可能呈现数据不统一或失落。
5.1.8 redis 集群分片存储
1、为什么要分片存储
redis 的内存需要可能超过机器的最大内存。(一台机器不够用)
2、官网集群计划
redis cluster 是 redis 的分布式集科解决方案,在 3.0 版本推出后无效地解决了 redis 分布式分面的需要,实现了数据在多个 Redis 节点之间主动分片, 故障主动转移, 扩容机制等性能。
次要基于 CRC16(key) % 16384 计算出每个 key 对应的 slot,而后依据 redis 集群中实例的预设槽 slot(16384 个)进行对应的操作,slot 不存储数据,仅仅用来做片区划分。
#### 3、搭建集群
- 筹备 6 个独立的 redis 服务
- 通过 redis-cli 工具创立集群
- 测验集群
- 故障转移测试
- 集群扩容
- 集群节点删除
4、集群关怀的问题
-
减少了 slot 槽的计算, 是不是比单机性能差?
不是的,为了防止每次都须要服务器计算重定向,优良的 Java 客户端都实现了本地计算,并且缓存服务器 slots 调配,有变动时再更新本地内容,从而防止了屡次重定向带来的性能损耗。
-
redis 集群大小, 到底能够装多少数据?
实践是能够做到 16384 个槽,每个槽对应一个实例,然而 redis 宫方倡议是最大 1000 个实例,因为存储曾经足够大了。
-
集群节点间是怎么通信的?
每个 Redis 群集节点都有一个额定的 TCP 端口,每个节点应用 TCP 连贯与每个其余节点连贯。检测和故障转移这些步骤根本和哨兵模式相似。
-
ask 和 moved 重定向的区别
重定向包含两种状况
若确定 slot 不属于以后节点,redis 会返回 moved。
若以后 redis 节点正在解决 slot 迁徙,则代表此处申请对应的 key 临时不在此节点,返回 ask,通知客户端本次申请重定向。
-
数据歪斜和拜访歪斜的问题
歪斜导致集群中局部节点数据多,压力大。解决方案分为后期和前期:
后期是业务层面提前预测,哪些 key 是热点,在设计的过程中躲避。
前期是 slot 迁徙,尽量将压力摊派(slot 调整有主动 rebalance、reshard 和手动)。
-
slot 手动迁徙怎么做?
- 在迁徙目标节点执行 cluster setslot <slot> IMPORTING <node ID> 命令,指明须要迁徙的 slot 和迁徙源节点。
- 在迁徙源节点执行 cluster setslot <slot> MIGRATING <node ID> 命令,指明须要迁徙的 slot 和迁徙目标节点。
- 在迁徙源节点执行 cluster getkeysinslot 获取该 slot 的 key 列表
- 在迁徙源节点执行对每个 key 执行 migrate 命令,该命令会同步把该 key 迁徙到目标节点。
- 在迁徙源节点重复执行 cluster getkeysinslo 命令,直到该 slot 的列表为空。
- 在迁徙源节点和目标节点执行 cluster setslot <slot> NODE <node ID>,实现迁徙操作。
- 节点之间会替换信息,传递的音讯包含槽的信息,带来带宽耗费。留神:防止应用大的一个集群, 能够分多个集群。
- Pub/Sub 公布订阅机制:对集群内任意的一个节点执行 pubish 公布音讯,这个音讯会在集群中进行流传,其余节点都接管到公布的音讯。
-
读写拆散:
redis-cluster 默认所有 从节点上的读写,都会重定向到 key 对应槽的主节点上。
能够通过 readonly 设置以后连贯可读,通过 readwrite 勾销以后连贯的可读状态。
留神:主从节点仍然存在数据不统一的问题
5.1.9 redis 监控
1、monitor 命令
monitor 是一个调试命令,返回服务器解决的每个命令。对于发现程序的谬误十分有用。出于平安思考,某些非凡治理命令 CONFIG 不会记录到 MONITOR 输入。
留神:运行一个 MONITOR 命令可能升高 50% 的吞吐量,运行多个 MONITOR 命令升高的吞吐量更多。
2、info 命令
INFO 命令以一种易于了解和浏览的格局,返回对于 Redis 服务器的各种信息和统计数值。
info 命令 | 返回信息 |
---|---|
server | Redis 服务器的个别信息 |
clients | 客户端的连贯局部 |
memory | 内存耗费相干信息 |
persistence | 长久化相干信息 |
stats | 个别统计 |
replication | 主 / 从复制信息 |
cpu | 统计 CPU 的耗费 |
commandstats | Redis 命令统计 |
cluster | Redis 集群信息 |
keyspace | 数据库的相干统计 |
能够通过 section 返回局部信息,如果没有应用任何参数时,默认为 detault。
3、图形化监控工具:Redis-Live
5.2 memcached 入门
因为 memcached 缓缓淡出了人们的视线,应用的公司越来越少,所以这里只是做个入门介绍。
1、简介
是一个收费开源的、高性能的、具备分布式内存对象的缓存零碎,它通过加重数据库负载减速动静 web 利用。
实质上就是一个内存 key-Value 缓存
协定简略,应用的是基于文本行的协定
不反对数据的长久化,服务器敞开之后数据全副失落
Memcached 简洁而弱小,便于疾速开发,上手较为容易
没有平安机制
2、设计理念
- 简略的键 / 值存储:服务器不关怀你的数据是什么样的,只管数据存储
-
服务端性能简略,很多逻辑依赖客户端实现
客户端专一如何抉择读取或写入的服务器,以及无奈分割服务器时要执行的操作。
服务器专一如何存储和治理何时革除或重用内存
- Memcached 实例之间没有通信机制
- 每个命令的复杂度为 0(1):慢速机器上的查问应该在 1ms 以下运行。高端服务器的吞吐量能够达到每秒数百万
- 缓存主动革除机制
- 缓存生效机制
3、常用命令
分组 | 命令 | 形容 |
---|---|---|
存储命令 | set | 用于将 value 存储在指定的 key 中。key 曾经存在,更新该 key 所对应的原来的数据。 |
add | 用于将 value 存储在指定的 key 中,存在则不更新。 | |
replace | 替换已存在的 key 的 Value,不存在,则替换失败。 | |
append | 用于向已存在 key 的 value 前面追加数据 | |
prepend | 向已存在 key 的 value 后面追加数据 | |
cas | 比拟和替换,比对后,没有被其余客户端批改的状况下能力写入。 | |
检索命令 | get | 获取存储在 key 中的 value,不存在,则返回空。 |
gets | 获取带有 CAS 令牌存的 value,若 key 不存在,则返回为空 | |
删除 | delete | 删除已存在的 key |
计算 | incr/decr | 对已存在的 key 的数字值进行自增或自减操作 |
统计 | stats | 返回统计信息如 PID(过程号)、版本号、连接数等 |
stats items | 显示各个 slab 中 item 的数目和存储时长(最初一次拜访间隔当初的秒数) | |
stats slabs | 显示各个 slab 的信息,包含 chunk 的大小、数目、应用状况等。 | |
stats sizes | 显示所有 item 的大小和个数 | |
革除 | flush_all | 革除所有内容 |
4、客户端应用
客户端反对的个性:集群下多服务器抉择,节点权重配置,失败 / 故障转移,数据压缩,连贯治理
5、服务端配置
-
命令行参数
查看 memcached- h 或 man memcached 获取最新文档
-
init 脚本
如果通过 yum 利用商店装置,能够应用 /etc/sysconfig/memcached 文件进行参数配置
-
查看运行配置
stats settings 查看运行中的 memcached 的配置
6、memcached 性能
Memcached 性能的要害是硬件,外部实现是 hash 表,读写操作都是 0(1)。硬件好,几百万的 OPS 都是没问题的。
最大连接数限度 :外部基于事件机制(相似 JAVA NIO) 所以这个限度和 nio 相似,只有内存,操作系统参数进行调整,轻松几十万。
集群节点数量限度:实践是没限度的,然而节点越多,客户端须要建设的连贯就会越多。
留神:memcached 服务端没有分布式的性能,所以不论是集群还是主从备份,都须要第三方产品反对。
7、服务器硬件须要
CPU 要求:CPU 占用率低,默认为 4 个工作线程
内存要求:
memcached 内容存在内存外面,所有内存使用率高。
倡议 memcached 实例独占服务器, 而不是混用。
倡议每个 memcached 实例内存大小都足统一的,如果不统一则须要进行权重调整
网络要求:
依据我的项目传输的内容来定,网络越大越好,尽管通常 10M 就够用了
倡议: 我的项目往 memcached 传输的内容放弃尽可能的小
8、Memcached 利用场景
- 数据查问缓存:将数据库中的数据加载到 memcached,提供程序的访问速度
- 计数器的场景:通过 incr/decr 命令实现评论数量、点击数统计,操作次数等等场景。
- 乐观锁实现:例如打算工作多实例部暑的场景下,通过 CAS 实现不反复执行
- 避免反复解决:CAS 命令
5.3 互联网高并发缓存架构
5.3.1 缓存架构剖析图
5.3.2 缓存雪崩
定义:因为缓存服务挂掉或者热点缓存生效,从而导致所有申请都去查数据库,导致数据库连贯不够用或者数据库解决不过去,从而导致整个零碎不可用。
罕用解决方案:
- 缓存数据的过期工夫设置随机,避免同一时间大量数据过期景象产生。
- 缓存降级,间接返回错误码;
- 加锁实现避免大量申请堆到数据库。
- 设置热点数据永远不过期,避免了主动生效的状况,通过其余后盾检查程序,避免缓存数据和数据库长期不同步
5.3.2 缓存击穿
定义:查问必然不存在的数据,申请透过 Redis,直击数据库。
罕用解决方案:
- 用户内容预生成。
- 拜访频率限度。
- 缓存中无数据,也不查询数据库,间接返回错误码。
- 布隆过滤器