• Redis系列-第五篇分布式锁与主从复制


    1.Redis分布式锁

    1.1锁的处理

    • 单应用中使用锁:单进程多线程

      synchronize、Lock

    • 分布式应用中使用锁:多进程

    1.2分布式锁的实现

    • 基于数据库的乐观锁实现分布式锁

    • 基于zookeeper临时节点的分布式锁

    • 基于redis的分布式锁

    1.3分布式锁注意事项

    • 互斥性:在任意时刻,只有一个客户端能持有锁

    • 同一性:加锁和解锁必须是同一个客户端,客户端自己不能把别人加的锁给解了。

    • 可重入性:即使有一个客户端在持有锁的期间崩溃而没有主动解锁,也能保证后续其他客户端能加锁。

    1.4实现分布式锁

    1.4.1获取锁

    在这里插入图片描述

    • (推荐)方式1(使用set命令实现):
    /**
    	 * 使用redis的set命令实现获取分布式锁
    	 * @param lockKey   	可以就是锁
    	 * @param requestId		请求ID,保证同一性
    	 * @param expireTime	过期时间,避免死锁
    	 * @return
    	 */
    	public static boolean getLock(String lockKey,String requestId,int expireTime) {
    		//NX:保证互斥性
    		String result = jedis.set(lockKey, requestId, "NX", "EX", expireTime);
    		if("OK".equals(result)) {
    			return true;
    		}
    		
    		return false;
    	}
    
    
    • 方式2(使用setnx命令实现):

    不推荐是因为方法代码块不是一个原子操作

    public static boolean getLock(String lockKey,String requestId,int expireTime) {
    		Long result = jedis.setnx(lockKey, requestId);
    		if(result == 1) {
    			jedis.expire(lockKey, expireTime);
    			return true;
    		}
    		
    		return false;
    	}
    
    

    1.4.2释放锁

    • 方式1(del命令实现):
        /**
    	 * 释放分布式锁
    	 * @param lockKey
    	 * @param requestId
    	 */
    	public static void releaseLock(String lockKey,String requestId) {
    	    if (requestId.equals(jedis.get(lockKey))) {
    	        jedis.del(lockKey);
    	    }
    	}
    
    • (推荐)方式2(redis+lua脚本实现)
    public static boolean releaseLock(String lockKey, String requestId) {
    		String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
    		Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));
    
    		if (result.equals(1L)) {
    			return true;
    		}
    		return false;
    	}
    
    

    2.Redis的主从复制

    2.1什么是主从复制

    持久化保证了即使redis服务重启也不会丢失数据,因为redis服务重启后会将硬盘上持久化的数据恢复到内存中,但是当redis服务器的硬盘损坏了可能会导致数据丢失,不过通过redis的主从复制机制就可以避免这种单点故障。同时在分布式系统中为了解决单点问题, 通常会把数据复制多个副本部署到其他机器, 满足故障恢复和负载均衡等需求。

    在这里插入图片描述

    说明:

    • 主redis中的数据有两个副本(replication)即从redis1和从redis2,即使一台redis服务器宕机其它两台redis服务也可以继续提供服务。

    • 主redis中的数据和从redis上的数据保持实时同步,当主redis写入数据时通过主从复制机制会复制到两个从redis服务上。

    • 只有一个主redis,可以有多个从redis。

    • 主从复制不会阻塞master,在同步数据时,master 可以继续处理client 请求

    • 一个redis可以即是主又是从,如下图:

    在这里插入图片描述

    2.2主从配置

    前提准备:复制两份redis.conf修改为redis-6379.conf(主配置),redis-6380.conf(从配置)

    修改:

    dbfilename dump-6379.rdb
    
    dbfilename dump-6380.rdb
    

    总体配置结构如下:

    在这里插入图片描述

    2.2.1主redis配置

    无需特殊配置。
    

    2.2.2从redis配置

    修改redis-6380.conf

    slaveof

    slaveof 127.0.0.1 6379
    

    上边的配置说明当前【从服务器】对应的【主服务器】的IP是127.0.0.1,端口是6379。

    默认情况下,从节点使用slave-read-only=yes 配置为只读模式,由于复制只能从主节点到从节点,对于从节点的任何修改主节点都无法感知,修改从节点会导致数据不一致。因此建议线上不要修改从节点的只读模式

    主节点:

    [root@localhost bin]# ./redis-cli -p 6379
    127.0.0.1:6379> keys *
    (empty list or set)
    127.0.0.1:6379> set s1 111
    OK
    127.0.0.1:6379> 
    

    从节点:

    [root@localhost bin]# ./redis-cli -p 6380
    127.0.0.1:6380> keys *
    (empty list or set)
    127.0.0.1:6380> keys *
    1) "s1"
    127.0.0.1:6380> set s1 22222
    (error) READONLY You can't write against a read only slave.
    

    从运行结果中看到复制已经工作了, 针对主节点6379的任何修改都可以,同步到从节点6380中。

    slaveof本身是异步命令, 执行slaveof命令时, 节点只保存主节点信息后返回, 后续复制流程在节点内部异步执行。 主从节点复制成功建立后, 可以使用info replication命令查看复制相关状态。

    主节点6379上执行 info replication

    127.0.0.1:6379> info replication
    # Replication
    role:master  ###表示当前节点是主节点
    connected_slaves:1 ###从节点的连接个数为1个
    slave0:ip=127.0.0.1,port=6380,state=online,offset=15033,lag=0 ####从节点的具体链接信息
    master_replid:3a86ebe866b3a5c2c996d175c87b311bd9e15fdc
    master_replid2:0000000000000000000000000000000000000000
    master_repl_offset:15033
    second_repl_offset:-1
    repl_backlog_active:1
    repl_backlog_size:1048576
    repl_backlog_first_byte_offset:1
    repl_backlog_histlen:15033
    

    在从节点6380上执行 info replication

    127.0.0.1:6380> info replication
    # Replication
    role:slave    ###表示当前节点是从节点
    master_host:127.0.0.1  ##主节点连接主机
    master_port:6379       ##主节点连接端口
    master_link_status:up
    master_last_io_seconds_ago:8
    master_sync_in_progress:0
    slave_repl_offset:15047
    slave_priority:100
    slave_read_only:1
    connected_slaves:0
    master_replid:3a86ebe866b3a5c2c996d175c87b311bd9e15fdc
    master_replid2:0000000000000000000000000000000000000000
    master_repl_offset:15047
    second_repl_offset:-1
    repl_backlog_active:1
    repl_backlog_size:1048576
    repl_backlog_first_byte_offset:1
    repl_backlog_histlen:15047
    

    在这里插入图片描述

    2.3其他配置主从方式

    • 在redis-server启动命令后加入--slaveof {masterHost} {masterPort}生效
    • 直接使用命令: slaveof {masterHost} {masterPort} 生效。

    2.4实现原理

    • Redis的主从同步,分为全量同步增量同步

    • 只有从机第一次连接上主机是全量同步

    • 断线重连有可能触发全量同步也有可能是增量同步(master判断runid是否一致

    • 除此之外的情况都是增量同步
      在这里插入图片描述

    2.4.1全量同步

    Redis的全量同步过程主要分三个阶段:

    • 同步快照阶段:Master创建并发送快照给Slave,Slave载入并解析快照。Master同时将此阶段所产生的新的写命令存储到缓冲区。

    • 同步写缓冲阶段:Master向Slave同步存储在缓冲区的写操作命令。

    • 同步增量阶段:Master向Slave同步写操作命令。

    在这里插入图片描述

    2.4.2增量同步

    • Redis增量同步主要指Slave完成初始化后开始正常工作时,Master发生的写操作同步到Slave的过程

    • 通常情况下,Master每执行一个写命令就会向Slave发送相同的写命令,然后Slave接收并执行。

    2.5安全性

    对于数据比较重要的节点, 主节点会通过设置requirepass参数进行密码验证, 这时所有的客户端访问必须使用auth命令实行校验。 从节点与主节点的复制连接是通过一个特殊标识的客户端来完成, 因此需要配置从节点的
    masterauth参数与主节点密码保持一致, 这样从节点才可以正确地连接到主节点并发起复制流程。

    # If the master is password protected (using the "requirepass" configuration
    # directive below) it is possible to tell the slave to authenticate before
    # starting the replication synchronization process, otherwise the master will
    # refuse the slave request.(如果主服务器受密码保护(使用下面的“requirepass”配置指令),则可以在启动复制同步进程之前告诉从服务器进行身份验证,否则主服务器将拒绝从服务器的请求。)
    #
    # masterauth <master-password>
    
    ################################## SECURITY ###################################
    
    # Require clients to issue AUTH <PASSWORD> before processing any other
    # commands.  This might be useful in environments in which you do not trust
    # others with access to the host running redis-server.
    #
    # This should stay commented out for backward compatibility and because most
    # people do not need auth (e.g. they run their own servers).
    #
    # Warning: since Redis is pretty fast an outside user can try up to
    # 150k passwords per second against a good box. This means that you should
    # use a very strong password otherwise it will be very easy to break.
    #(要求客户端在处理任何其他命令之前发出AUTH 。在您不信任其他人访问运行redis-server的主机的环境中,这可能很有用。
    
    为了向后兼容性,这应该保留注释,因为大多数人们不需要auth(例如,他们运行自己的服务器)。
    
    警告:由于Redis速度相当快,外部用户可以尝试高达每秒150k密码对一个好的框。这意味着您应该使用一个非常强大的密码,否则它将非常容易被打破。
    )
    # requirepass foobared
    

    微信公众号
    在这里插入图片描述

  • 相关阅读:
    C# 中的var关键字
    sql中去掉换行符和回车符
    Linq之旅:Linq入门详解(Linq to Objects)
    c# for 和 foreach
    c# Dictionary
    ASP.NET Web.config学习
    c# 装箱与拆箱的概念
    c# List集合学习
    Index was out of range
    C# double保留四位小数
  • 原文地址:https://www.cnblogs.com/niugang0920/p/12186246.html
Copyright © 2020-2023  润新知