Redis作緩存時存在的問題及其解決方案

Redis最常用的一個場景就是作為緩存,本文主要探討Redis作為緩存,在實踐中可能會有哪些問題?比如一致性, 穿擊, 穿透, 雪崩, 污染等。

為什么要理解Redis緩存問題

在高并發的業務場景下,數據庫大多數情況都是用戶并發訪問最薄弱的環節。所以,就需要使用redis做一個緩沖操作,讓請求先訪問到redis,而不是直接訪問Mysql等數據庫。這樣可以大大緩解數據庫的壓力。

當緩存庫出現時,必須要考慮如下問題:

  • 緩存穿透
  • 緩存穿擊
  • 緩存雪崩
  • 緩存污染(或者滿了)
  • 緩存和數據庫一致性

緩存穿透

1. 問題本質

緩存穿透是指 查詢一個不存在的數據,導致請求繞過緩存直接訪問數據庫。由于數據庫也沒有該數據,無法回填緩存,導致惡意請求反復穿透緩存壓垮數據庫。

2. 攻擊場景

  • 惡意請求:如頻繁查詢 id=-1 或超大ID等不存在的數據。
  • 漏洞利用:利用業務接口缺陷偽造非法參數。

3. 解決方案對比

(1) 參數校驗(最基礎防御)

  • 實現:在API層攔截明顯非法請求(如非正整數ID、超長字符串)。
  • 優點:簡單高效,攔截80%低級攻擊。
  • 缺點:無法防御精心構造的合法參數(如隨機不存在的ID)。

示例代碼

if (id <= 0 || id > MAX_ID) {throw new IllegalArgumentException("非法ID");
}

(2) 緩存空值(簡單有效)

  • 實現:數據庫未命中時,緩存 key-null 并設置短TTL(如30秒)。
  • 優點:避免同一Key反復穿透。
  • 缺點
    • 內存浪費(需存儲大量空值)。
    • 短時間仍可能被攻擊(如海量不同Key)。

示例代碼

