분산 캐시 일관성 해싱 알고리즘, 철저하게 기사를 홍보하기!

이 문서에서는 프로그래머 달력 개밀 기여자에서 온다

이 달력 Xiaobing 학생들을 공유 주셔서 감사합니다


분산 캐시의 분야에서 memcached를 일관된 해싱 알고리즘, Nginx와의 RPC 프레임 워크 및로드 밸런싱 다양한 분야의 응용 프로그램의 넓은 범위가

그것은 당신이 키를 매핑 할 슬롯의 수를 추가하는 기존의 해시 함수 해시 테이블 후 문제를 해결하기 위해 주로이다.

이 문서는 일관된 해싱 알고리즘과 그 구현의 원리를 소개하고 서로 다른 해시 함수는 달성 실현의 데이터 단편화 레디 스 클러스터를 탐험하는 데이터의 성능 비교를 제공합니다, 텍스트의 끝 구현 특정 주소 github의를 제공 할 것입니다.

memcached를 클라이언트 분산 캐시

Memcached가가 각 서버가 서로 통신하지 않습니다, 고성능 분산 캐싱 시스템, 그러나, 배포되지 않은 서버 기능입니다.

구현시켜 Memcached의 주요 기능입니다 라이브러리 클라이언트에 따라 분산 것. 예를 들어, 타사는 일관된 해싱 알고리즘을 기반으로 자사의 분산 캐시 기능을 달성하기 위해 클라이언트를 spymemcached.

먼저지도, 시각적 경험 :

다음과 같이 구체적인 단계는 다음과 같습니다

  • 키 값에 대응하는 키 알고리즘에 따른 계산 서버의 첫 번째 클라이언트에 데이터를 추가로 memcached.

  • 서버를 선택한 후, 캐시 된 데이터를 보유.

  • 동일한 키에 대한 데이터를 획득 할 때 데이터를 획득하기 위해, 알고리즘은 동일한 서버에 클라이언트를 찾을 수있다.

이 과정에서, 클라이언트는 최초 오프라인 또는 온라인이 존재, 데이터 마이그레이션 될 이주 될 필요가있는 데이터의 양을 최소화 할 때, 각각의 서버에서 가능한 한 균일 분산 알고리즘 캐시 데이터는 개별 서버 따라 확인한다.

그 중, 클라이언트 알고리즘은 클라이언트 분산 캐시 성능의 장점과 단점의 열쇠입니다.

해시 값을 계산하기 위해 통상의 해시 테이블 알고리즘 후 다른 서버 상에 매핑 된 키 값의 동작을 인계하여, 일반적

그러나 서버의 수가 변경 될 때, 모듈로 제수의 동작을 변경, 수신되지 않는 거의 항상 매핑 된 키 서버의 모든 변경, 분산 캐싱 시스템입니다.

일관된 해싱 알고리즘은 캐시 서버 마이그레이션 변화의 수가 발생 최소화합니다.

해시 알고리즘

첫째, 일관된 해싱 알고리즘은 보통 해시 알고리즘에 의존한다. 해시 알고리즘의 이해 학생들의 대부분은 JDK에 머물렀던 수 있습니다 hashCode기능에.

사실, 다른 시나리오는 서로 다른 해시 알고리즘을 사용할 수 있습니다를 들어, 서로 다른 측면에서 자신의 장점과 단점을 가지고를 달성하기 위해 많은 해시 알고리즘이 있습니다.

아래, 우리는 몇 가지 일반적인 해싱 알고리즘을 소개합니다, 그들은 분포의 균일 성, 해시 충돌과 성능의 가능성의 측면에서 장단점을 확인합니다.

MD5 알고리즘 : 전체 이름 메시지 다이제스트 알고리즘 5, 완전하고 일관된 정보 전송을 보장합니다. 그것은 널리 사용되는 컴퓨터, 주류 프로그래밍 언어는 일반적으로 MD5 구현을 해시 알고리즘 중 하나입니다.

MD5는 압축 형식으로 비밀 대용량 정보의 역할 (바이트는 16 진수의 고정 된 길이 스트링으로 임의의 길이의 문자열이다)이다. 일반적인 파일 무결성 검사는 MD5를 사용하는 것입니다.

CRC 알고리즘 : 전체 이름 CyclicRedundancyCheck의 순환 중복 검사의 중국 이름. 그것은 부호화 및 복호화 방법의 중요한 클래스가 간단하고, 통신 에러 검출 및 정정 능력 해싱 알고리즘은 널리 에러 제어를 달성하기 위해 사용된다.

