分布式系统常见问题

news2025/4/18 10:18:03

一.概述

分布式系统存在网络,时钟,以及许多不可预测的故障。分布式事务,一致性与共识问题,迄今为止仍没有得到很好的解决方案。要想完美地解决分布式系统中的问题不太可能,但是实践中应对特定问题仍有许多可靠的解决方案。本文不会谈及诸如BASE, CAP, ACID 等空泛的理论,只基于实践中遇到的问题提出可行的解决方案。

二.常见问题

1.读自己的写

现象: 用户在发布页发布了帖子,然后访问自己的主页查看帖子列表,并没有马上看到自己刚刚发布的帖子,等待1~2s后才看到

分析:后端db采取主从结构,复制任务在负载较高的情况下会有延迟。用户读取帖子列表查询的是从节点,所以无法及时看到刚刚发布的帖子。一般情况下延迟1~2s是可以接受的,但是为了更好的体验,可以做一些改进。

解决方案:

  • 如果用户读取的是自己的主页,就访问主节点。如果访问是他人的主页,就访问从节点。只需要在db层路由即可。
  • 客户端还可以记住最近更新时的时间戳,并附带在读请求中,据此信息,系统可以确保对该用户提供读服务时都应该至少包含了该时间戳的更新。如果不够新,要么交由另一个副本来处理,要么等待直到副本接收到了最近的更新

2.单调读

现象:用户查看某个帖子下面的评论,一会儿看到5条评论,一会儿看到6条评论。

分析:后端db采取主从结构,复制任务在负载较高的情况下会有延迟。用户读取评论列表查询的是从节点,但是两次读的是不同的从节点,当某个从节点具有明显延迟就会出现数据反复的现象。

解决方案:

  • 确保同一个用户每次都是读取同一个副本,可以在db层进行路由。这是一种典型的sticky 请求路由。

    replica = hash(user_id) % number_of_replica

3.负载倾斜与热点问题

现象:某个分区的数据明显比其他分区多,并且访问频率高,负载压力大。

分析:在某些特殊的业务场景下,比如官方或者名人账号有百万粉丝,当这些账号发布消息事件时,人们会对该消息进行评论,如果评论数据存储使用事件id进行hash,就会造成某个分区的负载产生倾斜。

解决:

  •   在关键词,比如消息事件id,的开头或者结尾添加一个随机数。只需一个两位数的十进制随机数就可以将关键字的写做操作分布到100个不同的关键字上,从而分片到不同的分区上。这些特殊逻辑只应用在一些特殊账号上。

4.fencing令牌

现象:在采用分布式锁的情况下,数据库中的事务重复执行。

分析:在分布式锁环境中,客户端A执行事务超时,分布式锁被释放。客户端B执行事务插入数据。客户端A恢复后继续执行事务,重复插入数据。

解决方案:

  • 这不是分布式事务的范畴。可以采用fencing令牌来解决。我们假设每次锁服务授予锁或租约时,同时还会返回一个fencing令牌,该令牌每授予一次就会递增。然后,要求客户端每次向存储系统发生写请求时,都必须包含所持有的fencing令牌。当使用zookeeper 作为锁服务时,可以用事务标识zxid,或节点版本cversion来充当fencing令牌,这两个都可以满足单调递增的要求。

5.Lamport时间戳

现象:客户端从两个分区获取两条不同的数据,比如事件a, b;a的序号小于b,但事实上b比a先发生。

分析:常见的有以下几种非因果序列发生器,产生的序列号与因果关系并不严格一致。

  • 每个节点单独产生自己的一组序列号。
  • 把墙上时间戳信息(物理时钟)附加在每个操作上。
  • 预先分配好序列号的区间范围,比如节点A负责区间1~1000的序列号,节点B负责1001~2000。

解决方案:

  • 使用Lamport时间戳。Lamport时间戳是一个kv对(计数器,节点ID)。核心流程:每个节点以及每个客户端都跟踪迄今为止所见到的最大计数器,并在每个请求中附带该最大计数器值。当节点收到请求(或者回复)时,如果发现请求内嵌的最大计数器大于节点自身的计数器,则它立即把自己的计数器修改为该最大值。

6.端到端的重复消除问题

现象:消息重复是非常普遍的,比如

  • 生产者发送消息到消费者,消费者消费成功后宕机,但是却没有更新消费位置,消费者重启后就会重新消费。
  • 常见的rpc调用,调用方因为网络问题没有收到被调用方的响应,选择重试。
  • 2PC 分布式事务中,因为网络问题,也可能出现重复事务的问题。
  • 用户在页面重复提交POST请求。

分析:端到端的重复问题是非常普遍的,在TCP 网络中也需要处理重复数据包的问题。有以下两种解决办法:

  • 最有效的办法之一是使操作满足幂等性,即无论执行一次还是多次,确保具有相同的结果。比如以下语句无论执行多少次效果都是一致的。

   update table set v = v2 where v = v1

  • 可以为操作生成一个唯一的标识符如(UUID),服务端对此UUID 进行去重校验。

  

  • 在典型的电商下单接口中采用了以上两种方法的结合:使用唯一标识符来进行去重,如果写入异常返回之前的订单。