String data = cache.get(key);
if (data == null) {data = db.query(key);if (data == null) { // 數據庫不存在cache.set(key, "NULL", 30); // 緩存空值} else {cache.set(key, data, 3600);}
}
return "NULL".equals(data) ? null : data;

(3) 布隆過濾器(終極防御)

  • 原理
    • 使用位數組和哈希函數,判斷元素 “一定不存在”“可能存在”
    • 將所有合法Key預先存入布隆過濾器,查詢前先檢查過濾器。
  • 優點
    • 內存占用極低(1億Key約占用12MB)。
    • 攔截不存在Key的效率接近O(1)。
  • 缺點
    • 誤判率:可能將不存在的Key誤判為存在(可通過調整哈希函數和數組大小控制)。
    • 無法刪除:傳統布隆過濾器不支持刪除(需用變種如Counting Bloom Filter)。

實現示例(Guava庫):

BloomFilter<String> filter = BloomFilter.create(Funnels.stringFunnel(), 1000000, // 預期元素數量0.01     // 誤判率
);// 預熱數據
for (String validKey : allValidKeys) {filter.put(validKey);
}// 查詢前檢查
if (!filter.mightContain(key)) {return null; // 絕對不存在
}

緩存擊穿

緩存擊穿(Cache Breakdown)是緩存系統中的一種典型問題,通常發生在高并發訪問某個熱點數據時,該數據恰好緩存過期,導致大量請求瞬間穿透緩存直接壓垮數據庫。

問題本質

  • 觸發條件
    • 緩存中的熱點Key過期(如秒殺商品、首頁頭條)。
    • 同時有大量并發請求訪問該Key。
  • 直接后果
    • 所有請求繞過緩存,直接查詢數據庫。
    • 數據庫短時間內承受極高QPS(可能崩潰)。

解決方案及實現

方案1:熱點數據永不過期

  • 適用場景:極高頻訪問且數據更新不頻繁(如配置信息)。
  • 實現方式
    • 物理永不過期:緩存不設TTL。
    • 邏輯續期:異步檢查數據版本,有更新時重新加載緩存。
  • 優點:徹底避免擊穿。
  • 缺點:數據更新不及時(需結合其他機制如消息隊列通知)。
// 示例:邏輯續期(偽代碼)
public String getHotData(String key) {String data = cache.get(key);if (data == null) {data = loadFromDB(key);cache.set(key, data); // 不設置過期時間scheduleRefresh(key); // 異步定時刷新}return data;
}

方案2:互斥鎖(Mutex Lock)

  • 核心思想:只允許一個請求重建緩存,其他請求阻塞或輪詢等待。
  • 實現方式
    • 分布式鎖:如Redis的 SETNX(推薦)。
    • 本地鎖:僅單機有效(如Java的 synchronized)。

分布式鎖實現(Redis)

public String getDataWithLock(String key) {String data = cache.get(key);if (data == null) {String lockKey = "lock:" + key;try {// 嘗試獲取分布式鎖(SETNX + 超時)boolean locked = redis.setnx(lockKey, "1", 10, TimeUnit.SECONDS);if (locked) {data = loadFromDB(key);          // 查數據庫cache.set(key, data, 1, TimeUnit.HOURS); // 回填緩存} else {Thread.sleep(100);              // 短暫等待后重試return getDataWithLock(key);    // 遞歸調用}} finally {redis.del(lockKey);                 // 釋放鎖}}return data;
}
  • 優點:保證數據庫不會被重復查詢。
  • 缺點
    • 鎖競爭可能導致部分請求延遲。
    • 需處理鎖超時和死鎖問題。

方案3:限流與熔斷降級

  • 適用場景:突發流量無法用緩存完全攔截時(如明星出軌新聞)。
  • 實現工具
    • 限流:Guava RateLimiter、Sentinel、Nginx。
    • 熔斷:Hystrix、Resilience4j。
  • 示例
// Guava 限流(每秒10個請求)
RateLimiter limiter = RateLimiter.create(10.0);
public String getDataWithRateLimit(String key) {if (!limiter.tryAcquire()) {return "系統繁忙,請稍后再試"; // 降級響應}return getData(key); // 正常邏輯
}
  • 優點:保護數據庫不被突發流量擊垮。
  • 缺點:可能誤傷正常用戶請求。

緩存雪崩

問題本質

緩存雪崩是指緩存中數據大批量到過期時間,而查詢數據量巨大,引起數據庫壓力過大甚至down機。和緩存擊穿不同的是,緩存擊穿指并發查同一條數據,緩存雪崩是不同數據都過期了,很多數據都查不到從而查數據庫。

觸發條件

  • 大批量Key同時過期:如緩存數據初始化時設置了相同的TTL(如默認1小時)。
  • 緩存服務宕機:Redis集群崩潰,所有請求直接打到數據庫。

解決方案

  1. 緩存數據的過期時間設置隨機,防止同一時間大量數據過期現象發生。
  2. 如果緩存數據庫是分布式部署,將熱點數據均勻分布在不同的緩存數據庫中。(緩存服務高可用)
  3. 設置熱點數據永遠不過期。

緩存污染(或滿了)

緩存污染問題說的是緩存中一些只會被訪問一次或者幾次的的數據,被訪問完后,再也不會被訪問到,但這部分數據依然留存在緩存中,消耗緩存空間。

緩存污染會隨著數據的持續增加而逐漸顯露,隨著服務的不斷運行,緩存中會存在大量的永遠不會再次被訪問的數據。緩存空間是有限的,如果緩存空間滿了,再往緩存里寫數據時就會有額外開銷,影響Redis性能。這部分額外開銷主要是指寫的時候判斷淘汰策略,根據淘汰策略去選擇要淘汰的數據,然后進行刪除操作。

最大緩存設置多大

系統的設計選擇是一個權衡的過程:大容量緩存是能帶來性能加速的收益,但是成本也會更高,而小容量緩存不一定就起不到加速訪問的效果。一般來說,我會建議把緩存容量設置為總數據量的 15% 到 30%,兼顧訪問性能和內存空間開銷

對于 Redis 來說,一旦確定了緩存最大容量,比如 4GB,你就可以使用下面這個命令來設定緩存的大小了:

CONFIG SET maxmemory 4gb

不過,緩存被寫滿是不可避免的, 所以需要數據淘汰策略。

緩存淘汰策略

Redis共支持八種淘汰策略,分別是noeviction、volatile-random、volatile-ttl、volatile-lru、volatile-lfu、allkeys-lru、allkeys-random 和 allkeys-lfu 策略。

怎么理解呢?主要看分三類看:

  • 不淘汰
    • noeviction (v4.0后默認的)
  • 對設置了過期時間的數據中進行淘汰
    • 隨機:volatile-random
    • ttl:volatile-ttl
    • lru:volatile-lru
    • lfu:volatile-lfu
  • 全部數據進行淘汰
    • 隨機:allkeys-random
    • lru:allkeys-lru
    • lfu:allkeys-lfu

具體對照下:

1. noeviction

該策略是Redis的默認策略。在這種策略下,一旦緩存被寫滿了,再有寫請求來時,Redis 不再提供服務,而是直接返回錯誤。這種策略不會淘汰數據,所以無法解決緩存污染問題。一般生產環境不建議使用。

其他七種規則都會根據自己相應的規則來選擇數據進行刪除操作。

2. volatile-random

這個算法比較簡單,在設置了過期時間的鍵值對中,進行隨機刪除。因為是隨機刪除,無法把不再訪問的數據篩選出來,所以可能依然會存在緩存污染現象,無法解決緩存污染問題。

3. volatile-ttl

這種算法判斷淘汰數據時參考的指標比隨機刪除時多進行一步過期時間的排序。Redis在篩選需刪除的數據時,越早過期的數據越優先被選擇。

4. volatile-lru

LRU算法:LRU 算法的全稱是 Least Recently Used,按照最近最少使用的原則來篩選數據,優先淘汰 最久未被訪問 的數據(基于最近訪問時間)。這種模式下會使用 LRU 算法篩選設置了過期時間的鍵值對。

Redis優化的LRU算法實現

Redis會記錄每個數據的最近一次被訪問的時間戳。在Redis在決定淘汰的數據時,第一次會隨機選出 N 個數據,把它們作為一個候選集合。接下來,Redis 會比較這 N 個數據的 lru 字段,把 lru 字段值最小的數據從緩存中淘汰出去。通過隨機讀取待刪除集合,可以讓Redis不用維護一個巨大的鏈表,也不用操作鏈表,進而提升性能。

Redis 選出的數據個數 N,通過 配置參數 maxmemory-samples 進行配置。個數N越大,則候選集合越大,選擇到的最久未被使用的就更準確,N越小,選擇到最久未被使用的數據的概率也會隨之減小。

5. volatile-lfu

優先淘汰 訪問頻率最低 的數據,頻率相同則淘汰最久未訪問的。會使用 LFU 算法選擇設置了過期時間的鍵值對。

LFU 算法:LFU 緩存策略是在 LRU 策略基礎上,為每個數據增加了一個計數器,來統計這個數據的訪問次數。當使用 LFU 策略篩選淘汰數據時,首先會根據數據的訪問次數進行篩選,把訪問次數最低的數據淘汰出緩存。如果兩個數據的訪問次數相同,LFU 策略再比較這兩個數據的訪問時效性,把距離上一次訪問時間更久的數據淘汰出緩存。 Redis的LFU算法實現:

當 LFU 策略篩選數據時,Redis 會在候選集合中,根據數據 lru 字段的后 8bit 選擇訪問次數最少的數據進行淘汰。當訪問次數相同時,再根據 lru 字段的前 16bit 值大小,選擇訪問時間最久遠的數據進行淘汰。

Redis 只使用了 8bit 記錄數據的訪問次數,而 8bit 記錄的最大值是 255,這樣在訪問快速的情況下,如果每次被訪問就將訪問次數加一,很快某條數據就達到最大值255,可能很多數據都是255,那么退化成LRU算法了。所以Redis為了解決這個問題,實現了一個更優的計數規則,并可以通過配置項,來控制計數器增加的速度。

參數

lfu-log-factor ,用計數器當前的值乘以配置項 lfu_log_factor 再加 1,再取其倒數,得到一個 p 值;然后,把這個 p 值和一個取值范圍在(0,1)間的隨機數 r 值比大小,只有 p 值大于 r 值時,計數器才加 1。

lfu-decay-time, 控制訪問次數衰減。LFU 策略會計算當前時間和數據最近一次訪問時間的差值,并把這個差值換算成以分鐘為單位。然后,LFU 策略再把這個差值除以 lfu_decay_time 值,所得的結果就是數據 counter 要衰減的值。

lfu-log-factor設置越大,遞增概率越低,lfu-decay-time設置越大,衰減速度會越慢。

我們在應用 LFU 策略時,一般可以將 lfu_log_factor 取值為 10。 如果業務應用中有短時高頻訪問的數據的話,建議把 lfu_decay_time 值設置為 1。可以快速衰減訪問次數。

volatile-lfu 策略是 Redis 4.0 后新增。

6. allkeys-lru

使用 LRU 算法在所有數據中進行篩選。具體LFU算法跟上述 volatile-lru 中介紹的一致,只是篩選的數據范圍是全部緩存,這里就不在重復。

7. allkeys-random

從所有鍵值對中隨機選擇并刪除數據。volatile-random 跟 allkeys-random算法一樣,隨機刪除就無法解決緩存污染問題。

8. allkeys-lfu

使用 LFU 算法在所有數據中進行篩選。具體LFU算法跟上述 volatile-lfu 中介紹的一致,只是篩選的數據范圍是全部緩存,這里就不在重復。

allkeys-lfu 策略是 Redis 4.0 后新增。

數據庫和緩存一致性

問題來源

使用redis做一個緩沖操作,讓請求先訪問到redis,而不是直接訪問MySQL等數據庫:

讀取緩存步驟一般沒有什么問題,但是一旦涉及到數據更新:數據庫和緩存更新,就容易出現緩存(Redis)和數據庫(MySQL)間的數據一致性問題。

不管是先寫MySQL數據庫,再刪除Redis緩存;還是先刪除緩存,再寫庫,都有可能出現數據不一致的情況。舉一個例子:

1.如果刪除了緩存Redis,還沒有來得及寫庫MySQL,另一個線程就來讀取,發現緩存為空,則去數據庫中讀取數據寫入緩存,此時緩存中為臟數據。

2.如果先寫了庫,在刪除緩存前,寫庫的線程宕機了,沒有刪除掉緩存,則也會出現數據不一致情況。

因為寫和讀是并發的,沒法保證順序,就會出現緩存和數據庫的數據不一致的問題。

4種相關模式

更新緩存的的Design Pattern有四種:Cache aside, Read through, Write through, Write behind caching;

節選最最常用的Cache Aside Pattern, 總結來說就是

  • 讀的時候,先讀緩存,緩存沒有的話,就讀數據庫,然后取出數據后放入緩存,同時返回響應。
  • 更新的時候,先更新數據庫,然后再刪除緩存。

其具體邏輯如下:

  • 失效:應用程序先從cache取數據,沒有得到,則從數據庫中取數據,成功后,放到緩存中。
  • 命中:應用程序從cache中取數據,取到后返回。
  • 更新:先把數據存到數據庫中,成功后,再讓緩存失效。

注意,我們的更新是先更新數據庫,成功后,讓緩存失效。那么,這種方式是否可以沒有文章前面提到過的那個問題呢?我們可以腦補一下。

一個是查詢操作,一個是更新操作的并發,首先,沒有了刪除cache數據的操作了,而是先更新了數據庫中的數據,此時,緩存依然有效,所以,并發的查詢操作拿的是沒有更新的數據,但是,更新操作馬上讓緩存的失效了,后續的查詢操作再把數據從數據庫中拉出來。而不會像文章開頭的那個邏輯產生的問題,后續的查詢操作一直都在取老的數據。

這是標準的design pattern,包括Facebook的論文《Scaling Memcache at Facebook》也使用了這個策略。為什么不是寫完數據庫后更新緩存?你可以看一下Quora上的這個問答《Why does Facebook use delete to remove the key-value pair in Memcached instead of updating the Memcached during write request to the backend?》,主要是怕兩個并發的寫操作導致臟數據。

那么,是不是Cache Aside這個就不會有并發問題了?不是的,比如,一個是讀操作,但是沒有命中緩存,然后就到數據庫中取數據,此時來了一個寫操作,寫完數據庫后,讓緩存失效,然后,之前的那個讀操作再把老的數據放進去,所以,會造成臟數據。

但,這個case理論上會出現,不過,實際上出現的概率可能非常低,因為這個條件需要發生在讀緩存時緩存失效,而且并發著有一個寫操作。而實際上數據庫的寫操作會比讀操作慢得多,而且還要鎖表,而讀操作必需在寫操作前進入數據庫操作,而又要晚于寫操作更新緩存,所有的這些條件都具備的概率基本并不大。

所以,這也就是Quora上的那個答案里說的,要么通過2PC或是Paxos協議保證一致性,要么就是拼命的降低并發時臟數據的概率,而Facebook使用了這個降低概率的玩法,因為2PC太慢,而Paxos太復雜。當然,最好還是為緩存設置上過期時間。

方案一:隊列 + 重試機制

流程如下所示

  • 更新數據庫數據;
  • 緩存因為種種問題刪除失敗
  • 將需要刪除的key發送至消息隊列
  • 自己消費消息,獲得需要刪除的key
  • 繼續重試刪除操作,直到成功

然而,該方案有一個缺點,對業務線代碼造成大量的侵入。于是有了方案二,在方案二中,啟動一個訂閱程序去訂閱數據庫的binlog,獲得需要操作的數據。在應用程序中,另起一段程序,獲得這個訂閱程序傳來的信息,進行刪除緩存操作。

方案二:異步更新緩存(基于訂閱binlog的同步機制)

  1. 技術整體思路

MySQL binlog增量訂閱消費+消息隊列+增量數據更新到redis

1)讀Redis:熱數據基本都在Redis

2)寫MySQL: 增刪改都是操作MySQL

3)更新Redis數據:MySQ的數據操作binlog,來更新到Redis

  1. Redis更新

1)數據操作主要分為兩大塊:

  • 一個是全量(將全部數據一次寫入到redis)
  • 一個是增量(實時更新)

這里說的是增量,指的是mysql的update、insert、delate變更數據。

2)讀取binlog后分析 ,利用消息隊列,推送更新各臺的redis緩存數據