MurmurHash 알고리즘 : 2008 년 오스틴 애플비에 의해 만들어진 높은 컴퓨팅 성능과 낮은 충돌 속도는,,, 하둡으로 된 libstdc ++, nginx를, libmemcached 및 기타 오픈 소스 시스템을 적용하고있다.

레디 스, Memcached가, 카산드라, HBase를, 루씬과 구아바 자바 커뮤니티를 사용하고 있습니다.

FNV 알고리즘 : 파울러 - NOLL-Vo에 알고리즘의 전체 이름은 세 발명가 글렌 파울러, 랜던 커트 놀, 퐁 기반으로 Vo에 후 이름, 먼저 1991 년에 제안되었다.

FNV는 URL, 호스트 이름, 파일 이름, 텍스트 및 IP 주소로, 고 분산 매우 유사한 해시 문자열에 적합하게 작은 충돌 속도를 빠르게 많은 양의 데이터를 해시 및 유지 관리.

Ketama 알고리즘 : 하나 그냥 단지 해시 맵의 기능을 대체 일관된 해싱 알고리즘, 다른 보편적 인 해시 알고리즘 일관된 해싱 알고리즘을 달성하기 위해,하지만 Ketama는 프로세스의 집합입니다, 우리는 것입니다 다시 소개.

일관된 해싱 알고리즘

아래, 우리는, 예를 들어 캐시 시나리오를 분산 일관된 해싱 알고리즘 링의 원리를 분석 할 수 있습니다.

먼저, 캐시 서버 (IP + 포트 번호) 해시 링의 노드로 맵핑되며, 동일한 고리에 매핑 해시 키 데이터 캐시 키 값을 계산하고, 시계 방향으로는 같은 가장 가까운 캐시 서버 노드를 선택 그것은 서버에 저장해야합니다. 특정 구현 다음 장을 참조하십시오.

이 때 예를 들어, A, B는 C, D 네 개의 캐쉬 서버는, 도시 한 바와 같이 일관된 해싱 링상의 위치 인 캐시 데이터 및 키 값은 서버에있어서 시계 방향의 가장 가까운 노드로 반올림 규칙은 캐시 데이터는 서버 B에 저장해야

키는 다음과 같이 데이터 버퍼 (4), 및 일관된 해싱 고리상의 위치에 저장하고, 그렇게되면 이것은 C. 서버에 저장해야

유사하게, 키 값 데이터 5,6- D의 서버에 존재한다, 키 값 데이터는 7,8 A. 서비스에 저장 될

이 경우, 서버 B 다운 오프라인, 서버 B에 저장된 캐시 데이터를 마이그레이션하려면,하지만 일관된 해싱 링 때문에, 데이터 만 1의 키 값을 마이그레이션해야, 다른 데이터 스토리지 서버가 발생하지 않습니다 변경합니다. 이것은 또한 매핑 알고리즘 뛰어난 장소의 나머지 부분을보다 일관된 해싱 알고리즘입니다.

C. 데이터를 이전하는 서버에 저장하도록 서버 B 오프라인 때문에, 시계 방향 (1)의 키 데이터 값은 가장 가까운 서버는 C이고,

현실 일관된 해싱 고리상의 서버 위치는 매우 균일 실제로 고리상의 각 노드가 차지하는 크기의 범위에서 얻어진 분산 될 수 없다.

이 경우, 더미 노드 해결하기 위해 증가 될 수있다. 더미 노드를 증가시킴으로써되도록 "관할"균일의 링 영역에 각 노드.

이 변화하면서 동시에 노드 변경 데이터의 분포에 가능한 최소의 영향, 데이터의 균일 한 분포를 보장 할 때되도록 아닙니다.

실현

우리는 Memcached가 분산 캐싱 시나리오에서 일관된 해싱 알고리즘을 달성하고, 특정 테스트 성능 데이터를 제공합니다 아래.

이 구현은 kiritomoe 보웬 spymemcached 구현과 클라이언트 코드를 그립니다. 나는 특정 구현 GitHub의 주소를 보면

https://github.com/ztelur/consistent-hash-algorithm.

NodeLocator일관된 해싱 알고리즘은 그것이 가지고 추상적 분산 캐싱 시나리오 getPrimary, 데이터 버퍼의 키 값을 수신하는 기능을 캐시 서버 인스턴스의 저장 데이터를 출력한다.

여기에서 사용하는 일반 일관된 해싱 알고리즘 인 TreeMap데이터 구조 일관된 해싱 고리로 ceilingEntry고리에 가까운 노드를 획득하기 위해 기능한다.

