缓存雪崩防范-一致性hash
2023-06-13 09:14:05 时间
什么是缓存雪崩
当缓存服务器重启或者大量缓存集中在某一个时间段失效,这样在失效的时候,也会给后端系统(比如DB)带来很大压力。
什么是一致性hash
一致性hash可以将set进redis集群的key均匀得分布在redis集群中,某一节点宕机不会导致大面积缓存失效。现在我们假设有100台redis data服务器,一份数据101进来的时候,以散列公式hash(i)&100,计算所存放的服务器,假设hash(i) = i,那么数据被散列到标号为1的服务器,然后这个时候服务器新增了一台,然后散列公式为hash(i)%101,这个时候请求访问数据101的时候,被分配至0号服务器,但是其实这个时候数据是在1号服务器的,所以这个时候大量的数据失效了。
先将集群机器使用hash函数散列到一个2^32个点的环中,在采用一致性哈希算法的分布式集群中将新的机器加入,其原理是通过使用与对象存储一样的Hash算法将机器也映射到环中(一般情况下对机器的hash计算是采用机器的IP或者机器唯一的别名作为输入值),然后以顺时针的方向计算,将所有对象存储到离自己最近的机器中。假设现在有NODE1,NODE2,NODE3三台机器,通过Hash算法得到对应的KEY值,映射到环中,其示意图如下:
Hash(NODE1) = KEY1;
Hash(NODE2) = KEY2;
Hash(NODE3) = KEY3;
优点:在增删机器时只需要移动少部分数据即可
虚拟节点
我们引入虚拟节点来解决负载不均衡的问题,可参考以下链接。
一致性Hash(Consistent Hashing)原理剖析
谷歌封装的一致性hash代码
import com.google.common.hash.Hashing;
...
List<String> servers = Arrays.asList("redis服务器A", "redis服务器B", "redis服务器C");
int bucket1 = Hashing.consistentHash(Hashing.md5().hashString("18612345678", Charsets.UTF_8), servers.size());
int bucket2 = Hashing.consistentHash(Hashing.md5().hashString("18600000000", Charsets.UTF_8), servers.size());
int bucket3 = Hashing.consistentHash(Hashing.md5().hashString("13512345678", Charsets.UTF_8), servers.s
System.out.println(servers.get(bucket1));
System.out.println(servers.get(bucket2));
System.out.println(servers.get(bucket3));
...
自定义一致性hash代码
import com.google.common.base.Charsets;
import com.google.common.hash.HashFunction;
import com.google.common.hash.Hashing;
import java.util.ArrayList;
import java.util.Collection;
import java.util.SortedMap;
import java.util.TreeMap;
/**
* 缓存雪崩防止-一致性hash
* Created by zhangluncong on 2018/5/24.
*/
public class ConsistentHash<T> {
/**
* 所用的hash函数
*/
private final HashFunction hashFunction;
/**
* server虚拟节点倍数(100左右比较合理)(虚拟节点=物理节点*numberOfReplicas)
*/
private final int numberOfReplicas;
/**
* server节点分布圆
*/
private final SortedMap<Integer, T> circle = new TreeMap<Integer, T>();
/**
* 初始化一致性hash算法
*
* @param hashFunction
* @param numberOfReplicas
* @param nodes server物理节点集合
*/
private ConsistentHash(HashFunction hashFunction, int numberOfReplicas, Collection<T> nodes) {
this.hashFunction = hashFunction;
this.numberOfReplicas = numberOfReplicas;
for (T node : nodes) {
add(node);
}
}
/**
* 加入server物理节点散列成的所有虚拟节点
*
* @param node
*/
public void add(T node) {
for (int i = 0; i < numberOfReplicas; i++) {
circle.put(hashFunction.hashString(node.toString() + i, Charsets.UTF_8).asInt(), node);
}
}
/**
* 移除server物理节点散列成的所有虚拟节点
*
* @param node
*/
public void remove(T node) {
for (int i = 0; i < numberOfReplicas; i++) {
circle.remove(hashFunction.hashString(node.toString() + i, Charsets.UTF_8).asInt());
}
}
/**
* 获取client对应server物理节点
*
* @param key 虚拟节点
* @return 物理节点
*/
public T get(Object key) {
if (circle.isEmpty()) {
return null;
}
//生成client对应的hash值
int hash = hashFunction.hashString(key.toString(), Charsets.UTF_8).asInt();
//如果没有对应此hash的server节点,获取大于等于此hash后面的server节点;如果还没有,则获取server节点分布圆的第一个节点
if (!circle.containsKey(hash)) {
SortedMap<Integer, T> tailMap = circle.tailMap(hash);
hash = tailMap.isEmpty() ? circle.firstKey() : tailMap.firstKey();
}
System.out.println(hash);
return circle.get(hash);
}
/********************** 测试代码**********start ************************/
public static void main(String[] args) {
HashFunction hf = Hashing.md5();
//物理服务器,可以是ip地址等
ArrayList<String> nodeList = new ArrayList<String>();
nodeList.add("redis服务器A");
nodeList.add("redis服务器B");
nodeList.add("redis服务器C");
ConsistentHash<String> consistentHash = new ConsistentHash<String>(hf, 100, nodeList);
//根据一致性hash算法获取客户端对应的服务器节点
System.out.println(consistentHash.get("18612345678"));
System.out.println(consistentHash.get("18600000000"));
System.out.println(consistentHash.get("13512345678"));
}
}
相关文章
- 吊打字节面试官,CPU缓存一致性协议MESI
- 如何保证数据库和缓存双写一致性?
- springboot+canal+kafka+mysql 别再问缓存一致性问题怎么解决了!!
- 数据库跟缓存的双写一致性
- ue4在c盘的缓存_怎么清除ps在C盘的缓存
- ABB 5SXE04-0150 GVC707AE01 接近性允许缓存一致性电路
- 12 张图看懂 CPU 缓存一致性与 MESI 协议,真的一致吗?
- 面试常问:如何保证Redis缓存和数据库的数据一致性
- 分布式之数据库和缓存双写一致性方案解析详解架构师
- Redis缓存实现强一致性(redis缓存一致性)
- 缓存Redis Java实现自动过期缓存(redisjava过期)
- Redis实现缓存刷新,加速数据查询(redis刷新缓存)
- Redis: 传智播客推出的缓存利器(redis 传智播客)
- 性本地缓存与Redis实现一致性方案(本地缓存与redis一致)
- 前端优化之路利用Redis实现缓存加速(前端使用redis缓存)
- Redis强大的数据库缓存系统(介绍一下 redis)
- Redis 闪退后如何找回丢失的缓存(redis 闪退找回缓存)
- 级缓存Redis重量级缓存,提升数据处理能力(redis重量)
- Redis记录最长的缓存时光(redis 记录 长度)
- 学习Redis缓存,多久才能精通(redis缓存要学多久)
- 探索Redis缓存的多种方式(redis缓存的几种方法)
- Redis缓存保证强一致性(Redis缓存强一致性)