這樣一旦MySQL中產生了新的寫入、更新、刪除等操作,就可以把binlog相關的消息推送至Redis,Redis再根據binlog中的記錄,對Redis進行更新。

其實這種機制,很類似MySQL的主從備份機制,因為MySQL的主備也是通過binlog來實現的數據一致性。

這里可以結合使用canal(阿里的一款開源框架),通過該框架可以對MySQL的binlog進行訂閱,而canal正是模仿了mysql的slave數據庫的備份請求,使得Redis的數據更新達到了相同的效果。

當然,這里的消息推送工具你也可以采用別的第三方:kafka、rabbitMQ等來實現消息推送更新Redis。

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

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

相關文章

day17 力扣654.最大二叉樹 力扣617.合并二叉樹 力扣700.二叉搜索樹中的搜索 力扣98.驗證二叉搜索樹

最大二叉樹給定一個不重復的整數數組 nums 。 最大二叉樹 可以用下面的算法從 nums 遞歸地構建:創建一個根節點&#xff0c;其值為 nums 中的最大值。遞歸地在最大值 左邊 的 子數組前綴上 構建左子樹。遞歸地在最大值 右邊 的 子數組后綴上 構建右子樹。返回 nums 構建的 最大…

天地圖前端實現geoJson與wkt格式互轉

