Redis practise(二)使用Docker部署Redis高可用,分布式集群

思路

鉴于之间学习过的Docker一些基础知识,这次准备部署一个简单的分布式,高可用的redis集群,如下的拓扑

tuopu.png

下面介绍下,对于这张拓扑图而言,需要了解的一些基础概念。

Redis持久化

Redis有两种持久化策略。

Rdb

  • 全量备份
  • 形成二进制文件: dump.rdb

在使用命令 save(停写), BgSave。或者Save配置条件触发时,开始全量持久化Rdb文件。
相关的Redis.conf配置有:

  • dir
  • dbfilename
  • save(停写)
    例如:
save 900 1  #after 900 sec (15 min) if at least 1 key changed
save 300 10 #after 300 sec (5 min) if at least 10 keys changed
save 60 10000 #after 60 sec if at least 10000 keys changed
dbfilename dump.rdb # The filename where to dump the DB
dir ./ # The DB will be written inside this directory

aof

  • Append only file
  • 增量备份

Redis.conf配置

appendonly yes
appendfilename "appendonly.aof" # The name of the append only file (default: "appendonly.aof")

那么aof和Rdb的一些对比呢:

Col1 rdb aof
性能影响 平时不影响性能
备份时性能影响较大
性能一直会受到影响
数据丢失 两次备份之间的增量数据会丢失 不会丢失数据
备份文件大小 只有数据,较小 除了数据还记录了数据的变化过程,较大

可参考 官方文档

Redis高可用

从两个角度来考虑高可用:主从复制,主从切换

主从复制

可以通过命令SLAVEOF,来配置当前节点是某个Redis的Slave节点。参考 slaveof

主从切换

使用官方的Redis-sentinel可以实现Redis的主从切换。
参考Redis官方文档sentinel.md

值得注意的是,配饰抖个Sentinel实例,他们之间是互相通信的。官方推荐:一个健康的集群部署,至少需要3个Sentinel实例。这样他们之间会通过quorum参数来执行主从切换操作ODOWN。

  • SDOWN:主观离线,Sentinel发现redis挂了。
  • ODOWN:客观离线,Sentinel根据规则投票后确定redis挂了
  • 规则为 #of SDOWN>quorum

启动Sentinel

redis-sentinel /path/to/sentinel.conf

在本文的例子中,Sentienl的配置文件如下所示:
sentinel 实例1:

port 26379
daemonize yes
logfile "/var/log/redis/sentinel_26379.log"
sentinel monitor firstmaster 10.165.124.10 16379 2
sentinel auth-pass firstmaster 123456
sentinel config-epoch firstmaster 0
sentinel leader-epoch firstmaster 0
sentinel monitor sencondmaster 10.165.124.10 16381 2
sentinel auth-pass sencondmaster 123456
sentinel config-epoch sencondmaster 0
sentinel leader-epoch sencondmaster  0

sentinel 实例2:

port 26380
daemonize yes
logfile "/var/log/redis/sentinel_26380.log"
sentinel monitor firstmaster 10.165.124.10 16379 2
sentinel auth-pass firstmaster 123456
sentinel config-epoch firstmaster 0
sentinel leader-epoch firstmaster 0
sentinel monitor sencondmaster 10.165.124.10 16381 2
sentinel auth-pass sencondmaster 123456
sentinel config-epoch sencondmaster 0
sentinel leader-epoch sencondmaster  0

sentinel 实例3:

port 26381
daemonize yes
logfile "/var/log/redis/sentinel_26381.log"
sentinel monitor firstmaster 10.165.124.10 16379 2
sentinel auth-pass firstmaster 123456
sentinel config-epoch firstmaster 0
sentinel leader-epoch firstmaster 0
sentinel monitor sencondmaster 10.165.124.10 16381 2
sentinel auth-pass sencondmaster 123456
sentinel config-epoch sencondmaster 0
sentinel leader-epoch sencondmaster  0

可以看出,3个实例监控了 firstmaster及secondmaster,两个集群。
对于其中一个sentinel实例,看到它的信息如下:

$ redis-cli -h  10.165.124.10 -p 26380 -a 123456
10.165.124.10:26380> info
# Server
redis_version:2.8.24
redis_git_sha1:00000000
redis_git_dirty:0
redis_build_id:7de005b109aa3dd5
redis_mode:sentinel
os:Linux 3.16.0-0.bpo.4-amd64 x86_64
arch_bits:64
multiplexing_api:epoll
gcc_version:4.7.2
process_id:28837
run_id:630dfd8f09105bc84147923319afbf1d5cebe9a5
tcp_port:26380
uptime_in_seconds:347096
uptime_in_days:4
hz:13
lru_clock:12213499
config_file:/etc/redis/sentinel_26380.conf

# Sentinel
sentinel_masters:2
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
master0:name=firstmaster,status=ok,address=10.165.124.10:16379,slaves=1,sentinels=3
master1:name=secondmaster,status=ok,address=10.165.124.10:16381,slaves=1,sentinels=3

最后2行可以看出监控的集群,其余2个sentinel实例也是如此。

下面讲,如何在这样的集群上,通过Jedis来进行集群操作

JedisSentinelPool

它解决了痛点:

  • 不确定主redis 的ip port
  • 需要从sentinel获取

JedisSentinelPool可以直接想sentinel查询当前master的ip port,在建立连接。


import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.JedisSentinelPool;

