精华内容
下载资源
问答
  • 分布式本地缓存到一致性hash 背景 最近在开发一款C端产品,研究了一下Java...我们的系统是基于springcloud的分布式系统,有了本地+远程缓存,那么就要考虑缓存一致性的问题了。远程缓存暂且不谈,毕竟同一个服...

    由分布式本地缓存到一致性hash

    背景

    最近在开发一款C端产品,研究了一下Java服务端缓存框架,发现阿里的jetcache不错,有二级缓存,既可以做本地缓存也可以做远程缓存,兼容springboot,使用起来很方便。

    使用二级缓存,可以很有效的分摊一个缓存的负载。我们的系统是基于springcloud的分布式系统,有了本地+远程缓存,那么就要考虑缓存一致性的问题了。远程缓存暂且不谈,毕竟同一个服务(下面说的服务大概都代表同一个服务的不同节点)都是用同一个Redis的库。服务之间本地缓存怎么同步呢?

    设想一个场景,如果服务a1从现在时刻缓存数据集d0,服务a2从t时刻缓存数据集d0,在t+n时(假设数据t<本地缓存时间<(t+n)),数据集修改为d1,此时正好请求到服务a1,此时a1缓存的数据集为d1,那么,在接下来一段时间内,由于负载均衡的原因,请求会去轮询请求服务a1a2,那么获取到的数据集就会是d0d1不一样的结果。这不是客户端想要的结果呀。实际上客户端想要的是请求保持幂等性

    解决方案

    同步缓存

    说到缓存一致,先想到的当然是从缓存本身入手,让每台机子的缓存都同步成一样的数据集。沿着这个思路,要么让数据变更时通知各个缓存数据服务,让其来同步数据,要么就是数据变更时统一刷新缓存,一个主动一个被动,但是都有同一个问题,就是几乎很难知道都有谁在使用这份数据。

    负载均衡

    同步缓存不行,那么换个思路,前面说过,请求是因为负载均衡才路由到不同的服务的,那么就让同一个客户端的请求都路由到同一个服务不就行了。但是这样还是有些问题:

    • 用什么来固定客户端,至少要保证同一个客户端,每次都标识都是一致的,才能保证根据标识来负载均衡是一致的?

      这个问题,可能只有IP才是“不变”,用户ID不行,没登录的用户用不了,客户端唯一key不行,这个web端没这个东西,好像就没有了吧?对于IP来说,有可能移动端或者连接WiFi的时候会在短时间内IP变化,但这种变化怎么说呢,可以不考虑的。

    • 负载均衡算法怎么保证一致性?

      • 首先,需要用到负载均衡的,在我现在的系统中,只有springcloud gateway和feignclient两个框架用到了,只需要重写其负载均衡的方法即可。

      • 其次,使用什么算法保证每个客户端都能路由到同一个服务中去。前面说到了唯一标识一个客户端的值,就是客户端的IP,那么可以用IP来映射服务,其实算法也就呼之欲出了,没错,就是一致性hash

    Talk is cheap. Show me the code.

    一致性hash算法

    需要依赖:

    <dependency>
        <groupId>com.google.guava</groupId>
        <artifactId>guava</artifactId>
        <version>23.0</version>
    </dependency>
    

    来源于streametry/jumphash,没有虚拟节点的一致性hash算法。

    package com.wuhenjian.util.hash;
    
    import com.google.common.primitives.UnsignedLong;
    
    /**
     * 一致性hash算法工具类
     * @author 無痕剑
     * @date 2019/7/17 18:59
     */
    public class ConsistentHashUtil {
    
    	private ConsistentHashUtil() {}
    
    	private static UnsignedLong KEY_TIMES = UnsignedLong.valueOf(2862933555777941757L);
    	private static long CONSTANT = 1L << 31;
    
    	/**
    	 * 使用对象的hashCode方法计算选择的hash桶值
    	 * @param buckets 桶数量
    	 * @param object 对象(最好是重写过hashCode方法的对象)
    	 * @return 选择的桶值
    	 */
    	public static int jump(int buckets, Object object) {
    		return jump(buckets, object.hashCode());
    	}
    
    	/**
    	 * 计算keyHash值最近的桶值
    	 * @param buckets 桶数量
    	 * @param keyHash hash值
    	 * @return 选择的桶值
    	 */
    	public static int jump(int buckets, long keyHash) {
    		UnsignedLong key = UnsignedLong.fromLongBits(keyHash);
    		long b = -1, j = 0;
    		while (j < buckets) {
    			b = j;
    
    			key = key.times(KEY_TIMES).plus(UnsignedLong.ONE);
    			UnsignedLong keyShift = UnsignedLong.fromLongBits(key.longValue() >>> 33).plus(UnsignedLong.ONE);
    
    			j = (long) ((b + 1) * (CONSTANT / keyShift.doubleValue()));
    		}
    
    		return (int) b;
    	}
    }
    
    

    重写负载均衡方法

    feignclient

    feignclient没有找到对应的官方文档说明,这个有点坑,但是自己对feign还是比较属性,之前写过一个SpringCloud微服务Zuul网关动态路由,里面有说明怎么配置才能在feign中使用RequestContextHolder.getRequestAttributes()获取到原请求。这里获取到原请求其实就是获取请求中携带的源IP,用于负载均衡。

    说明一个feign比较坑的地方,负载均衡选择服务com.netflix.loadbalancer.IRule#choose(Object key)的参数我真的是想尽了办法都没法把这个参数传给我自定义的类,没办法,所以只好在方法里面获取原请求来获取参数了。

    package com.wuhenjian.api.config;
    
    import com.netflix.client.config.IClientConfig;
    import com.netflix.loadbalancer.AbstractLoadBalancerRule;
    import com.netflix.loadbalancer.ILoadBalancer;
    import com.netflix.loadbalancer.Server;
    import com.wuhenjian.model.gateway.balance.BalanceConstant;
    import com.wuhenjian.util.hash.ConsistentHashUtil;
    import com.wuhenjian.util.string.StringUtil;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.util.CollectionUtils;
    import org.springframework.web.context.request.RequestContextHolder;
    import org.springframework.web.context.request.ServletRequestAttributes;
    
    import javax.servlet.http.HttpServletRequest;
    import java.util.List;
    
    /**
     * IP-一致性HASH负载均衡算法实现
     * @author 無痕剑
     * @date 2019/7/17 9:19
     */
    @Slf4j
    public class IpConsistentHashRule111 extends AbstractLoadBalancerRule {
    
    	@Override
    	public void initWithNiwsConfig(IClientConfig clientConfig) {
    	}
    
    	@Override
    	public Server choose(Object key) {
    		String clientRealIp;
    		// key为空,则使用RequestContextHolder获取请求源,再获取其中的客户端真实IP
    		if (key == null) {
    			ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
    			if (attributes == null) {
    				log.error("负载均衡未获取到原请求");
    				return null;
    			}
    
    			HttpServletRequest request = attributes.getRequest();
    			// 通过源请求获取到客户端真实IP,这个值必须存在,才能进行IP一致性HASH负载均衡
    			clientRealIp = request.getHeader(BalanceConstant.CLIENT_REAL_IP);
    			if (StringUtil.isBlank(clientRealIp)) {
    				log.error("在请求[{} {}]中未获取到客户端真实IP", request.getMethod(), request.getRequestURI());
    				return null;
    			}
    		} else {
    			clientRealIp = (String) key;
    		}
    
    		ILoadBalancer loadBalancer = super.getLoadBalancer();
    		if (loadBalancer == null) {
    			log.error("未获取到负载均衡操作接口对象");
    			return null;
    		}
    
    		// 获取可用服务
    		List<Server> reachableServerList = loadBalancer.getReachableServers();
    		if (CollectionUtils.isEmpty(reachableServerList)) {
    			log.error("负载均衡没有可用服务");
    			return null;
    		}
    
    		// 使用一致性hash算法计算路由到的服务序号
    		int jump = ConsistentHashUtil.jump(reachableServerList.size(), clientRealIp);
    
    		// 返回选择的服务
    		return reachableServerList.get(jump);
    	}
    }
    
    

    springcloud gateway

    这个官网说的就很清楚了,负载均衡默认是由这个类实现的,需要的就是重写这里org.springframework.cloud.gateway.filter.LoadBalancerClientFilter#choose。看重写的逻辑,实际上就是重写了org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient#getServer(com.netflix.loadbalancer.ILoadBalancer)方法中的return语句,把原来传的固定值default改为了客户端真实IP。

    package com.wuhenjian.gateway.pe.filter;
    
    import com.netflix.client.config.IClientConfig;
    import com.netflix.loadbalancer.ILoadBalancer;
    import com.netflix.loadbalancer.Server;
    import com.wuhenjian.model.gateway.balance.BalanceConstant;
    import com.wuhenjian.util.string.StringUtil;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.cloud.client.ServiceInstance;
    import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
    import org.springframework.cloud.gateway.filter.LoadBalancerClientFilter;
    import org.springframework.cloud.gateway.support.NotFoundException;
    import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
    import org.springframework.cloud.netflix.ribbon.*;
    import org.springframework.http.server.reactive.ServerHttpRequest;
    import org.springframework.stereotype.Component;
    import org.springframework.web.server.ServerWebExchange;
    
    import java.net.URI;
    
    /**
     * IP一致性HASH负载均衡
     * @author 無痕剑
     * @date 2019/7/18 11:55
     */
    @Slf4j
    @Component
    public class IpHashLoadBalancerClientFilter1111 extends LoadBalancerClientFilter {
    
    	private final SpringClientFactory clientFactory;
    
    	public IpHashLoadBalancerClientFilter1111(LoadBalancerClient loadBalancer,
    	                                          SpringClientFactory clientFactory) {
    		super(loadBalancer);
    		this.clientFactory = clientFactory;
    	}
    
    	@Override
    	protected ServiceInstance choose(ServerWebExchange exchange) {
    		URI uri = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR);
    		if (uri == null) {
    			log.error("负载均衡拦截器获取前置拦截器[ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR]属性失败");
    			throw new NotFoundException("负载均衡拦截器获取前置拦截器[ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR]属性失败");
    		}
    
    		String serviceId = uri.getHost();
    		// 获取负载均衡操作接口对象
    		ILoadBalancer loadBalancer = clientFactory.getLoadBalancer(serviceId);
    		if (loadBalancer == null) {
    			log.error("未获取到负载均衡操作接口对象");
    			throw new NotFoundException("未获取到负载均衡操作接口对象");
    		}
    
    		ServerHttpRequest request = exchange.getRequest();
    		// 获取请求头中的x_forwarded_for参数
    		String clientRealIp = request.getHeaders().getFirst(BalanceConstant.CLIENT_REAL_IP);
    		if (StringUtil.isBlank(clientRealIp)) {
    			log.error("在请求[{} {}]中未获取到客户端真实IP", request.getMethod(), request.getURI().getPath());
    			throw new NotFoundException("在请求中未获取到客户端真实IP");
    		}
    
    		// 根据请求源IP进行负载均衡
    		Server server = loadBalancer.chooseServer(clientRealIp);
    
    		return new RibbonLoadBalancerClient.RibbonServer(
    				serviceId,
    				server,
    				isSecure(server, serviceId),
    				serverIntrospector(serviceId).getMetadata(server)
    		);
    	}
    
    	private ServerIntrospector serverIntrospector(String serviceId) {
    		ServerIntrospector serverIntrospector = this.clientFactory.getInstance(serviceId,
    				ServerIntrospector.class);
    		if (serverIntrospector == null) {
    			serverIntrospector = new DefaultServerIntrospector();
    		}
    		return serverIntrospector;
    	}
    
    	private boolean isSecure(Server server, String serviceId) {
    		IClientConfig config = this.clientFactory.getClientConfig(serviceId);
    		ServerIntrospector serverIntrospector = serverIntrospector(serviceId);
    		return RibbonUtils.isSecure(config, serverIntrospector, server);
    	}
    }
    
    

    创建bean

    /**
     * 设置负载均衡算法为IP-一致性HASH算法
     */
    @Bean
    public IRule IpHashRule() {
    	return new IpConsistentHashRule();
    }
    

    最后

    后续会把负载均衡方法再补上去掉已失效的服务,重新请求这个功能,让它成为一个通用且能应用于生产的方法。

    展开全文
  • 文章目录分布式——缓存一致性(Redis、MySQL)1. 前言2. 常见方案的问题点2.1 先更新数据库,再更新缓存2.2 先删除缓存,再更新数据库2.3 先更新数据库,再删除缓存3. 维护一致性3.1 设置缓存过期时间3.2 利用消息...

    分布式——缓存一致性(Redis、MySQL)

    1. 前言

    • 分布式一致性的问题,既是指“如何保证分布式多个节点的数据一样、没有信息差异”。通常会通过各类算法方案来保证一致性,例如Paxos、Raft、ZAB等。(我的一份简易记录
    • 分布式缓存一致性,通常是谈论一个节点中的缓存与另一个节点的原始数据如何维持一致性(或多个节点)。
    • 在这里按照最常用的软件来做分析:Redis + MySQL

    分布式——缓存一致性01

    • 通常由于多个Service的高并发请求,会导致Redis中缓存的数据与MySQL中的数据不一致,这也就是需要解决的问题。

    2. 常见方案的问题点

    2.1 先更新数据库,再更新缓存

    • 若存在如下逻辑,则会出现不一致的情况
    • 示意图

    分布式——缓存一致性02

    • 逻辑步骤
      1. Service 1 需要更新数据,更新 MySQL 数据库,设置 value = A
      2. Service 2 需要更新数据,更新 MySQL 数据库,设置 value = B
      3. Service 2 更新 Redis 缓存库,设置 value = B
      4. Service 1 更新 Redis 缓存库,设置 value = A
      5. 最终, MySQLvalue = BRedisvalue = A,产生不一致的问题

    2.2 先删除缓存,再更新数据库

    • 若存在如下逻辑,则会出现不一致的情况
    • 示意图
      分布式——缓存一致性03
    • 逻辑步骤
      1. Service 1 需要更新数据,删除 Redis 的缓存值
      2. Service 2 需要查询数据,查询 Redis 的缓存值,无值
      3. Service 2 查询 MySQL 数据库,得到旧值 A
      4. Service 1 更新 MySQL 数据库,设置 value = B
      5. Service 2 更新 Redis 缓存库,设置 value = A
      6. 最终, MySQLvalue = BRedisvalue = A,产生不一致的问题

    2.3 先更新数据库,再删除缓存

    • 若存在如下逻辑,则会出现不一致的情况

    • 示意图
      分布式——缓存一致性04

    • 逻辑步骤

      1. Redis 库中的缓存失效,过期被更前一个Service删除
      2. Service 2 需要查询数据,查询 Redis 的缓存值,无值
      3. Service 2 查询 MySQL 数据库,得到旧值 A
      4. Service 1 需要更新数据,更新 MySQL 数据库,设置 value = B
      5. Service 1 删除 Redis 的缓存值
      6. Service 2 更新 Redis 缓存库,设置 value = A
      7. 最终, MySQLvalue = BRedisvalue = A,产生不一致的问题
    • 注意

      • 国外的“Cache-Aside pattern”,也是支持该方案的(先更新数据库,再删除缓存)。其原因在于,通常情况下,数据库的更新会比查询慢,因此"查询数据库后更新缓存"的逻辑会在“更新数据库后删除缓存”的逻辑之前执行完,最终缓存会被删除。
      • 只是可能出现如上所述的小概率事件

    3. 维护一致性

    3.1 设置缓存过期时间

    • 该方式较为简单,只要数据会过期,最终还是会保持两边的一致性
    • 仍然存在两个问题点
      • 过长的过期时间,会导致较长时间存在不一致性问题
      • 过短的过期时间,会导致频繁查询MySQL数据库

    3.2 异步延迟删除

    • 针对导致“先更新数据库,再删除缓存”方案出现不一致的小概率事件(更新Redis删除Redis之后),我们还可以进行延迟删除,也就是说更新MySQL数据库后,我们可以等几秒(异步)再删除Redis的缓存。
    • 这样,就能保证删除Redis更新Redis之后。

    3.3 利用消息队列来异步处理

    • 在前面所说的方案中,“先更新数据库,再删除缓存”属于最优方案。但除开并发导致的顺序问题外,其实还有存在删除缓存失败的可能(例如Redis挂了,在恢复中)。
    • 如果删除缓存失败,就会导致数据不一致,那么你可以
      • 删除失败,那就不管了(导致数据不一致)
      • 删除失败,那就一直重试(阻塞,影响业务)
      • 删除失败,那就多次重试(较小的影响业务,超过次数后依然失败的话,还是会导致不一致)
      • 删除失败,那就异步重试删除(不影响当前业务)
    • 显然“异步重试删除”更好,其通常的方案如下
      • 在本节点构建一个消息队列,负责异步重试删除缓存
      • 在其他节点构建一个缓存删除服务,重试删除缓存
      • 将消息发往消息中间件(RocketMQ、RabbitMQ等),使用其他程序接收中间件的数据,进行异步删除

    分布式——缓存一致性05

    • 其实这几方案的逻辑都比较相似,其主要逻辑图如下

    分布式——缓存一致性06

    3.4 利用Canal监控MySQL,来做异步处理

    • 流程图如下
      分布式——缓存一致性07

    4. 维护一致性——拓展思考

    4.1 思考

    • 前面部分针对现网络上常见的方案,进行了描述与解析,基本上已经能解决缓存一致性问题。
    • 但是,我们可以做一些拓展思考
      • 能否进行进一步解耦呢?业务Service 不直接负责 Redis 缓存的更新。
      • 能否做一个一致性维护的服务呢?有一个服务来专门维护MySQL与Redis的一致性,保证顺序性。

    4.2 分布式架构

    • 示意图
      分布式——缓存一致性08

    • 描述

      • 一致性服务,负责缓存的更新、删除,保证执行的顺序性,如图中的蓝色部分
      • 任何业务 Service 查询数据,都只从 Redis 缓存库中获取,如图中黄色部分
        1. Service 1 开始查询数据,查询 Redis 的缓存值,无值
        2. Service 1 发送更新缓存的消息到一致性服务中,本节点继续轮询 Redis 缓存库(或监听一致性服务
        3. 一致性服务 获得消息,查询 MySQL 数据库中的数据
        4. 一致性服务 利用查到的数据,更新 Redis 库中的缓存
        5. Service 1 最终从 Redis 查得数据
      • 任何业务 Service 更新数据,都从 MySQL 数据库中更新,其后不负责删除数据,如图中橙色部分
        1. Service 2 需要更新数据,更新 MySQL 数据库
        2. Service 2 发送缓存失效的消息到一致性服务中,本节点继续执行其他代码
        3. 一致性服务 获得消息,删除 Redis 库中的缓存
    • 注意

      • 因为 一致性服务 中队列的顺序性,因此一条消息执行完成后,才会执行下一条
        • 情况 1
          • 队列顺序:“缓存失效”、“更新缓存”
          • 执行顺序:删除缓存、查询MySQL数据库、更新缓存
        • 情况 2
          • 队列顺序:“更新缓存”、“缓存失效”
          • 执行顺序:查询MySQL数据库、更新缓存、删除缓存
      • 因此不会存在不一致的问题

    4.3 分布式架构(优化)

    • 问题点 1

      • 仔细看上面 一致性服务 的执行逻辑就会发现:所有消息都是有顺序的,不相关的缓存之间也会进行阻塞。
      • 其实,我们只需要保证同一个 key 对应的缓存的一致性即可。因此,我们可以多分几个队列,只要保证同一个 key 的所有消息进入同一个队列即可(利用hash取模)。
    • 问题点 2

      • 另外,在分布式并发请求的情况下,可能队列中会同时收到多个 缓存失效更新缓存 的消息,部分步骤是没必要重复做的。例如,连续多条针对同一个 key更新缓存 的消息,更新一次了后,没必要重新再做“从MySQL查询,并更新Redis”的操作,除非该 key 在MySQl库中的值变了。
      • 因此,我们可以为每个 key 维护一个布尔值的 flag
        • 处理 缓存失效 的消息时,检查 flag
          • 如果为 true,那么表示该 key 已存在的缓存,进行删除缓存操作,最后设置 flag 为 false
          • 如果为 flase,那么表示 key 的缓存不存在,可以不进行缓存删除操作,但是还是建议执行删除缓存操作(确保一定失效)
        • 处理 更新缓存 的消息时,检查 flag
          • 如果为 true,那么表示前面已有消息更新了该 key 的缓存,直接不做处理
          • 如果为 flase,那么表示 key 的缓存不存在,需要“从MySQL查询,并更新Redis”,最后将该 flag 设置为 true
    • 示意图
      分布式——缓存一致性09

    • 描述

      • 由于 Service 是并发的,因此会发送各种消息到 一致性服务
      • 根据对 key 的hashcode取模,模以队列个数,可以知道该 key 会进入哪个队列,从而保证同一个key 进入同一个队列,保证了该 key 消息的顺序性
      • 消息进入队列后,会被按顺序处理,处理时根据 key 对应的 flag 来决定后续逻辑(见问题点2)
    • 注意

      • 显然该 一致性服务 可以是单个节点,同时还可以做成HA架构
      • 为了保证健壮性、处理量,我们还可以直接利用分布式的消息队列来实现,一致性服务 中的每个队列即对应分布式消息队列的一个分区(单个分区内是有序的)!例如 RocketMQ/Kafka + Flink。
    展开全文
  • 分布式缓存一致性

    2020-11-24 16:23:51
    一、分布式缓存一致性 1、分布式缓存一致性概念 分布式缓存不一致产生的原因, 解决方式 各种解决方式存在的问题 如何解决。

    一、分布式缓存一致性

    1、分布式缓存一致性概念

    分布式缓存不一致产生的原因,
    解决方式
    各种解决方式存在的问题
    如何解决。

    展开全文
  • 因为最近项目由于性能优化,引入了redis,但是随之而来的是大家关于缓存一致性问题的讨论。接下来主要是想和大家来聊一聊关于这个缓存一致性的带来的问题和一些处理方式。 关于缓存的读取,没猜错的话,大家应该都...

    因为最近项目由于性能优化,引入了redis,但是随之而来的是大家关于缓存一致性问题的讨论。接下来主要是想和大家来聊一聊关于这个缓存一致性的带来的问题和一些处理方式。

    关于缓存的读取,没猜错的话,大家应该都是按照如下的流程进行业务处理的。

                                                                      

    注:什么是缓存一致性的问题,其实就是我们最终落库数据和我们缓存中的数据不一致。

    但是我们对于缓存数据的更新方式上大家有各种处理方式,下面针对不同的处理,以及各自对应的缺陷进行一下总结。

    1.设置过期时间

    这种方案我们是为缓存的key值设置失效时间,这种情况下不管我们数据库数据怎么折腾,最晚会在缓存失效后,重新从数据库中获取最新的数据,加载到缓存中。

    优点:这种方式注重在最终缓存内数据和数据库中数据是一致的。

    缺点:内容更新不及时。

    我们通常将这个中方式用作后续讲到方式的补充。

    2.先更新数据库,再更新缓存

    这种方案的问题在于,当有两个或者多个线程同时进行数据更新时,会发生数据不一致的情况。流程如下图:

                                                                     

    由此可以看出,当线程1 先更新数据库,但是由于网路等原因晚于线程2更新缓存,最终导致的结果是缓存数据的不一致。

    2.先删除缓存,在更新数据库

    此方案问题在于,当有一个线程读取缓存时,缓存已经被另一个更新线程删除了,此时读取线程会去数据库查询数据,并且放到缓存中,此时更新线程还没有完成数据库的更新操作,依旧会出现缓存一致性的问题。流程如下:

                                               

    那么这种情况我们可以解决吗?可以 我么你可以采用延迟双删的策略,在我们更新数据库之后,延时一段时间,然后将缓存再次删除掉,这样其他读取线程再次读取时候,还是会加载最新的数据。

    3.先更新数据库,再删除缓存。

                                              

    可以看到此时数据库和缓存中的数据是一致的,有些朋友会问,难道这种情况就可以完全杜绝一致性问题吗?答案不是的。如果我们的读取数据库数据在更新之前,并且更新缓存数据在删除缓存之后,也同样会出现一致性问题。流程如下:  

                                                                  

    如图也会出现缓存一致性问题,但是这种出现概率很小。原因如下:

    通常我们读取操作的耗时会明显短于更新操作,所以即使先于更新读取了数据,但是由于更新线程最终删除了缓存,所以下次在读取时还会重新载入。流程如下

                                                                  

    所以大部分公司会选择这种更新方式。来最大限度避免一致性问题。

    4.使用分布式锁

    有些朋友会说,我们使用分布式锁是不是可以彻底解决这个一致性问题呢。答案是的。当我们在更新和读取线程同时加锁,我们会按照顺序进行执行,这样我们确实可以杜绝,但是为什么好多公司不这么做呢?答案是效率问题,因为加了锁,在高并发的情况下,我们大量读取缓存的操作可能会因为锁而排队。

     

    展开全文
  • 对于分布式中间件缓存的节点同步其实还是很好处理的,应用服务器集群都是向中间件缓存操作缓存数据,只需要保证缓存中间件节点的数据一致性即可保证缓存数据一致性。当然,对于不同的缓存中间件,节点数据同步机制也...
  • 在了解一致性哈希算法之前,最好先了解一下缓存中的一个应用场景,了解了这个应用场景之后,再来理解一致性哈希算法,就容易多了,也更能体现出一致性哈希算法的优点,那么,我们先来描述一下这个经典的分布式缓存的...
  • 分布式缓存一致性Hash的Java实现

    千次阅读 2016-12-24 22:27:48
    分布式缓存的一致性Hash的Java实现关于分布式缓存一致性Hash算法的原理,有很多书籍、博客都有详细的介绍。本文主要是想对一致性Hash算法进行一个小小的实现,方便自己更好的理解。算法的具体原理如下:先构造一个...
  • 该文章主要是来自于通用配置系统使用了文件缓存作为二级缓存,他的一致性如果保证的问题,目前了解到的有三种方案: ... 2.采用队列方式,将更新作为消息放入mq中进行...3.缓存同步、如何保证缓存一致性、缓存误用 ...
  • 这个特点会严重制约分布式缓存集群的伸缩设计,因为新上线的缓存服务器没有缓存任何数据,而已经下线的缓存服务器还缓存着网站的许多热点数据。 必须让新上线的缓存服务器对整个分布式缓存集群影响...
  • 缓存数据一致性 - 双写模式 这种模式下 ,当我们更改某个数据的时候,同时修改数据库的数据,和缓存的数据,但是在并发情况下会出现缓存的不一致问题, 解决方案: 加分布式读写锁(Redisson) 如果不需要数据的强...
  • 分布式本地缓存的一种实现

    千次阅读 2017-08-11 22:18:57
    ,我们要解决跨进程的数据复制,确保同一集群中各个进程实例地址空间数据副本的一致性,网络节点监控以及服务发现等能力。   采用JAVA实现本地缓存的SDK,因为在实际应用中,我们的所有业务系统都是Java...
  • 分布式缓存一致性hash

    千次阅读 2014-07-04 10:42:56
    第一种:传统的数据分布方法,将key的hash值对机器数取模 这个算法的实现非常简单,计算... 第二种:一致性hash 试想下如果使用传统取模算法。如果有一个key要存到缓存中,根据hash(key)/n (n表示有n台缓存服务...
  • A项目修改或者创建采集模型时,通过http接口调用B项目,B项目将信息放入redis,同时放入本地map缓存缓存的key为模型的token,value为模型的所有采集字段 代码如下 private ConcurrentHashMap<String, Map<...
  • 分布式缓存一致性hash算法

    千次阅读 2016-03-16 17:15:26
    基本场景 比如你有 N 个 cache 服务器(后面简称 cache ),那么如何将一个对象 object 映射到 N 个 cache 上呢,你很可能会采用类似下面的通用方法计算 object ...对于N台缓存服务器构成的集群缓存,依次编号为0 -
  • 分布式系统的一致性问题(汇总)

    万次阅读 2019-09-02 15:32:19
    保证分布式系统数据一致性的6种方案 问题的起源 在电商等业务中,系统一般由多个独立的服务组成,如何解决分布式调用时候数据的一致性? 具体业务场景如下,比如一个业务操作,如果同时调用服务 A、B、C,需要...
  • public Map<String, List<... //(锁的粒度,越细越快:具体缓存的是某个数据,11号商品) product-11-lock //RLock catalogJsonLock = redissonClient.getLock("catalogJson-lock"); //创建读锁 R.
  • 在现在的系统架构中,缓存的地位可以说是非常高的。...不过,由此产生的问题也是非常多的,其中一个就是如何保证数据库和缓存之间的数据一致性。 由于数据库的操作和缓存的操作不可能在一个事务中,也...
  • 解决分布式一致性
  • 先mark: https://www.cnblogs.com/yanglang/p/9098661.html ...mid=404308725&idx=1&sn=1a25ce76dd1956014ceb8a011855268e&scene=21#wechat_redirec...
  • 分布式多级缓存

    千次阅读 2018-07-17 10:38:42
    基本概念 缓存数据的方式 是否过期 不过期缓存 过期缓存 细粒度缓存 大Value缓存 热点缓存 ...所谓分布式多级缓存,就是指在整个系统的不同层级进行数据的缓存,以提升...轮询可以是请求更加的平均,一致性哈希可以...
  • 分布式缓存中的一致性Hash 算法

    千次阅读 2017-03-29 10:22:46
    分布式缓存服务器集群中,所有的缓存服务器中缓存的数据各不相同,这时路由算法就至关重要了,路由算法负责根据应用程序输入的缓存数据KEY计算得到应该将数据写入到哪台缓存服务器(写缓存) 或则 应该从哪台缓存...
  • 一致性 hash 算法(自动缓存迁移)+ 虚拟节点(自动负载均衡) redis cluster 的 hash slot 算法 hash 算法 来了一个 key,首先计算 hash 值,然后对节点数取模。然后打在不同的 master 节点上。一旦某一个 ...
  • 5. 分布式缓存中的一致性Hash 算法

    千次阅读 2017-01-05 16:47:27
    分布式缓存服务器集群中,所有的缓存服务器中缓存的数据各不相同,这时路由算法就至关重要了,路由算法负责根据应用程序输入的缓存数据KEY计算得到应该将数据写入到哪台缓存服务器(写缓存) 或则 应该从哪台缓存...
  • 分布式应用缓存

    2018-07-17 08:59:27
    缓存命中率 缓存回收策略 ...分布式缓存 多级缓存 示例代码 多级缓存封装 本地缓存初始化 写缓存封装 读缓存封装 NULL Cache 缓存是解决分布式提供高可用的利器之一,它可以大幅度的...
  • 一,缓存的使用 1,那些数据适合放入缓存? 及时性,数据一致性要求不高的数据;访问量大且更新频率不高的数据(读多写少) 2,本地缓存(Map): ...3,分布式缓存-缓存中间件(这里采用Red...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 141,251
精华内容 56,500
关键字:

分布式本地缓存一致性