geoJson與wkt都是WebGIS開發中經常用到的格式&#xff0c;天地圖行政區劃邊界接口返回的是wkt格式數據&#xff0c;需要轉換一下。 安裝插件&#xff1a;terraformer/wkt npm install terraformer/wkt 兩個函數&#xff1a; .wktToGeoJSON(WKT) ? object.geojsonToWKT(Geo…

(1-7-3)數據庫的基本查詢

目錄 1. 數據庫的基本查詢 1.1 簡單的記錄查詢 1.2 使用列別名 2. 數據分頁查詢 &#xff08;1&#xff09;查詢前五行數據 &#xff08;2&#xff09;查詢 11 ~ 15 行數據 3. 結果集排序 3.1 單關鍵字排序 &#xff08;1&#xff09;升序排列 &#xff08;2&#…

寶塔配置pgsql可以遠程訪問及pdo_pgsql擴展的安裝

本地navicat premium 17.0 可以遠程訪問pgsql v16.1寶塔的軟件商店里&#xff0c;找到pgsql管理器&#xff1b;在pgsql管理器里找到客戶端認證&#xff1a;第二步&#xff1a;配置修改&#xff0c;CtrlF 查找listen_addresses關鍵字&#xff1b;第三步&#xff1a;在navicat里配…

SQL進階:自連接的用法