buildConsistentHashRing 일관된 해싱 링을 구성하는 과정에 포함 된 기능, 12 기본 가상 노드에 합류했다.

public class ConsistentHashNodeLocator implements NodeLocator {    private final static int VIRTUAL_NODE_SIZE = 12;    private final static String VIRTUAL_NODE_SUFFIX = "-";    private volatile TreeMap<Long, MemcachedNode> hashRing;    private final HashAlgorithm hashAlg;    public ConsistentHashNodeLocator(List<MemcachedNode> nodes, HashAlgorithm hashAlg) {        this.hashAlg = hashAlg;        this.hashRing = buildConsistentHashRing(hashAlg, nodes);    }    @Override    public MemcachedNode getPrimary(String k) {        long hash = hashAlg.hash(k);        return getNodeForKey(hashRing, hash);    }    private MemcachedNode getNodeForKey(TreeMap<Long, MemcachedNode> hashRing, long hash) {        /* 向右找到第一个key */        Map.Entry<Long, MemcachedNode> locatedNode = hashRing.ceilingEntry(hash);        /* 想象成为一个环,超出尾部取出第一个 */        if (locatedNode == null) {            locatedNode = hashRing.firstEntry();        }        return locatedNode.getValue();    }    private TreeMap<Long, MemcachedNode> buildConsistentHashRing(HashAlgorithm hashAlgorithm, List<MemcachedNode> nodes) {        TreeMap<Long, MemcachedNode> virtualNodeRing = new TreeMap<>();        for (MemcachedNode node : nodes) {            for (int i = 0; i < VIRTUAL_NODE_SIZE; i++) {                // 新增虚拟节点的方式如果有影响,也可以抽象出一个由物理节点扩展虚拟节点的类                virtualNodeRing.put(hashAlgorithm.hash(node.getSocketAddress().toString() + VIRTUAL_NODE_SUFFIX + i), node);            }        }        return virtualNodeRing;    }}复制代码

에서 getPrimary함수, 이는 먼저 사용되는 HashAlgorithm키 값에 대응하는 해시 값을 계산 한 다음 호출 getNodeForKey에서 기능 TreeMap취득한 최신 서버 노드 해당 인스턴스.

HashAlgorithm 추상 해시 알고리즘은 일관된 해싱 알고리즘 등등 CRC, MurmurHash FNV와 같은 일반적인 해시 알고리즘의 다양한 사용할 수 있습니다.

아래, 우리는 차이가 가져 오는 성능을 달성하기 위해 다양한 해싱 알고리즘을 비교합니다.

성능 테스트

테스트 데이터는 알고리즘의 진정한 품질, 사고의 양적 모드 여야 평가하는 가장 효과적인 방법이며, 마법 프로그래머 고급의 하나입니다.

해시 함수에 따라 서로 다른 해시 알고리즘의 일관성의 평가를 위해 다음과 같은 네 가지 정량적 지표를 가져 가라.

  • 각 캐쉬 서버에 저장된 통계 노드의 개수, 분산, 표준 편차를 계산

    버퍼 분포를 측정, 우리는 서버 (100), 테스트 편차 및 표준 편차의 양이 저장된 최종 노드 캐시 데이터에 할당 50000 개 캐시 데이터를 시뮬레이션 할 수있다.

  • 임의 서버의 조립 라인 10 % 오프, 캐시, 캐시 통계의 이동 속도를 재 할당 .

    경우에 오프라인 측정 노드, 우리는 임의의 오프 후에 지정된 서버 (100), 서버 (10)에 할당 된 50,000 캐시 데이터를 시뮬레이션 50,000 데이터를 재 할당 할 수있는 통계는 이전의, 즉 비, 다른 서버의 비율에 할당 된 버퍼 .

