異常處理小妙招——2.代碼的韌性:如何實現操作的原子性回滾

一、核心思想:什么叫“失敗原子性”?

想象一下你在玩一個闖關游戲,有一關需要你連續跳過三個平臺。

  • 不具有原子性:你跳過了第一個和第二個平臺,但在跳第三個時失敗了、掉下去了。結果你不僅沒過關,連之前跳過的兩個平臺也白費了,你被迫回到了起點。這種感覺非常糟糕,因為你部分成功的努力被浪費了。
  • 具有原子性:同樣,你在跳第三個平臺時失敗了。但這次,系統會把你穩穩地放回第二個平臺的起點,讓你可以從那里立刻開始重跳第三個平臺,而不是一切歸零。

所以,“失敗原子性”就是:
當一個操作(比如一個方法或函數)執行失敗時,它應該讓系統(或對象)“就像這個操作從來沒被執行過一樣”,保持原有的、完整的狀態,而不會處于一個“半成功半失敗”的混亂中間狀態。


二、為什么它重要?(會出什么亂子?)

來看一個經典的、會出大問題的例子:銀行轉賬

public void transfer(Account from, Account to, BigDecimal amount) {// 1. 從A賬戶扣錢from.debit(amount); // 假設扣款成功后,系統在這里突然崩潰了(比如數據庫斷開)// 2. 往B賬戶加錢(還沒來得及執行!)to.credit(amount); 
}

后果是什么?
錢已經從你的賬戶扣走了,但卻沒有進入對方的賬戶!這筆錢就這樣憑空消失了。這就是典型的“失敗不原子”導致的災難性后果。系統處于一個不一致的狀態,沒有人知道錢去哪了,恢復和排查都極其困難。


三、在實踐中如何實現?(四大法寶)

法寶一:📋 事前檢查(參數校驗) - “先看路,再開車”

在真正修改數據之前,先把所有可能出錯的地方都檢查一遍。

轉賬例子改良:

public void transfer(Account from, Account to, BigDecimal amount) {// 事前檢查所有條件if (from.getBalance().compareTo(amount) < 0) {throw new InsufficientFundsException("余額不足");}if (amount.compareTo(BigDecimal.ZERO) <= 0) {throw new InvalidAmountException("金額必須大于0");}// 確認所有條件OK,才開始執行核心操作from.debit(amount); to.credit(amount);   // 即使這里失敗,也只是沒加錢,但還沒扣錢呢!
}

打比方: 就像你出門前檢查“手機、錢包、鑰匙”都帶齊了再關門,而不是走到半路發現沒帶鑰匙,結果門已經鎖上了。

法寶二:🔀 調整順序(先做可能失敗的) - “先做難的,再做簡單的”

把那些不會改變狀態的、或者容易失敗的計算先做完,最后再一步到位地更新狀態。

例子: 假設你要更新一個用戶列表,需要先計算一個新值。

// 不太好的方式:先改了狀態,后做可能失敗的計算
public void update() {this.state = someNewValue; // 先修改了狀態this.result = computeVeryHardThing(); // 這里如果計算失敗拋出異常,state就已經被污染了
}// 更好的方式:先做計算,最后賦值
public void update() {var tempResult = computeVeryHardThing(); // 先在不影響狀態的情況下完成計算this.state = someNewValue;   // 然后一次性更新狀態this.result = tempResult;
}

打比方: 就像做菜,你應該先把所有食材都切好備好(完成所有準備工作和計算),最后再開火下鍋(更新狀態)。而不是油都燒冒煙了才發現蒜還沒剝。

法寶三:📝 副本模式(在臨時拷貝上操作) - “草稿紙策略”

不在原件上直接修改,而是先做個拷貝,在拷貝上完成所有操作,確認無誤后,再一次性替換原件。

例子: 你想修改一個用戶的昵稱,但這個操作需要一連串復雜的校驗。

public void setUserName(User user, String newName) {// 1. 創建一份用戶數據的副本(或克隆)User tempUser = user.copy();// 2. 在副本上進行所有復雜操作和校驗tempUser.setName(newName);validateUserName(tempUser); // 可能失敗的操作someOtherComplexOperation(tempUser); // 另一個可能失敗的操作// 3. 只有上面全部成功了,才一次性替換原來的對象this.user = tempUser;
}

打比方: 就像領導讓你寫一份重要報告,你絕不會在唯一的原件上直接修改。而是先復制一份Word文檔,在副本上大膽修改、調整格式,全部滿意后,再把副本重命名為正式文件,替換掉舊的。這樣即使修改過程中電腦死機,原件也毫發無損。

法寶四:?? 事務與回滾(記日記) - “玩游戲隨時存檔”

這是最強大、最正式的方法。像數據庫一樣,把要做的每一步操作都記錄下來(寫日志),如果中途失敗,就按照日志記錄反向操作,把已經執行了的步驟撤銷掉。