目錄 一、可重排列、排列、組合 1、創建表 2、錄入數據 3、獲取可重排列的商品名稱&#xff08;有序&#xff09; 4、獲取排列的商品名稱&#xff08;有序&#xff09; 5、獲取組合的商品名稱&#xff08;無序&#xff09; 6、獲取3個元素的組合商品名稱&#xff08;無序…

Spark集群優化配置指南

Spark集群優化配置指南 &#x1f4cb; 概述 本文檔記錄了5節點Spark集群的性能優化配置&#xff0c;主要解決Thrift Server內存不足(OOM)問題和CPU資源利用率低的問題。 文檔內容 Spark架構原理: Driver與Executor的關系和工作機制Driver內存配置詳解: 三個關鍵內存參數的作用和…

Layui —— select

前言&#xff1a;記錄在修改bug時遇到的一些奇怪問題。遇到的奇怪問題1&#xff1a;項目中引入了 layui&#xff0c;而且也使用了 layui.use 按需導入了需要的組件&#xff0c;但是在頁面每次剛初始化的時候去使用layui&#xff0c;控制臺都會報 組件未定義的問題&#xff08;正…

代碼隨想錄day32dp1

文章目錄509. 斐波那契數70. 爬樓梯746. 使用最小花費爬樓梯確定dp數組&#xff08;dp table&#xff09;以及下標的含義 確定遞推公式 dp數組如何初始化 確定遍歷順序 舉例推導dp數組509. 斐波那契數 題目鏈接 文章講解 class Solution { public:int fib(int n) {// 1. 確定…

