STL 性能優化實戰:解決項目中標準模板庫的性能瓶頸

🧑 博主簡介:CSDN博客專家、全棧領域優質創作者、高級開發工程師、高級信息系統項目管理師、系統架構師,數學與應用數學專業,10年以上多種混合語言開發經驗,從事DICOM醫學影像開發領域多年,熟悉DICOM協議及其應用開發技術。我的技能涵蓋了多種編程語言和技術框架:作為高級C/C++與C#開發工程師,擅長Windows系統下的.NET及C++開發技術,尤其精通MFC、DLL動態鏈接庫、WinForm、WPF、Windows服務、WebAPI及.NET Core跨平臺等技術的開發工作。熟悉Java開發,并利用業余時間學習了JavaScript、Vue等前端技術,同時自學了QT開發工具,對Python開發也有一定的了解,因此使我具備了使用多種混合語言進行開發的能力。我一直堅持撰寫博客文章,記錄個人的學習歷程,分享編程開發相關的知識與經驗,旨在為編程愛好者提供幫助和支持。通過這樣的方式,我希望可以與志同道合的朋友交流探討,共同進步,在技術的世界里不斷學習和成長。如果您也熱衷于技術探索,愿意一起討論最新技術趨勢或解決遇到的技術難題,歡迎隨時聯系。讓我們攜手共進,在追求卓越技術的道路上越走越遠。歡迎關注、學習及合作,可提供解決方案和技術支持!
技術合作請加本人wx(注明來自csdn):xt20160813

在這里插入圖片描述

STL 性能優化實戰:解決項目中標準模板庫的性能瓶頸

在現代 C++ 編程中,標準模板庫(STL)是開發者不可或缺的工具。它提供了豐富的容器、算法和迭代器,極大地簡化了代碼編寫的復雜性。然而,盡管 STL 提供了強大的功能,但不當使用也可能導致顯著的性能瓶頸,尤其是在大型或對性能要求嚴格的項目中。本篇博客將深入探討 STL 的性能優化策略,通過實際案例和詳細示例,幫助開發者有效識別并解決 STL 在項目中的性能瓶頸問題。
在這里插入圖片描述

目錄

  1. STL 性能概述
  2. 識別性能瓶頸
    • 常用的性能分析工具
    • 常見的 STL 性能問題
  3. 性能優化策略
    • 1. 選擇合適的容器
    • 2. 減少內存分配
    • 3. 避免不必要的拷貝
    • 4. 利用移動語義和原位構造
    • 5. 優化算法選擇
    • 6. 提高緩存局部性
    • 7. 使用自定義分配器
  4. 實戰案例:優化高性能日志系統
    • 初始實現
    • 優化步驟
    • 優化后的實現
  5. 最佳實踐與總結

STL 性能概述

在深入優化 STL 性能之前,有必要理解 STL 各組成部分的基本性能特征:

容器的時間復雜度

每種 STL 容器都有其特定的時間復雜度特性,這直接影響到它在不同場景下的性能表現。例如:

  • std::vector:在尾部插入和訪問隨機位置元素時具有 O(1) 的時間復雜度,但在中間插入或刪除元素時需要 O(n) 時間。
  • std::list:在任意位置插入和刪除元素時具有 O(1) 的時間復雜度,但不支持高效的隨機訪問。
  • std::map / std::unordered_mapstd::map 基于平衡二叉樹實現,查找、插入、刪除操作平均為 O(log n),而 std::unordered_map 基于哈希表實現,平均為 O(1)(最壞情況為 O(n))。
    在這里插入圖片描述

算法的性能

STL 提供了豐富的算法庫,選擇合適的算法對性能有著重要影響。例如,std::sort 的復雜度為 O(n log n),而 std::stable_sort 也是 O(n log n),但常數因子更大。

迭代器的使用

不同類型的迭代器(輸入迭代器、輸出迭代器、前向迭代器、雙向迭代器、隨機訪問迭代器)在性能上有不同影響。比如,隨機訪問迭代器支持常數時間的跳轉,而雙向迭代器則需要線性時間。

理解這些基本概念是優化 STL 性能的前提。

識別性能瓶頸

在優化之前,必須首先識別出性能瓶頸所在。沒有正確的性能分析,任何優化都是盲目的。
在這里插入圖片描述

常用的性能分析工具