create table order(
  # ...
  dedup_key varchar(60) not null comment 'key to pretend order duplication',
  client_id,
  # ...
  unique uniq_dedup_key(dedup_key, client_id)
);


@Transactional
Order createOrder(Integer userId, String prodCode, Decimal amount, String dedupKey) {
  try {
    String orderId = createOrder(userId, prodCode, amount, deupKey); // insert a new order
    Order order = getOrderById(orderId); // read order from db
    order.setDuplicated(false); // 标记是否有重复下单
    return order;
  } catch(UniqueKeyViolationException e) {
    // if duplicated order has existed, return previous order
    Order order = getOrderByDedupKey(dedupKey, clientId);
    order.setDuplicated(true);
    return order;
  } catch (Exception e) {
    // hanlde other errors and rollback transaction ...
  }
}

7.唯一性约束

现象:在集群高并发的环境下,用户A创建用户marquezzzz,用户B同时创建了用户marquezzzz,两者的用户名相同,这违背了唯一性约束。

分析:创建用户名的逻辑是,先去db中查询是否有对应的用户名(步骤1),如果没有就创建,如果存在就更新用户的其他信息(步骤2)。用户A执行了步骤1, 用户B执行了步骤1和2,然后用户A执行了步骤2,这样生成了两个同名的用户。

解决方案:

  • 串行化请求,将创建用户的请求串行化,比如发送到队列中,这样可以确保全局唯一性。
  • 在db层进行唯一性约束,比如使用唯一索引,考虑到庞大的数据量,性能会下降。如果做了分表,唯一索引的方法也不太可行。
  • 使用分布式锁,比如redis, zookeeper,redis伪代码如下:
boolean r = redisClient.setnx("userName", currentThread, 10s); // 使用 setnx 原子命令
if (!r) {
    return false;
}

// 步骤1 查找db确保没有重名

// 步骤2 插入用户

redisClient.delete("userName");

8.时钟问题

现象:在许多app中,客户端会上报事件,但是事件的发生时间不准确

分析:app客户端时钟可能不准确,或者用户手动调整过系统时钟。

解决方案:

为了调整不正确的设备时钟,一种方法是记录三个时间戳:

  1. 根据设备的时钟,记录事件发生的时间, device_event_time
  2. 根据设备的时钟,记录将事件发生到服务器的时间, device_send_time
  3. 根据服务器时钟,记录服务器收到事件的时间, server_receive_time

事件真实发生时间 = device_event_time + (server_receive_time - device_send_time)

三.参考

《数据密集型应用系统设计》

https://cloud.tencent.com/developer/article/1121727

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/741397.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

rapo雷栢鼠标MT750L说明书(含驱动)

驱动下载:点击跳转至本站下载

Dbeaver客户端存入的DateTime与实际有偏差

前几天调试项目的时候,发现数据库里存入的时间DateTime与我写入的时间有偏差 这里有两列,Name是字符串类型的,Time是DateTime类型的,Name是以当前时间戳命令的,Time是直接存的当前时间,理论上两者是应该一…

PHP之 Socket实践

一 Socket简介 1.1 Socket(套接宇),用来描述IP地址和端口,是通信链的句柄,应用程序可以通过Socket向网络发送请求或者应答网络请求。 1.2 Socket是支持TCP/IP协议的网络通信的基本操作单元,是对网络通信过程中端点的抽象表示,包…

Vue之v-for(包含key内部原理讲解)

文章目录 前言一、v-for二、key1.介绍2.使用3.原理4.总结 总结 前言 v-for&#xff1a;列表渲染 一、v-for v-for将JSON数据中的数组或对象渲染出列表的样式呈现。 直接见代码实例&#xff0c;如下&#xff1a; <!DOCTYPE html> <html lang"en"><h…

多肽试剂84211-54-1,Cyclo(7-aminoheptanoyl-Phe-D-Trp-Lys-Thr[Bzl]),特点说明

----------资料编辑|陕西新研博美生物科技有限公司小编MISSwu--------- 多肽试剂 | 基础知识概述&#xff08;部分&#xff09;: 英文名称&#xff1a;Cyclo(7-aminoheptanoyl-Phe-D-Trp-Lys-Thr[Bzl]) CAS号&#xff1a;84211-54-1 分子式&#xff1a;C44H57N7O6 分子量&…

UE5_ 地编_siki海岛

1.开启水插件 制作流体 2.开启大陆地形的插件

《Kali渗透基础》08. 弱点扫描(二)

kali渗透 1&#xff1a;OpenVAS / GVM1.1&#xff1a;介绍1.2&#xff1a;安装1.3&#xff1a;使用 2&#xff1a;Nessus2.1&#xff1a;介绍2.2&#xff1a;安装2.3&#xff1a;使用 3&#xff1a;Nexpose 本系列侧重方法论&#xff0c;各工具只是实现目标的载体。 命令与工具…

JAVA并发专题(2)之JMMsynchronizedvolatile详解