import java.util.HashSet;
import java.util.Set;

public class JedisSentinelTest {
    public static Set<String> sentinels = new HashSet<String>();
    public static JedisPoolConfig config = new JedisPoolConfig();
    static{
        config.setMaxTotal(100);
        config.setMaxIdle(100);
        config.setMaxWaitMillis(1000);
        sentinels.add("10.165.124.10:26379");
    }
    public static JedisSentinelPool sentinelPool = new JedisSentinelPool("firstmaster", sentinels, config);

    public static void main(String[] args){
        Jedis jedis = sentinelPool.getResource();
        try{
            jedis.auth("123456");
            jedis.set("sentinel_one", "sentinel_one");
            System.out.println(jedis.get("sentinel_one"));
        }finally {
            if (jedis!= null){
                jedis.close();
            }

        }
    }
}

从代码里可以看出,我们的Jedis链接是通过Sentinel来获取的。

分布式

解决痛点:

  • 用户较多数据量大,一台服务器内存不足。
  • 部署多台增加容量,但是需要考虑如何分割数据

Redis的分布式使用的是一致性哈希算法。满足:

  • 分散性:避免相同的内容映射到不同节点
  • 平衡性:均匀分布,每个节点上的内容数量差不多
  • 单调性:增删节点时,不影响旧的映射。

一致性哈希的原理如图所示:

haxi.png

可参考:中文介绍英文介绍

有了算法,那么数据分片在Redis中还有3种分类:

1,客户端分片
使用比如ShardedJedis,Predis,Redis-rb等客户端的包,在客户端金鑫该数据分片。

shardJedis.png

在本例中,使用ShardedJedisPool,代码如下:

public class SharedJedisTest {
    public  static JedisPoolConfig poolConfig = new JedisPoolConfig();
    public  static List<JedisShardInfo> shards = new ArrayList<JedisShardInfo>();
    static {
        poolConfig.setMaxTotal(100);
        poolConfig.setMaxIdle(10);
        poolConfig.setMaxWaitMillis(2000);
        JedisShardInfo info1 = new JedisShardInfo("10.165.124.10",16379);
        info1.setPassword("123456");
        JedisShardInfo info2 = new JedisShardInfo("10.165.124.10",16381);
        info2.setPassword("123456");
        shards.add(info1);
        shards.add(info2);
    }
    public static ShardedJedisPool shardedJedisPool = new ShardedJedisPool(poolConfig,shards);

    public static void main(String[] args){
        ShardedJedis jedis = null;
        try {
            jedis = shardedJedisPool.getResource();
            for (int i =0; i<50; i++){
                String key = String.format("key%d",i);
                String value = String.format("value%d",i);
                jedis.set(key, value);
                Client client1= jedis.getShard(key).getClient();
                System.out.println(String.format(("%s in server: %s, and port is: %d"),key,client1.getHost(),client1.getPort()));
            }
        }finally {
            if (jedis != null){
                jedis.close();
            }
        }

    }
}

可以看出,两个Master都在一个shardedJedisPool里。但是这里有个问题就是需要维护Master的地址,所以后续如果可能,也需要开发Sentinel管理的JedisPool的插件。

2,代理分片
Twemproxy, codis, onecache。如图:

proxy.png

特点:

  • 服务端计算分片,客户端简单
  • 客户端无需维护redis的ip
  • Proxy会增加响应时间

3,查询路由
Redis 3.0:Redis集群自己做好分片。
特点:

  • 无需proxy
  • 客户端可以记录下每个key对应的redis以增加性能
  • 支持3.0 的客户端还很少
route.png

docker 镜像:
https://hub.docker.com/r/tutum/redis/
https://github.com/tutumcloud/redis

Redis监控统计

Info {options}

  • Server/Clients/Memory/Persistence/Stats/Replications/Cpu/Commandstats/Keyspaces

常用的

  • Memory : used_memory_human 当前使用内存量
  • Memory : mem_fragmentation_ratio 内存碎片率
  • Stats : instantaneous_ops_per_sec 实时的QPS
  • Stats: expired_keys, evicted_keys, keyspace_hits, keyspace_misses 过期的key,被置换的key,命中的key数量,未命中的key数量
  • Replication 主从连接是否正常,复制是否正常
  • Commandstats 每个命令的执行情况
  • Keyspaces 每个db上有多少key

彩蛋

一款成功产品的架构图:

单点式: 一主多从

single.png

分布式:

  • Redis:多个Redis节点,每个节点都一主一从。
  • Redis-sentinel: 主从探获、切换。
  • Proxy:业务代理转发,数据分片
  • NLB:负载均衡,Proxy去单点
multi.png
部署Redis的Docker file
FROM tutum/redis:latest

# redis configuration
ENV REDIS_PASS "123456"
ENV REDIS_MAXMEMORY_POLICY="allkeys-lru"
ENV REDIS_MAXMEMORY="10mb"
ENV REDIS_DATABASES="2"
ENV REDIS_APPENDONLY="yes"
ENV REDIS_APPENDFSYNC=everysec

启动容器:
集群1:

docker run -d --name master1 -p 16379:6379 test/redis
docker run -d --name slave1 -p 16380:6379 -e -e REDIS_MASTERAUTH="123456" test/redis

集群2:

docker run -d --name master2 -p 16381:6379 test/redis
docker run -d --name slave2 -p 16382:6379 -e REDIS_MASTERAUTH="123456" test/redis
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容