以下是幾種常用的性能分析工具,可以幫助開發者定位程序中的性能問題:

  • gprof:GNU 的性能分析器,適用于 Linux 環境。
  • Valgrind (Callgrind):一個強大的程序分析工具,尤其適用于檢測程序的性能熱點。
  • Visual Studio Profiler:集成在 Visual Studio 中的性能分析工具,適用于 Windows 開發環境。
  • Perf:Linux 系統下的性能分析工具,功能強大。
使用 gprof 進行性能分析的示例
  1. 編譯時啟用分析選項

    g++ -pg -O2 -o my_app my_app.cpp
    
  2. 運行應用程序

    ./my_app
    
  3. 生成分析報告

    gprof my_app gmon.out > analysis.txt
    
  4. 分析 analysis.txt 以識別性能熱點

常見的 STL 性能問題

通過分析,以下是一些在項目中常見的 STL 性能問題:

  • 頻繁的內存分配:例如,頻繁向 std::vector 中插入元素導致多次分配和拷貝。
  • 不必要的拷貝:傳遞或存儲對象時未使用引用或移動,從而導致不必要的拷貝開銷。
  • 選擇不當的容器:在需要頻繁插入或刪除的場景中使用 std::vector 而不是 std::list 等更合適的容器。
  • 算法選擇不當:例如,在已排序的數據集中使用線性搜索而不是二分搜索。
    在這里插入圖片描述

性能優化策略

以下幾種策略可以顯著提升 STL 的性能:

1. 選擇合適的容器

選擇適合特定使用場景的容器是提升性能的第一步。不同的容器在不同操作上的表現差異巨大。

常見容器比較

操作std::vectorstd::liststd::dequestd::map / std::unordered_map
隨機訪問O(1)不支持O(1)不支持
中間插入/刪除O(n)O(1)O(n)-
尾部插入/刪除O(1) 攤銷O(1)O(1)-
查找O(n)O(n)O(n)O(log n) / O(1)

示例:std::vector vs std::list

假設需要在集合中頻繁地在中間插入元素:

#include <vector>
#include <list>
#include <algorithm>void insert_elements_vector(std::vector<int>& vec, const std::vector<int>& elements) {for (const auto& el : elements) {vec.insert(vec.begin() + vec.size() / 2, el); // 每次插入中間位置,O(n) 時間}
}void insert_elements_list(std::list<int>& lst, const std::vector<int>& elements) {auto it = lst.begin();std::advance(it, lst.size() / 2);for (const auto& el : elements) {lst.insert(it, el); // 每次插入中間位置,O(1) 時間}
}

優化啟示:如果應用在中間頻繁插入或刪除元素,使用 std::list 可以提供更好的性能。然而,對于大多數其他用例,std::vector 因其更好的緩存局部性和更低的內存開銷,表現更優。

2. 減少內存分配

動態內存分配是昂貴的,減少內存分配的次數和開銷可以顯著提升性能。

優化技巧

  • 預留空間(Reserve):在容器已知大小或可以估算時,預先分配足夠的內存,避免多次重新分配。
  • 合理縮減(Shrink-to-Fit):在容器大小大幅變化時,適時縮減容器容量,減少內存占用。

示例:使用 reserve 優化 std::vector

#include <vector>
#include <iostream>int main() {std::vector<int> vec;vec.reserve(1000); // 預先分配 1000 個元素的空間for (int i = 0; i < 1000; ++i) {vec.emplace_back(i);}std::cout << "Vector size: " << vec.size() << std::endl;return 0;
}

優勢:通過預留空間,可以避免 std::vector 在插入元素時多次重新分配內存,從而提升性能。

3. 避免不必要的拷貝

拷貝大型對象會帶來顯著的性能開銷,使用引用、指針或移動語義可以避免這些開銷。

示例:函數參數傳遞時避免拷貝

#include <vector>// 不優化的版本:傳值,會復制整個 vector
void process_vector(std::vector<int> vec) {// 處理代碼
}// 優化后的版本:傳遞常量引用,避免拷貝
void process_vector(const std::vector<int>& vec) {// 處理代碼
}

優勢:通過傳遞引用,可以避免在函數調用過程中復制整個容器,從而節省大量時間和內存。

4. 利用移動語義和原位構造

C++11 引入的移動語義和原位構造(emplacement)功能,可以減少臨時對象的創建和拷貝操作。