RedisJSON 技術揭秘`JSON.ARRTRIM`用窗口裁剪,讓數組保持“剛剛好”

1、指令速查 JSON.ARRTRIM <key> <path> <start> <stop>key&#xff1a;Redis 鍵名path&#xff1a;JSONPath&#xff0c;默認 $ 根&#xff1b;可用 .[*]/.. 多路徑匹配start / stop&#xff1a;要保留的 [start, stop] 閉區間索引 支持負值&#xff…

fpga調試經驗

fpga調試經驗 調測場景&#xff1a; 外接adc傳感器芯片&#xff0c;采集壓力&#xff0c;溫度等模擬量&#xff0c;fpga通過spi/i2c接口與adc傳感器芯片通信 問題1&#xff1a;adc芯片在穩定環境中&#xff0c;輸出數字量不穩定。 結論&#xff1a;adc輸入電壓由fpga板供應&…

cefSharp.WinForms.NETCore 138.xx (cef138/Chromium 138.0.7204.97) 升級測試體驗

一、版本說明及變化 該版本支持cef138.0.x系列,cefsharp138.0.170 無重大更新;該版本暫不支持h264,請關注后續 關注欄目,關注我,學習cefsharp少走彎路 不迷路! CefSharp 設置緩存的注意事項參考 說明:欄目是訂閱文章,無附件,如需要單獨獲取(看底部介紹說明) 該版本1…

