分散式系統常見問題

来源:https://www.cnblogs.com/darcy-yuan/archive/2023/05/13/17394181.html
-Advertisement-
Play Games

一.概述 分散式系統存在網路,時鐘,以及許多不可預測的故障。分散式事務,一致性與共識問題,迄今為止仍沒有得到很好的解決方案。要想完美地解決分散式系統中的問題不太可能,但是實踐中應對特定問題仍有許多可靠的解決方案。本文不會談及諸如BASE, CAP, ACID 等空泛的理論,只基於實踐中遇到的問題提出 ...


一.概述

分散式系統存在網路,時鐘,以及許多不可預測的故障。分散式事務,一致性與共識問題,迄今為止仍沒有得到很好的解決方案。要想完美地解決分散式系統中的問題不太可能,但是實踐中應對特定問題仍有許多可靠的解決方案。本文不會談及諸如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


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 生成的代碼將和介面對應的參數、返回值一一對應,本文底層使用的工具為NSwag.exe,其他可替代的方案還有AutoSet.exe。 本文中生成的代碼將在編譯過程中自動編譯,類似grpc生成代碼的模式,如果使用AutoSet則需要手動引入代碼。 另外也可以使用NSwag對應的vs插件(https:// ...
  • LTSpice 是常用的電路模擬軟體, 但是只有 Windows 版本和 Mac 版本, 在 Linux 下需要用 Wine 運行. 以下說明如何在 Ubuntu 下安裝最新的 LTSpice 17.1.8 ...
  • 本文首發於公眾號:Hunter後端 原文鏈:Redis基礎命令彙總,看這篇就夠了 本篇筆記將彙總 Redis 基礎命令,包括幾個常用的通用命令,和各個類型的數據的操作,包括字元串、哈希、列表、集合、有序集合等在內的基本操作。 以下是本篇筆記目錄: 通用命令 字元串命令 哈希命令 列表命令 集合命令 ...
  • 部分概念: 1、在資料庫中產生數據不一致的根本原因是冗餘 2、一個事務對某數據加S鎖後,其它的事務不能對該數據加任何類型的鎖(錯誤):所謂S鎖,是事務T對數據A加上S鎖時,其他事務只能再對數據A加S鎖,而不能加X鎖,直到T釋放A上的S鎖 3、一個資料庫只有一個模式和一個內模式 4、使某個事務永遠處於 ...
  • 索引(基礎) 一、索引介紹 1.1、前言 在資料庫中,執行如下語句時: select * from emp where id=1000; mysql 是從第一條記錄開始遍歷,直至找到 id = 1000 的數據,然而這樣查詢的效率低,所以 mysql 允許通過建立索引來加快數據表的查詢和排序。 1. ...
  • 大數據面試題 Hadoop 分散式系統基礎架構,主要是為瞭解決海量數據的存儲和和海量數據的分析計算問題 Hadoop的特點 高可靠性:Hadoop底層維護多個數據副本,即使Hadoop某個計算元素或存儲出現故障,也不會 導致數據的丟失 高擴展性:集群分配任務數據,可方便擴展數以千計的節點 高效性:並 ...
  • 系統目錄是關係型資料庫存放模式元數據的地方,比如表和列的信息,以及內部統計信息等。PostgreSQL的系統目錄就是普通表。你可以刪除並重建這些表、增加列、插入和更新數值, 然後徹底把你的系統搞垮。 通常情況下,我們不應該手工修改系統目錄,通常有SQL命令可以做這些事情。(例如,CREATE DAT... ...
  • 開始 mock一個用於攔截ajax請求,並返回模擬數據的庫。主要讓前端獨立於後端進行開發,通過 pnpm add mockjs 來進行安裝 基礎 初窺門徑 var data = Mock.mock({ // 屬性 list 的值是一個數組,其中含有 1 到 10 個元素 'list|1-10': [ ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...