示例:使用 std::moveemplace_back

#include <vector>
#include <string>int main() {std::vector<std::string> vec;std::string temp = "Hello, World!";// 拷貝vec.push_back(temp); // 復制 temp// 移動vec.push_back(std::move(temp)); // 移動 temp// 原位構造vec.emplace_back("直接構造字符串"); // 直接在 vector 中構造字符串return 0;
}

優勢:移動語義和原位構造可以減少不必要的拷貝操作,特別是對于復雜或大型對象,能顯著提升性能。

5. 優化算法選擇

選擇合適的 STL 算法,結合合適的數據結構,可以顯著提升性能。

示例:使用二分搜索替代線性搜索

#include <vector>
#include <algorithm>int main() {std::vector<int> sorted_vec = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};// 線性搜索auto it = std::find(sorted_vec.begin(), sorted_vec.end(), 7); // O(n) 時間// 二分搜索bool found = std::binary_search(sorted_vec.begin(), sorted_vec.end(), 7); // O(log n) 時間return 0;
}

優化啟示:在數據已經排序的情況下,使用 std::binary_search 這種時間復雜度更低的算法,可以顯著提升查找性能。

6. 提高緩存局部性

數據結構的內存布局對緩存局部性有直接影響,進而影響性能。盡量選擇內存連續布局的容器,如 std::vector,可以提高緩存命中率,加快訪問速度。

示例:使用 std::vector 進行迭代處理

#include <vector>
#include <list>void process_vector(std::vector<int>& vec) {for(auto& el : vec) {// 處理元素}
}void process_list(std::list<int>& lst) {for(auto& el : lst) {// 處理元素}
}

優勢std::vector 的連續存儲特性使其在迭代過程中利用了緩存預取機制,從而比基于節點的 std::list 擁有更快的迭代速度。

7. 使用自定義分配器

STL 容器默認使用全局分配器,適用于通用情況。但在特定場景下,自定義分配器可以優化內存分配模式,減少碎片和分配開銷。

示例:簡單的內存池分配器

#include <memory>
#include <vector>template <typename T>
struct PoolAllocator {using value_type = T;PoolAllocator() = default;template <typename U>PoolAllocator(const PoolAllocator<U>&) {}T* allocate(std::size_t n) {// 簡單的內存池分配,實際實現應更復雜return static_cast<T*>(::operator new(n * sizeof(T)));}void deallocate(T* p, std::size_t) noexcept {::operator delete(p);}
};int main() {std::vector<int, PoolAllocator<int>> vec;vec.reserve(1000); // 使用自定義分配器預分配空間// 執行其他操作return 0;
}

優勢:自定義分配器可以針對特定的內存使用模式進行優化,如固定大小分配、多線程分配等,從而提升整體性能。

實戰案例:優化高性能日志系統

為了更直觀地展示 STL 性能優化策略的應用,以下將以一個高性能日志系統為例,詳細說明優化過程。

初始實現

假設有一個簡單的日志系統,用于實時記錄事件:

#include <vector>
#include <string>
#include <mutex>struct LogEntry {int id;std::string message;
};class Logger {
public:void log(int id, const std::string& message) {std::lock_guard<std::mutex> lock(mutex_);logs_.emplace_back(LogEntry{id, message});}const std::vector<LogEntry>& get_logs() const { return logs_; }private:std::vector<LogEntry> logs_;mutable std::mutex mutex_;
};

潛在問題

  1. 無限增長logs_ 容器可能無限增長,導致內存問題。
  2. 線程爭用:高頻率的日志記錄導致多個線程爭用鎖,影響性能。
  3. 字符串拷貝開銷:傳遞和存儲字符串時的拷貝操作可能帶來額外開銷。

優化步驟

針對上述問題,可以采取以下優化措施:

1. 預先分配內存

通過 reserve 預先分配足夠的空間,避免多次內存重新分配。

Logger() {logs_.reserve(1000000); // 預先分配空間,避免多次 reallocation
}
2. 減少鎖爭用

使用線程本地存儲(Thread-Local Storage)或分離的日志緩沖區,減少多個線程間對同一鎖的爭用。

使用線程本地緩沖區

#include <thread>
#include <vector>
#include <string>
#include <mutex>struct LogEntry {int id;std::string message;
};class Logger {
public:void log(int id, std::string&& message) {auto& buffer = get_buffer();buffer.emplace_back(LogEntry{ id, std::move(message) });}std::vector<LogEntry> collect_logs() {std::vector<LogEntry> all_logs;std::lock_guard<std::mutex> lock(mutex_);for(auto& buffer : buffers_) {all_logs.insert(all_logs.end(),std::make_move_iterator(buffer.begin()),std::make_move_iterator(buffer.end()));buffer.clear();}return all_logs;}private:static const int THREAD_COUNT = 4;std::vector<LogEntry> buffers_[THREAD_COUNT];std::mutex mutex_;std::vector<LogEntry>& get_buffer() {size_t thread_id = std::hash<std::thread::id>()(std::this_thread::get_id()) % THREAD_COUNT;return buffers_[thread_id];}
};