chatgpt是怎么誕生的,詳解GPT1到GPT4的演化之路及相關背景知識

人工智能革命正在發生&#xff0c;我們是何其幸運的一代&#xff0c;能親眼見證人類/機器智能的大爆發。 僅僅作為這場革命的看客顯然是有些遺憾的&#xff0c;如何進一步了解它&#xff1f; 本文將討論chatgpt的誕生過程&#xff0c;串聯起OpenAI發表的一系列重要論文&#…

[筆記] 動態 SQL 查詢技術解析:構建靈活高效的企業級數據訪問層

文章目錄一. 應用場景二. 使用示例示例1示例2示例3三. 實現1. 動態表查詢構建器&#xff0c;模仿MyBatis-Plus2. mapper3. mapper.xml功能概述參數說明四. 動態 SQL 的優化與風險防控在企業級應用開發中&#xff0c;數據查詢場景往往呈現出復雜多變的特點 —— 從簡單的單表篩選…

.net天擎分鐘降水數據統計

1.需求&#xff1a;計算滑動時間下的1小時、3小時、6小時、12小時、24小時降水數據&#xff0c;統計這個時間下的分鐘級降水數據2.分析第一版本&#xff1a;降水分鐘級數據保存時間不長&#xff0c;保存太多意義不大&#xff0c;以更新的形式來保存這些統計數據效果會比較好&am…

圖片合并pdf

文章目錄 背景目標實現下載 背景 整合&#xff1a; 將零散的圖片集合成一個單一文件。有序化&#xff1a; 固定圖片的排列順序。標準化&#xff1a; 轉換為通用、兼容性強的PDF格式。高效管理&#xff1a; 便于存儲、查找、分享和傳輸。正式化/文檔化&#xff1a; 滿足提交、報…

【vue3+js】文件下載方法整理

前端文件下載方式 引言 在前端開發中,文件下載是一個常見的需求。后端可能以不同的方式返回文件數據,前端需要根據不同的返回類型采用相應的處理方式。本文將總結幾種常見的后端返回類型及對應的前端處理方案,主要基于Vue3和JavaScript環境。 一、后端返回文件URL 場景描…

MicrobiomeStatPlots | 森林圖教程Forest plot tutorial

視頻講解https://www.bilibili.com/video/BV1mA3yzEEnc/森林圖簡介什么是森林圖&#xff1f;參考&#xff1a;https://mp.weixin.qq.com/s/vwNf_sFlmhp7DeSYaQ3NxQ森林圖是以統計指標和統計分析方法為基礎&#xff0c;用數值運算結果繪制出的圖形。它在平面直角坐標系中&#x…

vscode 打開項目時候,有部分外部依賴包找不到定義或者聲明,但是能使用cmake正常編譯并且運行

解決&#xff1a;是依賴路徑的問題&#xff0c;先看includePath對不對&#xff0c;但是有時候會依賴外部文件&#xff0c;這時候入股cmake編譯能夠聽過&#xff0c; 說明編譯器能夠找到依賴路徑&#xff0c; 但是vscode的 IntelliSense 找不到依賴路徑 → 導致編輯器提示錯誤、…

nginx:SSL_CTX_use_PrivateKey failed

SSL_CTX_use_PrivateKey("/home/nginx-vue/cret/*.com.key") failed (SSL: error:0B080074:x509 certificate routines:x509_check_private_key:key values mismatch) Nginx 嘗試加載私鑰文件時失敗&#xff0c;原因是&#xff1a;證書與私鑰不匹配 問題本質 SSL 證…

Docker 基于 Cgroups 實現資源限制詳解【實戰+源碼】

本文將帶你深入理解 Docker 如何借助 Linux Cgroups 實現對內存、CPU 等系統資源的精細化控制&#xff0c;并提供完整演示與圖解、Compose 配置模板和資源包下載&#xff0c;適合初學者與工程師深入學習與實戰。 文章目錄 一、什么是 Cgroups&#xff1f;為什么對容器如此關鍵…