轉賬例子終極解決方案:
這其實就是數據庫事務(Transaction)的核心思想。

START TRANSACTION; -- 開始一個事務UPDATE accounts SET balance = balance - 100 WHERE id = 1; -- 扣款
UPDATE accounts SET balance = balance + 100 WHERE id = 2; -- 加款-- 如果執行到這里都沒錯,就提交事務,讓更改永久生效
COMMIT;-- 如果任何一句SQL失敗,就回滾,所有更改全部撤銷
ROLLBACK;

打比方: 就像玩RPG游戲,你在進入Boss房之前會手動存檔。如果打Boss失敗了,你讀檔重來,游戲世界會完全恢復到打Boss之前的狀態,就像什么都沒發生過一樣。

總結與實踐建議

方法一句話精髓常用場景
事前檢查先看路,再開車參數驗證、權限校驗、前置條件判斷
調整順序先做難的,再做簡單的計算密集型任務,操作步驟有依賴關系
副本模式草稿紙策略復雜對象的修改、集合操作(如 Collections.copy
事務回滾玩游戲隨時存檔數據庫操作、任何需要多個步驟保持一致的業務(如轉賬)
  1. 優先選擇“不可變對象”:如果一個對象創建后就不能被修改(如Java中的String),那就天然具有失敗原子性,這是最簡單的辦法。
  2. 多用“事前檢查”:這是代價最小、最有效的習慣,能排除80%的問題。
  3. 復雜操作think in“副本”:當你需要修改一個復雜狀態時,先想想“我能不能先拷貝一份,弄好了再換回來?”
  4. 數據庫操作一定要用“事務”:這是底線。

記住這個原則的核心目標:努力讓你的代碼失敗得“優雅”,而不是“一地雞毛”。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/95448.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/95448.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/95448.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Crawl4AI:為LLM而生的下一代網頁爬蟲框架

在當今AI驅動的信息處理時代&#xff0c;從網頁中高效提取高質量、結構化的數據已成為連接互聯網與大語言模型&#xff08;LLM&#xff09;的關鍵橋梁。Crawl4AI作為一款開源的LLM友好型網頁爬蟲與刮板工具&#xff0c;正迅速成為開發者處理這一任務的首選解決方案。本文將深入…

輸出一個愛心

輸出效果&#xff1a;代碼實現&#xff1a;#include<iostream> #include<iomanip> #include<algorithm> using namespace std; int main() {int n;cin>>n;char a[8] {I,L,O,V,E,Y,O,U};int j 1;int k n*21;int o n*2-2;int aa 0; for(int i 0;i&…

深度集成Dify API:企業級RAG知識庫管理平臺解決方案

&#x1f3af; 需求和概述 當前基于Dify實現企業級的智能問答系統需求日益增長&#xff0c;Dify的低代碼開發框架和功能完整、靈活適應各種需求的特色得到廣大大模型和RAG開發著的歡迎。但是Dify在落地企業級應用時候&#xff0c;也面臨不少的問題&#xff0c;最突出的就是Dif…

C++循環越界問題

for (int i 0; i < historyTableList.size() - 1; i) {historyList2.push_back(historyTableList[i]); } historyList.size()0時&#xff0c;為什么會異常historyTableList.size() 返回的是 size_t 類型&#xff08;無符號整數&#xff09;當 size() 0 時&#xff0c;size…

MongoDB 從零到入門:實用指南

什么是 MongoDB&#xff1f; MongoDB 是一個流行的非關系型數據庫&#xff08;NoSQL&#xff09;&#xff0c;它使用類似 JSON 的文檔來存儲數據&#xff0c;而不是傳統的表格形式。這使得 MongoDB 非常靈活&#xff0c;特別適合處理半結構化數據和快速迭代的開發場景。 核心概…

WebRTC音頻QoS方法五(音頻變速算法之Expand算法實現)

一、概述介紹在WebRTC中&#xff0c;存在兩種擴展算法&#xff1a;PreemptiveExpand和Expand。盡管這兩種算法的目標都是擴展音頻信號&#xff0c;但它們的實現原理和應用場景卻有所不同。PreemptiveExpand&#xff08;預防性擴張&#xff09;主動擴展策略&#xff0c;旨在防止…

【Python - 基礎 - 工具】解決pycharm“No Python interpreter configured for the project”問題

解決pycharm“No Python interpreter configured for the project”問題 當你在 PyCharm 中遇到“No Python interpreter configured for the project”錯誤時&#xff0c;意味著你的項目沒有配置 Python 解釋器。以下是解決該問題的步驟。 示例 # 嘗試運行代碼時出現錯誤 prin…

Elasticsearch創建索引分片和副本大小建議

在Elasticsearch中&#xff0c;?分片(shard)和副本(replica)? 的設置直接影響集群性能、容錯能力和擴展性。以下是最佳實踐指南&#xff1a;核心概念?類型??描述??是否可修改??主分片(Primary Shard)?數據的最小存儲單元&#xff0c;每個索引被拆分成多個主分片? 索…

“人工智能+虛擬仿真”開啟新學期智慧學習之旅

在教育領域掀起數字化革新浪潮的今天&#xff0c;新學期的開啟不僅意味著知識探索新征程的起步&#xff0c;更蘊含著教育模式深度變革的無限可能。虛擬仿真技術作為教育現代化的關鍵驅動力&#xff0c;正重塑學習體驗&#xff0c;引領教育范式轉移。人工智能與虛擬仿真技術的結…

Photoshop用戶必看:讓你的PSD像JPG一樣可預覽

軟件介紹 Photoshop縮略圖補丁插件3.8.0.96是一款實用的工具&#xff0c;它能夠將PSD格式的文件&#xff08;Photoshop的專用格式&#xff09;以縮略圖的形式顯示出來。這一功能極大地提升了用戶在管理和查找圖像文件時的效率&#xff0c;使得看圖、找圖變得更加輕松便捷。該插…

idea2025.1.5安裝+pj

寫在前邊&#xff1a;如果是卸載舊版本IDEA重裝&#xff0c;一定記得之前的插件啥的&#xff0c;截個圖。還有主題字體設置啥的 目錄背景原因卸載原來版本安裝教程背景原因 原來的2022.2不支持jdk21的語言版本 卸載原來版本 1、如何徹底卸載 IDE, 可參考這篇的文章&#xff…

(四)Python控制結構(條件結構)

程序中的語句默認會按照自上而下的順序逐條執行&#xff0c;但通過一些特定的語句可以更改語句的執行順序&#xff0c;使之產生跳躍、回溯等現象&#xff0c;進而靈活地控制程序的執行流程。控制結構是編程中用于控制程序執行流程的語句&#xff0c;程序的三種基本控制結構為&a…

血緣元數據采集開放標準:OpenLineage Guides 使用 Apache Airflow? 和 OpenLineage + Marquez 入門

OpenLineage 是一個用于元數據和血緣采集的開放標準&#xff0c;專為在作業運行時動態采集數據而設計。它通過統一的命名策略定義了由作業&#xff08;Job&#xff09;、運行實例&#xff08;Run&#xff09;和數據集&#xff08;Dataset&#xff09; 組成的通用模型&#xff0…

FPGA|Quartus II 中使用TCL文件進行引腳一鍵分配

在FPGA設計過程中&#xff0c;合理的引腳分配是確保硬件功能正確實現的關鍵步驟之一。Quartus II 提供了通過 TCL&#xff08;Tool Command Language&#xff09;腳本自動化引腳分配的功能&#xff0c;這不僅可以大大提高設計效率&#xff0c;還能夠確保引腳分配的精確性和可重…

【Docker/Redis】服務端高并發分布式結構演進之路

目錄 概述 常見概念 基本概念 應用&#xff08;Application&#xff09;/ 系統&#xff08;System&#xff09; 模塊&#xff08;Module&#xff09;/ 組件&#xff08;Component&#xff09; 分布式&#xff08;Distributed&#xff09; 集群&#xff08;Cluster&#x…

【Excel】將一個單元格內??的多行文本,??拆分成多個單元格,每個單元格一行??

??所有文本都堆積在“prefix”列頂部的同一個單元格里&#xff08;很可能是B10單元格&#xff09;&#xff0c;并且它們是用空格分隔的&#xff0c;而不是換行符。??因此&#xff0c;您不需要處理換行符&#xff0c;而是需要??按“空格”進行分列&#xff0c;并且將分列后…

新手SEO操作第一步

內容概要 網站優化對于新手而言&#xff0c;常常感覺無從下手。別擔心&#xff0c;這篇文章就是為你量身打造的入門指南。我們將從最基礎也是最重要的關鍵詞研究開始講起&#xff0c;手把手教你如何精準找到目標用戶搜索的詞。掌握了關鍵詞&#xff0c;接下來就是如何創作出搜索…

【高階數據結構】秘法(一)——并查集:探索如何高效地管理集合

前言&#xff1a; 前面我們已經學習了簡單的數據結構&#xff0c;包括棧與隊列、二叉樹、紅黑樹等等&#xff0c;今天我們繼續數據結構的學習&#xff0c;但是難度上會逐漸增大&#xff0c;在高階數據結構中我們要學習的重點是圖等 目錄 一、并查集的原理 二、并查集的基本操作…

spring boot 整合AI教程

1、pom.xml配置<?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://maven.apache.org/POM/4…

基于SpringBoot2+Vue2開發的儲物柜管理系統

角色 管理員&#xff1a;管理系統、用戶&#xff0c;管理儲物柜用戶&#xff1a;借用、歸還儲物柜&#xff0c;報修故障 技術棧 后端&#xff1a;Springboot2, JWT, PageHelper前端&#xff1a;Vue2數據庫&#xff1a;MySQL 核心功能 提供智能儲物柜管理&#xff0c;包括用戶注…