說明:通過為每個線程分配獨立的日志緩沖區,避免多個線程對同一個鎖的爭用,提升日志記錄性能。

3. 避免字符串拷貝

利用移動語義減少字符串拷貝開銷。

void log(int id, std::string message) { // 按值傳遞,允許移動std::lock_guard<std::mutex> lock(mutex_);logs_.emplace_back(LogEntry{ id, std::move(message) }); // 利用移動語義
}

說明:通過將字符串參數按值傳遞,并在插入時使用 std::move,可以避免不必要的拷貝操作,提升性能。

優化后的實現

結合上述優化措施,最終的 Logger 類如下:

#include <vector>
#include <string>
#include <mutex>
#include <thread>
#include <deque>
#include <atomic>struct LogEntry {int id;std::string message;
};class Logger {
public:Logger() {logs_.reserve(1000000); // 預先分配空間}void log(int id, std::string&& message) {auto& buffer = get_buffer();buffer.emplace_back(LogEntry{ id, std::move(message) });}std::vector<LogEntry> collect_logs() {std::vector<LogEntry> all_logs;std::lock_guard<std::mutex> lock(mutex_);for(auto& buffer : buffers_) {all_logs.insert(all_logs.end(),std::make_move_iterator(buffer.begin()),std::make_move_iterator(buffer.end()));buffer.clear();}return all_logs;}private:static const int THREAD_COUNT = 4;std::vector<LogEntry> buffers_[THREAD_COUNT];std::mutex mutex_;std::vector<LogEntry>& get_buffer() {size_t thread_id = std::hash<std::thread::id>()(std::this_thread::get_id()) % THREAD_COUNT;return buffers_[thread_id];}
};

優化效果

  • 減少內存重新分配:通過預先分配足夠的空間,減少了 std::vector 的多次內存分配。
  • 降低鎖爭用:通過采用線程本地緩沖區,減少了多個線程對同一互斥鎖的爭用,提高了并行性能。
  • 消除不必要的拷貝:利用移動語義和原位構造,減少了字符串拷貝帶來的性能開銷。

最佳實踐與總結

通過以上的討論與實戰案例,可以總結出以下 STL 性能優化的最佳實踐:

  1. 優先選擇 std::vector:由于其優秀的緩存局部性和較低的內存開銷,std::vector 是大多數情況下的首選容器。
  2. 合理預留空間:在已知或可估算的情況下,使用 reserve 預先分配容器空間,避免多次內存分配。
  3. 使用移動語義和原位構造:盡量使用 std::moveemplace_back 等函數,減少不必要的對象拷貝。
  4. 選擇合適的算法和數據結構:根據具體需求,選擇時間復雜度和空間復雜度更優的算法和數據結構。
  5. 提高緩存局部性:優先選擇內存連續布局的容器,提升數據訪問的緩存命中率。
  6. 減少鎖爭用:在多線程環境下,設計數據結構和訪問模式以減少同步開銷,如使用線程本地存儲或分離的緩沖區。
  7. 使用自定義分配器:在特定場景下,定制內存分配策略以提升內存分配效率和降低內存碎片。
  8. 持續進行性能分析與優化:通過性能分析工具不斷監測和優化代碼,確保優化措施實際有效。

總結:STL 是 C++ 中強大的工具,了解其性能特性并合理使用,可以顯著提升項目的性能表現。通過選擇合適的容器、優化內存管理、減少不必要的拷貝以及合理選擇算法,開發者可以有效克服 STL 帶來的性能瓶頸,實現高效、穩定的應用程序。

參考資料

  • C++ STL 官方文檔
  • Effective STL
  • C++ Primer

標簽

C++、STL、性能優化、編程技巧

版權聲明

本文版權歸作者所有,未經允許,請勿轉載。

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

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

相關文章

大模型如何優化數字人的實時交互與情感表達

標題:大模型如何優化數字人的實時交互與情感表達 內容:1.摘要 隨著人工智能技術的飛速發展&#xff0c;數字人在多個領域的應用愈發廣泛&#xff0c;其實時交互與情感表達能力成為提升用戶體驗的關鍵因素。本文旨在探討大模型如何優化數字人的實時交互與情感表達。通過分析大模…

qt designer 軟件主題程序設計

對于使用Qt Designer設計的界面&#xff0c;主題切換的實現需要結合Qt的信號槽機制、樣式表動態加載以及資源管理。以下是針對Qt Designer UI的詳細解決方案&#xff1a; 一、UI文件與主題系統的整合架構 二、核心實現步驟 1. 動態樣式表加載系統 // ThemeManager.h class …

一、STM32簡介

一、實驗器材介紹 二、STM32簡介 1.STM32 名詞解釋 STM32是ST公司基于ARM Cortex-M內核開發的32位微控制器。 ST&#xff0c;指ST公司&#xff08;意法半導體&#xff09;;M&#xff0c;MicroController 微控制器&#xff08;MCU,MicroController Unit 微控制器單元/單片機&…

JVM虛擬機篇(一)深入理解JVM:組成部分、運行流程及程序計數器詳解

JVM虛擬機篇&#xff08;一&#xff09;深入理解JVM&#xff1a;組成部分、運行流程及程序計數器詳解 JVM虛擬機篇&#xff08;一&#xff09;深入理解JVM&#xff1a;組成部分、運行流程及程序計數器詳解一、引言二、JVM的組成部分2.1 類加載子系統2.2 運行時數據區2.3 執行引…

elementui的默認樣式修改

今天用element ui &#xff0c;做了個消息提示&#xff0c;發現提示的位置總是在上面&#xff0c;如圖&#xff1a; 可是我想讓提示的位置到下面來&#xff0c;該怎么辦&#xff1f; 最后還是看了官方的api 原來有個自定義樣式屬性 customClass 設置下就好了 js代碼 css代碼 效…

游戲引擎學習第204天

回顧并為今天的內容做鋪墊 好&#xff0c;現在開始這一集。今天我們將進行一些用戶界面編程&#xff0c;覺得這是一個展示如何編寫這類代碼的好時機。很多人對如何做用戶界面代碼都很好奇&#xff0c;所以展示一下如何編寫是非常有意義的。 我之所以在現在的這個地方做這些工…

我的世界1.20.1forge模組開發進階教程——TerraBlender

TerraBlender介紹 從模組開發者的視角來看,TerraBlender為Minecraft生物群系類模組的開發提供了全方位的技術支持,顯著降低了開發門檻并提升了模組的質量與擴展性: 跨平臺兼容性架構支持Forge/Fabric/Quilt/NeoForge四大主流加載器,開發者無需為不同平臺單獨適配代碼客戶端…

借助mcpo在open-webui中使用mcp

open-webui前幾天發布了0.6版本&#xff0c;我立即進行了升級。新版本中一個重要功能是通過mcpo方式支持了mcp server。本文將介紹mcpo是什么&#xff0c;以及如何在open-webui中使用它。同時&#xff0c;我也會分享幾個在接入過程中遇到的問題及解決方案。 首先來介紹mcpo&…

安裝gpu版本的dgl

1.先去網址&#xff0c;找到對應版本的dgl,然后下載到本地。 dgl-whl下載地址 我的是python 3.8 &#xff0c;cuda 11.6. windows 2.在虛擬環境里 輸入 pip install E:\dgl-1.0.2cu116-cp38-cp38-win_amd64.whl &#xff08;因為我下載到E盤里了&#xff09; 這樣GPU版本的d…

PyTorch使用(7)-張量常見運算函數

1. 基本數學運算 1.1 平方根和冪運算 import torchx torch.tensor([4.0, 9.0, 16.0])# 平方根 sqrt_x torch.sqrt(x) # tensor([2., 3., 4.])# 平方 square_x torch.square(x) # tensor([16., 81., 256.])# 任意冪次 pow_x torch.pow(x, 3) # tensor([64., 729., 4096…

Nginx功能及應用全解:從負載均衡到反向代理的全面剖析

Nginx作為一款開源的高性能HTTP服務器和反向代理服務器&#xff0c;憑借其高效的資源利用率和靈活的配置方式&#xff0c;已成為互聯網領域中最受歡迎的Web服務器之一。無論是作為HTTP服務器、負載均衡器&#xff0c;還是作為反向代理和緩存服務器&#xff0c;Nginx的多種功能廣…

安徽京準:NTP時間同步服務器操作使用說明

安徽京準&#xff1a;NTP時間同步服務器操作使用說明 3.1 連接天線 天線連接到“ANT”口。 3.2 連接電源 將220V電源線連到AC220V座上或將電源適配器&#xff08;7.5V~12V&#xff09;接到DC口上。也可以同時接上&#xff0c;提高供電可靠性。 3.3 LAN網口 網線連接到NTP…

Java項目之基于ssm的懷舊唱片售賣系統(源碼+文檔)

項目簡介 懷舊唱片售賣系統實現了以下功能&#xff1a; 用戶信息管理&#xff1a; 用戶信息新增&#xff1a;添加新用戶的信息。 用戶信息修改&#xff1a;對現有用戶信息進行修改。 商品信息管理&#xff1a; 商品信息添加&#xff1a;增加新的商品&#xff08;唱片&#x…

基于 Python 的自然語言處理系列(70):檢索增強生成(RAG)

1. 什么是 RAG&#xff1f; 在許多大模型&#xff08;LLM&#xff09;應用場景中&#xff0c;我們需要使用特定的用戶數據&#xff0c;而這些數據并未包含在模型的訓練集中。檢索增強生成&#xff08;Retrieval Augmented Generation&#xff0c;RAG&#xff09;是一種有效的解…

CAD插件實現:所有文字顯示到列表、縮放、編輯——CAD-c#二次開發

當圖中有大量文字&#xff0c;需要全部顯示到一個列表時并縮放到需要的文字時&#xff0c;可采用插件實現&#xff0c;效果如下&#xff1a; 附部分代碼如下&#xff1a; private void BtnSelectText_Click(object sender, EventArgs e){var doc Application.DocumentManager.…

Systemd構建自動化備份服務與外部存儲管理

實訓背景 你是一家數據公司的系統管理員&#xff0c;需設計一套自動化備份系統&#xff0c;滿足以下需求&#xff1a; 定期備份&#xff1a;每周日凌晨1點將 /data 目錄壓縮備份到 /backups。外部存儲掛載&#xff1a;插入USB設備時自動掛載到 /mnt/usb&#xff0c;并觸發增量…

PostgreSQL中根據另一表的值來更新一個字段

UPDATE table1 SET value t2.new_value FROM table2 t2 WHERE table1.id t2.reference_id; 解釋 UPDATE table1&#xff1a;指定要更新的表&#xff0c;不要用別名。 SET value t2.new_value&#xff1a;設置要更新的字段及其新值&#xff0c;這里新值來自 table2。也可更…

#SVA語法滴水穿石# (000)斷言基本概念和背景

一、前言 隨著數字電路規模越來越大、設計越來越復雜,使得對設計的功能驗證越來越重要。首先,我們要明白為什么要對設計進行驗證?驗證有什么作用?例如,在用FPGA進行設計時,我們并不能確保設計出來的東西沒有功能上的漏洞,因此在設計后我們都會對其進行驗證仿真。換句話說…

Git 從入門到精通(開源協作特別版)

&#x1f9e0; Git 從入門到精通&#xff08;開源協作特別版&#xff09; ? 基礎命令 &#x1f9f0; 高級用法 &#x1f6e0;? 開源實戰技巧 &#x1f30d; GitHub 社區協作 適合&#xff1a;從0開始 → 熟練開發者 → 參與/維護開源項目 &#x1f530; 第1章&#xff1a;…

【SQL】取消sql某一列的唯一值key值的方法

在插入數據到sql時&#xff0c;遇到了這個問題&#xff1a; Duplicate entry ‘XXX’ for key 起因是&#xff1a; 我之前設計表的時候&#xff0c;手動給product_title 這個列加了一個key&#xff0c; key 是這個字段的唯一鍵約束&#xff0c;就不能重復在這一列存入重復的數…