  • 다른 해싱 알고리즘의 효율성의 JMH 사용은 비교된다 .

다음과 같은 특정 평가 알고리즘이다.

public class NodeLocatorTest {    /**     * 测试分布的离散情况     */    @Test    public void testDistribution() {        List<MemcachedNode> servers = new ArrayList<>();        for (String ip : ips) {            servers.add(new MemcachedNode(new InetSocketAddress(ip, 8080)));        }        // 使用不同的DefaultHashAlgorithm进行测试,得出不同的数据        NodeLocator nodeLocator = new ConsistentHashNodeLocator(servers, DefaultHashAlgorithm.NATIVE_HASH);        // 构造 50000 随机请求        List<String> keys = new ArrayList<>();        for (int i = 0; i < 50000; i++) {            keys.add(UUID.randomUUID().toString());        }        // 统计分布        AtomicLongMap<MemcachedNode> atomicLongMap = AtomicLongMap.create();        for (MemcachedNode server : servers) {            atomicLongMap.put(server, 0);        }        for (String key : keys) {            MemcachedNode node = nodeLocator.getPrimary(key);            atomicLongMap.getAndIncrement(node);        }        System.out.println(StatisticsUtil.variance(atomicLongMap.asMap().values().toArray(new Long[]{})));        System.out.println(StatisticsUtil.standardDeviation(atomicLongMap.asMap().values().toArray(new Long[]{})));    }    /**     * 测试节点新增删除后的变化程度     */    @Test    public void testNodeAddAndRemove() {        List<MemcachedNode> servers = new ArrayList<>();        for (String ip : ips) {            servers.add(new MemcachedNode(new InetSocketAddress(ip, 8080)));        }        //随机下线10个服务器, 先shuffle,然后选择0到90,简单模仿随机计算。        Collections.shuffle(servers);        List<MemcachedNode> serverChanged = servers.subList(0, 90);        NodeLocator loadBalance = new ConsistentHashNodeLocator(servers, DefaultHashAlgorithm.NATIVE_HASH);        NodeLocator changedLoadBalance = new ConsistentHashNodeLocator(serverChanged, DefaultHashAlgorithm.NATIVE_HASH);        // 构造 50000 随机请求        List<String> keys = new ArrayList<>();        for (int i = 0; i < 50000; i++) {            keys.add(UUID.randomUUID().toString());        }        int count = 0;        for (String invocation : keys) {            MemcachedNode origin = loadBalance.getPrimary(invocation);            MemcachedNode changed = changedLoadBalance.getPrimary(invocation);           // 统计发生变化的数值            if (!origin.getSocketAddress().equals(changed.getSocketAddress())) count++;        }        System.out.println(count / 50000D);    }    static String[] ips = {...};}复制代码

JMH 테스트 스크립트는 다음과 같습니다.

@BenchmarkMode(Mode.AverageTime)@OutputTimeUnit(TimeUnit.MICROSECONDS)@State(Scope.Thread)public class JMHBenchmark {    private NodeLocator nodeLocator;    private List<String> keys;    @Benchmark    public void test() {        for (String key : keys) {            MemcachedNode node = nodeLocator.getPrimary(key);        }    }    public static void main(String[] args) throws RunnerException {        Options opt = new OptionsBuilder()                .include(JMHBenchmark.class.getSimpleName())                .forks(1)                .warmupIterations(5)                .measurementIterations(5)                .build();        new Runner(opt).run();    }    @Setup    public void prepare() {        List<MemcachedNode> servers = new ArrayList<>();        for (String ip : ips) {            servers.add(new MemcachedNode(new InetSocketAddress(ip, 8080)));        }        nodeLocator = new ConsistentHashNodeLocator(servers, DefaultHashAlgorithm.MURMUR_HASH);        // 构造 50000 随机请求        keys = new ArrayList<>();        for (int i = 0; i < 50000; i++) {            keys.add(UUID.randomUUID().toString());        }    }    @TearDown    public void shutdown() {    }    static String[] ips = {...};}复制代码

우리는에 해당하는 JDK 해시 알고리즘, FNV132 알고리즘, CRC 알고리즘, MurmurHash 알고리즘과 Ketama 알고리즘을 테스트 하였다 , , , .DefaultHashAlgorithmNATIVE_HASHFNV1_32_HASHCRC_HASHMURMUR_HASHKETAMA_HASH

상세 데이터에 도시 된 바와 같이.

가상 슬롯 파티션

일부 기사는 레디 스 클러스터가 일관된 해싱 알고리즘하지만, 가상 슬롯 분할 알고리즘의 사용을 사용하지 않는 말했다. 그러나 가상 슬롯 파티션 사용 외부 인터넷 레디 스 만 일관된 해싱 알고리즘의 변형이라고 말할 가상 슬롯 레디 스 동적 확장을 할 수 있습니다.

레디 스 아마도에만이 질문에 대한 정확한 답을하기 위해 소스 코드를 찾을 수 있습니다.

종료


개인 공공 번호 : 휴 퍼진 아키텍처 노트 (ID : shishan100)

: 맵을 길게 누르면에있는 공개 문제에 오신 것을 환영하지 휴 퍼진 노트의 구조를!

응답 없음 공공 배경 정보 없음 , 학습 자료의 독점 비밀에 접근

휴 퍼진 아키텍처 노트, BAT 아키텍처 경험 지갑




추천

출처juejin.im/post/5d0781c1f265da1b827a9ca2