一、什么是JMM模型 Java内存模型(Java Memory Model简称JMM)是一种抽象的概念&#xff0c;并不真实存在&#xff0c;它描述的是一组规则或规范&#xff0c;通过这组规范定义了程序中各个变量&#xff08;包括实例字段&#xff0c;静态字段和构成数组对象的元素&#xff09;的访…

Chapter 5: Loops and Iterations | Python for Everybody 讲义笔记_En

文章目录 Python for Everybody课程简介Loops and IterationsUpdating variablesThe while statementInfinite loopsFinishing iterations with continueDefinite loops using forLoop patternsCounting and summing loopsMaximum and minimum loopsDebuggingGlossary Python f…

Linux命令简单学习

文件 输出并覆盖到哪个文件夹 1>文件 正常输出并覆盖 2>文件 输出错误流到某个文件夹 nohup 不强杀不退出 软连接,注意这里需要绝对路径 %% 一个文字的 %%a 当前locale 的星期名缩写(例如&#xff1a; 日&#xff0c;代表星期日)%A 当前locale 的星期名全称 (如&…

wsl 1和wsl 2在形式上的区别

完整的比较请参考&#xff1a; 比较 WSL 版本 | Microsoft Learn 如果是已经安装完成&#xff0c;但安装的是wsl &#xff0c;之后想由 wsl 1升级到wsl 2&#xff0c;请参考&#xff1a;WSL1升级至WSL2_wsl1升级wsl2_goldVitaminC的博客-CSDN博客 在形式上&#xff0c;wsl 1是…

Android AccessibilityService 实现《李跳跳》功能

AccessibilityService&#xff08;无障碍服务&#xff09;是 Android 操作系统中的一个功能&#xff0c;旨在帮助用户具有视觉、听觉或运动上的障碍更轻松地使用设备。它是 Android 提供的一种特殊服务&#xff0c;可以接收设备上发生的各种事件&#xff0c;并提供自定义的反馈…

UWB的技术特点

近年来&#xff0c;超宽带(UWB)无线通信成为短距离、高速无线网络最热门的物理层技术之一。 UWB的产生与发展 超宽带(UWB)有着悠久的发展历史&#xff0c;但在1989年之前&#xff0c;超宽带这一术语并不常用&#xff0c;在信号的带宽和频谱结构方面也没有明确的规定。1989年&a…

2023年了,v-if和v-for的优先级千万别怼错了,可尴尬的...

前言 v-if和v-for到底是谁的优先级更高呢&#xff1f;在vue3版本出来之前你直接说v-for更高&#xff0c;我无法反驳你&#xff0c;但是老哥现在是2023年了&#xff0c;咱可不兴这样回答了&#xff0c;可尴尬的... 剖析 我们都知道&#xff0c;这个v-if是条件渲染&#xff0c;…

flutter开发实战-Running Gradle task ‘assembleDebug‘ 的解决方法

flutter开发实战-Running Gradle task ‘assembleDebug‘ 的解决方法 使用Android studio经常出现Running Gradle task ‘assembleDebug‘问题&#xff0c;记录一下解决方法。 一、在Android目录下更改build.gradle 将repositories中的google(), mavenCentral() repositori…

一、简单的Spring Authorization Server示例代码

需要有一定的OAuth2的基础 需要有一定的Spring Security基础 Spring Authorization Server 官方简介&#xff1a;Spring Authorization Server is a framework that provides implementations of the OAuth 2.1 and OpenID Connect 1.0 个人理解为OAuth 2.1 and OpenID Conne…

用技术指标伦敦金行情走势图

经常有投资者说&#xff0c;伦敦金行情走势图老是涨跌涨跌&#xff0c;抓不准它涨跌的规律&#xff0c;老是被它弄得头昏脑胀。其实看伦敦金行情走势图的方法有很多&#xff0c;最直接的就是使用技术指标。技术指标本来就是投资者为了避免伦敦金行情走势图上价格干扰性波动&…

玩转AI二维码:分享我的漂亮二维码生成秘诀

这几天我又生成了很多漂亮的二维码图片&#xff0c;有了一些感受和想法&#xff0c;特总结此文&#xff0c;分享给大家 先看效果&#xff0c;喜欢的可以继续读下去&#xff08;遵守平台规则&#xff0c;图片已阉割&#xff0c;需要更多图片参数的同学可直接看文章最后&#xf…

Leap AI + Python 开发绘图应用

使用python语言&#xff0c;并借助Leap AI网站的api key&#xff0c;可以轻松实现AI绘图功能。使用时&#xff0c;用户只要输入prompt提示词&#xff0c;几秒钟之内服务器就能生成图片并返回图片的链接地址。开发人员可以利用这个功能开发个性化的绘图软件&#xff0c;或者整合…

后端Linux软件安装大全[JDK、Tomcat、MySQL、Irzsz...持续更新中]

文章目录 前言1.软件安装方式2.安装jdk3.安装Tomcat4.安装MySQL5.安装lrzsz 总结 前言 为了巩固所学的知识&#xff0c;作者尝试着开始发布一些学习笔记类的博客&#xff0c;方便日后回顾。当然&#xff0c;如果能帮到一些萌新进行新技术的学习那也是极好的。作者菜菜一枚&…