Redis客戶端緩存的4種實現方式

Redis作為當今最流行的內存數據庫和緩存系統,被廣泛應用于各類應用場景。然而,即使Redis本身性能卓越,在高并發場景下,應用與Redis服務器之間的網絡通信仍可能成為性能瓶頸。

這時,客戶端緩存技術便顯得尤為重要。

客戶端緩存是指在應用程序內存中維護一份Redis數據的本地副本,以減少網絡請求次數,降低延遲,并減輕Redis服務器負擔。

本文將分享Redis客戶端緩存的四種實現方式,分析其原理、優缺點、適用場景及最佳實踐.

方式一:本地內存緩存 (Local In-Memory Cache)

技術原理

本地內存緩存是最直接的客戶端緩存實現方式,它在應用程序內存中使用數據結構(如HashMap、ConcurrentHashMap或專業緩存庫如Caffeine、Guava Cache等)存儲從Redis獲取的數據。這種方式完全由應用程序自己管理,與Redis服務器無關。

實現示例

以下是使用Spring Boot和Caffeine實現的簡單本地緩存示例:

@Service
public class RedisLocalCacheService {private final StringRedisTemplate redisTemplate;private final Cache<String, String> localCache;public RedisLocalCacheService(StringRedisTemplate redisTemplate) {this.redisTemplate = redisTemplate;// 配置Caffeine緩存this.localCache = Caffeine.newBuilder().maximumSize(10_000)  // 最大緩存條目數.expireAfterWrite(Duration.ofMinutes(5))  // 寫入后過期時間.recordStats()  // 記錄統計信息.build();}public String get(String key) {// 首先嘗試從本地緩存獲取String value = localCache.getIfPresent(key);if (value != null) {// 本地緩存命中return value;}// 本地緩存未命中,從Redis獲取value = redisTemplate.opsForValue().get(key);if (value != null) {// 將從Redis獲取的值放入本地緩存localCache.put(key, value);}return value;}public void set(String key, String value) {// 更新RedisredisTemplate.opsForValue().set(key, value);// 更新本地緩存localCache.put(key, value);}public void delete(String key) {// 從Redis中刪除redisTemplate.delete(key);// 從本地緩存中刪除localCache.invalidate(key);}// 獲取緩存統計信息public Map<String, Object> getCacheStats() {CacheStats stats = localCache.stats();Map<String, Object> statsMap = new HashMap<>();statsMap.put("hitCount", stats.hitCount());statsMap.put("missCount", stats.missCount());statsMap.put("hitRate", stats.hitRate());statsMap.put("evictionCount", stats.evictionCount());return statsMap;}
}

優缺點分析

優點

  • 實現簡單,易于集成
  • 無需額外的服務器支持
  • 可完全控制緩存行為(大小、過期策略等)
  • 顯著減少網絡請求次數
  • 對Redis服務器完全透明

缺點

  • 緩存一致性問題:當Redis數據被其他應用或服務更新時,本地緩存無法感知變化
  • 內存占用:需要消耗應用程序的內存資源
  • 冷啟動問題:應用重啟后緩存需要重新預熱
  • 分布式環境下多實例之間的緩存不一致

適用場景

  • 讀多寫少的數據(如配置信息、靜態數據)
  • 對數據實時性要求不高的場景
  • 單體應用或數據一致性要求不高的分布式系統
  • 作為其他緩存策略的補充手段

最佳實踐

  1. 合理設置緩存大小和過期時間:避免過多內存占用
  2. 選擇正確的緩存驅逐策略:LRU、LFU等根據業務特點選擇
  3. 定期刷新重要數據:主動更新而不總是被動等待過期
  4. 添加監控和統計:跟蹤命中率、內存使用等指標
  5. 考慮緩存預熱:應用啟動時主動加載常用數據

方式二:Redis服務器輔助的客戶端緩存 (Server-Assisted Client-Side Caching)

技術原理

Redis 6.0引入了服務器輔助的客戶端緩存功能,也稱為跟蹤模式(Tracking)。

在這種模式下,Redis服務器會跟蹤客戶端請求的鍵,當這些鍵被修改時,服務器會向客戶端發送失效通知。這種機制確保了客戶端緩存與Redis服務器之間的數據一致性。

Redis提供了兩種跟蹤模式:

  1. 默認模式:服務器精確跟蹤每個客戶端關注的鍵
  2. 廣播模式:服務器廣播所有鍵的變更,客戶端過濾自己關心的鍵

實現示例

使用Lettuce(Spring Boot Redis的默認客戶端)實現服務器輔助的客戶端緩存:

@Service
public class RedisTrackingCacheService {private final StatefulRedisConnection<String, String> connection;private final RedisCommands<String, String> commands;private final Map<String, String> localCache = new ConcurrentHashMap<>();private final Set<String> trackedKeys = ConcurrentHashMap.newKeySet();public RedisTrackingCacheService(RedisClient redisClient) {this.connection = redisClient.connect();this.commands = connection.sync();// 配置客戶端緩存失效監聽器connection.addListener(message -> {if (message instanceof PushMessage) {PushMessage pushMessage = (PushMessage) message;if ("invalidate".equals(pushMessage.getType())) {List<Object> invalidations = pushMessage.getContent();handleInvalidations(invalidations);}}});// 啟用客戶端緩存跟蹤commands.clientTracking(ClientTrackingArgs.Builder.enabled());}public String get(String key) {// 首先嘗試從本地緩存獲取String value = localCache.get(key);if (value != null) {return value;}// 本地緩存未命中,從Redis獲取value = commands.get(key);if (value != null) {// 啟用跟蹤后,Redis服務器會記錄這個客戶端正在跟蹤這個鍵localCache.put(key, value);trackedKeys.add(key);}return value;}public void set(String key, String value) {// 更新Rediscommands.set(key, value);// 更新本地緩存localCache.put(key, value);trackedKeys.add(key);}private void handleInvalidations(List<Object> invalidations) {if (invalidations != null && invalidations.size() >= 2) {// 解析失效消息String invalidationType = new String((byte[]) invalidations.get(0));if ("key".equals(invalidationType)) {// 單個鍵失效String invalidatedKey = new String((byte[]) invalidations.get(1));localCache.remove(invalidatedKey);trackedKeys.remove(invalidatedKey);} else if ("prefix".equals(invalidationType)) {// 前綴失效String prefix = new String((byte[]) invalidations.get(1));Iterator<Map.Entry<String, String>> it = localCache.entrySet().iterator();while (it.hasNext()) {String key = it.next().getKey();if (key.startsWith(prefix)) {it.remove();trackedKeys.remove(key);}}}}}// 獲取緩存統計信息public Map<String, Object> getCacheStats() {Map<String, Object> stats = new HashMap<>();stats.put("cacheSize", localCache.size());stats.put("trackedKeys", trackedKeys.size());return stats;}// 清除本地緩存但保持跟蹤public void clearLocalCache() {localCache.clear();}// 關閉連接并清理資源@PreDestroypublic void cleanup() {if (connection != null) {connection.close();}}
}

優缺點分析

優點

  • 自動維護緩存一致性,無需手動同步
  • Redis服務器能感知客戶端緩存狀態
  • 顯著減少網絡請求數量
  • 支持細粒度(鍵級別)的緩存控制
  • 實時感知數據變更,數據一致性保證強

缺點

  • 需要Redis 6.0以上版本支持
  • 增加Redis服務器內存占用(跟蹤狀態)
  • 客戶端連接必須保持活躍
  • 服務器廣播模式可能產生大量失效消息
  • 實現復雜度高于簡單本地緩存

適用場景

  • 對數據一致性要求高的場景
  • 讀多寫少但又需要實時反映寫入變化的場景
  • 分布式系統中多客戶端訪問相同數據集
  • 大型應用需要減輕Redis負載但又不能容忍數據不一致

最佳實踐

  1. 選擇合適的跟蹤模式

    • 默認模式:客戶端數量少且各自訪問不同數據集
    • 廣播模式:客戶端數量多或訪問模式不可預測
  2. 使用前綴跟蹤:按鍵前綴組織數據并跟蹤,減少跟蹤開銷

  3. 合理設置REDIRECT參數:在多個客戶端共享跟蹤連接時

  4. 主動重連策略:連接斷開后盡快重建連接和緩存

  5. 設置合理的本地緩存大小:避免過度占用應用內存

方式三:基于過期時間的緩存失效策略 (TTL-based Cache Invalidation)

技術原理

基于過期時間(Time-To-Live,TTL)的緩存失效策略是一種簡單有效的客戶端緩存方案。

它為本地緩存中的每個條目設置一個過期時間,過期后自動刪除或刷新。

這種方式不依賴服務器通知,而是通過預設的時間窗口來控制緩存的新鮮度,平衡了數據一致性和系統復雜度。

實現示例

使用Spring Cache和Caffeine實現TTL緩存:

@Configuration
public class CacheConfig {@Beanpublic CacheManager cacheManager() {CaffeineCacheManager cacheManager = new CaffeineCacheManager();cacheManager.setCaffeineSpec(CaffeineSpec.parse("maximumSize=10000,expireAfterWrite=300s,recordStats"));return cacheManager;}
}@Service
public class RedisTtlCacheService {private final StringRedisTemplate redisTemplate;@Autowiredpublic RedisTtlCacheService(StringRedisTemplate redisTemplate) {this.redisTemplate = redisTemplate;}@Cacheable(value = "redisCache", key = "#key")public String get(String key) {return redisTemplate.opsForValue().get(key);}@CachePut(value = "redisCache", key = "#key")public String set(String key, String value) {redisTemplate.opsForValue().set(key, value);return value;}@CacheEvict(value = "redisCache", key = "#key")public void delete(String key) {redisTemplate.delete(key);}// 分層緩存 - 不同過期時間的緩存@Cacheable(value = "shortTermCache", key = "#key")public String getWithShortTtl(String key) {return redisTemplate.opsForValue().get(key);}@Cacheable(value = "longTermCache", key = "#key")public String getWithLongTtl(String key) {return redisTemplate.opsForValue().get(key);}// 在程序邏輯中手動控制過期時間public String getWithDynamicTtl(String key, Duration ttl) {// 使用LoadingCache,可以動態設置過期時間Cache<String, String> dynamicCache = Caffeine.newBuilder().expireAfterWrite(ttl).build();return dynamicCache.get(key, k -> redisTemplate.opsForValue().get(k));}// 定期刷新緩存@Scheduled(fixedRate = 60000) // 每分鐘執行public void refreshCache() {// 獲取需要刷新的鍵列表List<String> keysToRefresh = getKeysToRefresh();for (String key : keysToRefresh) {// 觸發重新加載,會調用被@Cacheable注解的方法this.get(key);}}private List<String> getKeysToRefresh() {// 實際應用中,可能從配置系統或特定的Redis set中獲取return Arrays.asList("config:app", "config:features", "daily:stats");}// 使用二級緩存模式,對熱點數據使用更長的TTLpublic String getWithTwoLevelCache(String key) {// 首先查詢本地一級緩存(短TTL)Cache<String, String> l1Cache = Caffeine.newBuilder().maximumSize(1000).expireAfterWrite(Duration.ofSeconds(10)).build();String value = l1Cache.getIfPresent(key);if (value != null) {return value;}// 查詢本地二級緩存(長TTL)Cache<String, String> l2Cache = Caffeine.newBuilder().maximumSize(10000).expireAfterWrite(Duration.ofMinutes(5)).build();value = l2Cache.getIfPresent(key);if (value != null) {// 提升到一級緩存l1Cache.put(key, value);return value;}// 查詢Redisvalue = redisTemplate.opsForValue().get(key);if (value != null) {// 更新兩級緩存l1Cache.put(key, value);l2Cache.put(key, value);}return value;}
}

優缺點分析

優點

  • 實現簡單,易于集成到現有系統
  • 不依賴Redis服務器特殊功能
  • 適用于任何Redis版本
  • 內存占用可控,過期的緩存會自動清理
  • 通過調整TTL可以在一致性和性能之間取得平衡

缺點

  • 無法立即感知數據變更,存在一致性窗口期
  • TTL設置過短會導致緩存效果不佳
  • TTL設置過長會增加數據不一致的風險
  • 所有鍵使用統一TTL策略時缺乏靈活性
  • 可能出現"緩存風暴"(大量緩存同時過期導致突發流量)

適用場景

  • 可以容忍短時間數據不一致的應用
  • 讀多寫少的數據訪問模式
  • 更新頻率相對可預測的數據
  • 使用舊數據造成的影響較小的場景
  • 簡單應用或作為其他緩存策略的補充

最佳實踐

  1. 基于數據特性設置不同TTL

    • 頻繁變化的數據:短TTL
    • 相對穩定的數據:長TTL
  2. 添加隨機因子:TTL加上隨機偏移量,避免緩存同時過期

  3. 實現緩存預熱機制:應用啟動時主動加載熱點數據

  4. 結合后臺刷新:對關鍵數據使用定時任務在過期前主動刷新

  5. 監控緩存效率:跟蹤命中率、過期率等指標,動態調整TTL策略

方式四:基于發布/訂閱的緩存失效通知 (Pub/Sub-based Cache Invalidation)

技術原理

基于發布/訂閱(Pub/Sub)的緩存失效通知利用Redis的發布/訂閱功能來協調分布式系統中的緩存一致性。

當數據發生變更時,應用程序通過Redis發布一條失效消息到特定頻道,所有訂閱該頻道的客戶端收到消息后清除對應的本地緩存。

這種方式實現了主動的緩存失效通知,而不依賴于Redis 6.0以上版本的跟蹤功能。

實現示例

@Service
public class RedisPubSubCacheService {private final StringRedisTemplate redisTemplate;private final Map<String, String> localCache = new ConcurrentHashMap<>();@Autowiredpublic RedisPubSubCacheService(StringRedisTemplate redisTemplate) {this.redisTemplate = redisTemplate;// 訂閱緩存失效通知subscribeToInvalidations();}private void subscribeToInvalidations() {// 使用獨立的Redis連接訂閱緩存失效通知RedisConnectionFactory connectionFactory = redisTemplate.getConnectionFactory();if (connectionFactory != null) {// 創建消息監聽容器RedisMessageListenerContainer container = new RedisMessageListenerContainer();container.setConnectionFactory(connectionFactory);// 消息監聽器,處理緩存失效通知MessageListener invalidationListener = (message, pattern) -> {String invalidationMessage = new String(message.getBody());handleCacheInvalidation(invalidationMessage);};// 訂閱緩存失效通知頻道container.addMessageListener(invalidationListener, new PatternTopic("cache:invalidations"));container.start();}}private void handleCacheInvalidation(String invalidationMessage) {try {// 解析失效消息Map<String, Object> invalidation = new ObjectMapper().readValue(invalidationMessage, new TypeReference<Map<String, Object>>() {});String type = (String) invalidation.get("type");if ("key".equals(type)) {// 單個鍵失效String key = (String) invalidation.get("key");localCache.remove(key);} else if ("prefix".equals(type)) {// 前綴失效String prefix = (String) invalidation.get("prefix");localCache.keySet().removeIf(key -> key.startsWith(prefix));} else if ("all".equals(type)) {// 清空整個緩存localCache.clear();}} catch (Exception e) {// 處理解析錯誤}}public String get(String key) {// 首先嘗試從本地緩存獲取String value = localCache.get(key);if (value != null) {return value;}// 本地緩存未命中,從Redis獲取value = redisTemplate.opsForValue().get(key);if (value != null) {// 存入本地緩存localCache.put(key, value);}return value;}public void set(String key, String value) {// 更新RedisredisTemplate.opsForValue().set(key, value);// 更新本地緩存localCache.put(key, value);// 發布緩存更新通知publishInvalidation("key", key);}public void delete(String key) {// 從Redis中刪除redisTemplate.delete(key);// 從本地緩存中刪除localCache.remove(key);// 發布緩存失效通知publishInvalidation("key", key);}public void deleteByPrefix(String prefix) {// 獲取并刪除指定前綴的鍵Set<String> keys = redisTemplate.keys(prefix + "*");if (keys != null && !keys.isEmpty()) {redisTemplate.delete(keys);}// 清除本地緩存中匹配的鍵localCache.keySet().removeIf(key -> key.startsWith(prefix));// 發布前綴失效通知publishInvalidation("prefix", prefix);}public void clearAllCache() {// 清空本地緩存localCache.clear();// 發布全局失效通知publishInvalidation("all", null);}private void publishInvalidation(String type, String key) {try {// 創建失效消息Map<String, Object> invalidation = new HashMap<>();invalidation.put("type", type);if (key != null) {invalidation.put(type.equals("key") ? "key" : "prefix", key);}invalidation.put("timestamp", System.currentTimeMillis());// 添加來源標識,防止自己接收自己發出的消息invalidation.put("source", getApplicationInstanceId());// 序列化并發布消息String message = new ObjectMapper().writeValueAsString(invalidation);redisTemplate.convertAndSend("cache:invalidations", message);} catch (Exception e) {// 處理序列化錯誤}}private String getApplicationInstanceId() {// 返回應用實例唯一標識,避免處理自己發出的消息return "app-instance-" + UUID.randomUUID().toString();}// 獲取緩存統計信息public Map<String, Object> getCacheStats() {Map<String, Object> stats = new HashMap<>();stats.put("cacheSize", localCache.size());return stats;}
}

優缺點分析

優點

  • 不依賴Redis特定版本的高級功能
  • 可實現近實時的緩存一致性
  • 適用于分布式系統中的多實例協調
  • 靈活度高,支持鍵級別、前綴級別和全局緩存操作
  • 可擴展為處理復雜的緩存依賴關系

缺點

  • 消息可能丟失,導致緩存不一致
  • 發布/訂閱不保證消息持久化和有序交付
  • 系統復雜度增加,需要額外的消息處理邏輯
  • 實現不當可能導致消息風暴
  • 網絡分區可能導致通知失敗

適用場景

  • 多實例分布式應用需要協調緩存狀態
  • 對緩存一致性有較高要求但又不想依賴Redis 6.0+的跟蹤功能
  • 需要實現跨服務緩存協調的系統
  • 微服務架構中的數據變更傳播
  • 需要細粒度控制緩存失效的應用

最佳實踐

  1. 避免處理自己發出的消息:通過源標識過濾消息
  2. 實現消息冪等處理:同一消息可能收到多次
  3. 設置消息過期時間:忽略延遲過久的消息
  4. 批量處理密集更新:合并短時間內的多次失效通知
  5. 結合TTL策略:作為安全保障,設置最大緩存生命周期
  6. 監控訂閱連接:確保失效通知能正常接收
  7. 考慮消息可靠性:關鍵場景可結合消息隊列實現更可靠的通知

性能對比與選擇指南

各種緩存策略的性能對比:

實現方式實時性復雜度內存占用網絡開銷一致性保證Redis版本要求
本地內存緩存任意
服務器輔助緩存6.0+
TTL過期策略任意
Pub/Sub通知中強任意

選擇指南

根據以下因素選擇合適的緩存策略:

  1. 數據一致性要求

    • 要求嚴格一致性:選擇服務器輔助緩存
    • 允許短暫不一致:考慮TTL或Pub/Sub方案
    • 對一致性要求低:簡單本地緩存足夠
  2. 應用架構

    • 單體應用:本地緩存或TTL方案簡單有效
    • 微服務架構:Pub/Sub或服務器輔助緩存更合適
    • 高擴展性需求:避免純本地緩存
  3. Redis版本

    • Redis 6.0+:可考慮服務器輔助緩存
    • 舊版Redis:使用其他三種方案
  4. 讀寫比例

    • 高讀低寫:所有方案都適用
    • 寫入頻繁:慎用純本地緩存,考慮TTL或服務器輔助方案
  5. 資源限制

    • 內存受限:使用TTL控制緩存大小
    • 網絡受限:優先考慮本地緩存
    • Redis負載已高:本地緩存可減輕壓力

總結

Redis客戶端緩存是提升應用性能的強大工具,通過減少網絡請求和數據庫訪問,可以顯著降低延遲并提高吞吐量。

在實際應用中,這些策略往往不是相互排斥的,而是可以組合使用,針對不同類型的數據采用不同的緩存策略,以獲得最佳性能和數據一致性平衡。

無論選擇哪種緩存策略,關鍵是理解自己應用的數據訪問模式和一致性需求,并據此設計最合適的緩存解決方案。

通過正確應用客戶端緩存技術,可以在保持數據一致性的同時,顯著提升系統性能和用戶體驗。

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

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

相關文章

eNSP中路由器OSPF協議配置完整實驗和命令解釋

本實驗使用三臺華為路由器&#xff08;R1、R2和R3&#xff09;相連&#xff0c;配置OSPF協議實現網絡互通。拓撲結構如下&#xff1a; 實驗IP規劃 R1: GE0/0/0: 192.168.12.1/24 (Area 0)Loopback0: 1.1.1.1/32 (Area 0) R2: GE0/0/0: 192.168.12.2/24 (Area 0)GE0/0/1: 192.…

內網滲透——紅日靶場三

目錄 一、前期準備 二、外網探測 1.使用nmap進行掃描 2.網站信息收集 3.漏洞復現(CVE-2021-23132) 4.disable_function繞過 5.反彈shell&#xff08;也&#xff0c;并不是&#xff09; 6.SSH登錄 7.權限提升&#xff08;臟牛漏洞&#xff09; 8.信息收集 9.上線msf 三…

解決Win11下MySQL服務無法開機自啟動問題

問題描述 在win11系統中&#xff0c;明明將MySQL服務設置成了自動啟動&#xff0c;但在重啟電腦后MySQL服務還是無法自動啟動&#xff0c;每次都要重新到計算機管理的服務中找到服務再手動啟動。 解決方式 首先確保mysql服務的啟動類型為自動。 設置方法&#xff1a;找到此電…

后端項目進度匯報

項目概述 本項目致力于構建一個先進的智能任務自動化平臺。其核心技術是一套由大型語言模型&#xff08;LLM&#xff09;驅動的后端系統。該系統能夠模擬一個多角色協作的團隊&#xff0c;通過一系列精心設計或動態生成的處理階段&#xff0c;來高效完成各種復雜任務&#xff…

深度學習中學習率調整:提升食物圖像分類模型性能的關鍵實踐

深度學習中學習率調整&#xff1a;提升食物圖像分類模型性能的關鍵實踐 接上篇保存最優模型&#xff0c;在深度學習模型訓練過程中&#xff0c;學習率作為核心超參數&#xff0c;其設置直接影響模型的收斂速度與最終性能。本文將結合食物圖像分類項目&#xff0c;深入探討學習…

Vue 3零基礎入門:從環境搭建到第一個組件

Vue 3零基礎入門&#xff1a;從環境搭建到第一個組件 一、Vue 3簡介 Vue.js是一款漸進式JavaScript框架&#xff0c;用于構建用戶界面。Vue 3是Vue的最新主要版本&#xff0c;于2020年9月發布&#xff0c;帶來了許多改進和新特性&#xff1a; 更快的渲染速度更小的包體積Com…

為了結合后端而學習前端的學習日志(1)——純CSS靜態卡片案例

前端設計專欄 使用純CSS創建簡潔名片卡片的學習實踐 在這篇技術博客中&#xff0c;我將分享我的前端學習過程&#xff0c;如何使用純HTML和CSS創建一個簡潔美觀的名片式卡片&#xff0c;就像我博客首頁展示的那樣。這種卡片設計非常適合作為個人簡介、產品展示或團隊成員介紹…

k8s監控方案實踐(一):部署Prometheus與Node Exporter

k8s監控方案實踐&#xff08;一&#xff09;&#xff1a;部署Prometheus與Node Exporter 文章目錄 k8s監控方案實踐&#xff08;一&#xff09;&#xff1a;部署Prometheus與Node Exporter一、Prometheus簡介二、PrometheusNode Exporter實戰部署1. 創建Namespace&#xff08;p…

谷歌最新推出的Gemini 2.5 Flash人工智能模型因其安全性能相較前代產品出現下滑

每周跟蹤AI熱點新聞動向和震撼發展 想要探索生成式人工智能的前沿進展嗎&#xff1f;訂閱我們的簡報&#xff0c;深入解析最新的技術突破、實際應用案例和未來的趨勢。與全球數同行一同&#xff0c;從行業內部的深度分析和實用指南中受益。不要錯過這個機會&#xff0c;成為AI領…

【Python】PDF文件處理(PyPDF2、borb、fitz)

Python提供了多種方法和庫用于處理PDF文件&#xff0c;這些工具可以幫助開發者實現諸如讀取、寫入、合并、拆分以及壓縮等功能。以下是幾個常用的Python PDF操作庫及其基本用法&#xff08;PyPDF2、borb、fitz&#xff09;。 1. PyPDF2 PyPDF2 是一個功能強大的庫&#xff0…

websocketd 10秒教程

websocketd 參考地址&#xff1a;joewalnes/websocketd 官網地址&#xff1a;websocketd websocketd簡述 websocketd是一個簡單的websocket服務Server&#xff0c;運行在命令行方式下&#xff0c;可以通過websocketd和已經有程序進行交互。 現在&#xff0c;可以非常容易地構…

Spring Boot 基于 Cookie 實現單點登錄:原理、實踐與優化詳解

前言 在多系統交互的應用場景中&#xff0c;單點登錄&#xff08;SSO&#xff09;能夠顯著提升用戶體驗&#xff0c;減少重復登錄的繁瑣操作。基于 Cookie 的單點登錄方案&#xff0c;憑借其簡單直觀、瀏覽器原生支持的特性&#xff0c;成為快速實現單點登錄的有效方式。本文將…

ModBus協議詳解:從基礎概念到C#實現RTU與TCP通訊

ModBus協議是莫迪康公司為了讓PLC之間進行數據通信而設計出來的協議。它是一種總線協議&#xff0c;是一種一對多&#xff0c;上下級的關系。 它的應用廣泛&#xff0c;具有免費開源&#xff0c;操作簡單的有點&#xff0c;并且可以兼容串口和網絡通訊&#xff0c;兼容也不錯。…

PHP數組排序深度解析:sort()、rsort()、asort()、arsort()、ksort()、krsort() 的適用場景與性能對比

在PHP開發中&#xff0c;數組排序是日常操作的核心技能之一。無論是處理用戶數據、產品列表&#xff0c;還是分析日志信息&#xff0c;合理的排序方法能顯著提升代碼的效率和可維護性。PHP提供了多種數組排序函數&#xff08;如 sort()、rsort()、asort() 等&#xff09;&#…

RabittMQ-高級特性2-應用問題

文章目錄 前言延遲隊列介紹ttl死信隊列存在問題延遲隊列插件安裝延遲插件使用事務消息分發概念介紹限流非公平分發&#xff08;負載均衡&#xff09; 限流負載均衡RabbitMQ應用問題-冪等性保障順序性保障介紹1順序性保障介紹2消息積壓總結 前言 延遲隊列介紹 延遲隊列(Delaye…

HOW - 在 Mac 上的 Chrome 瀏覽器中調試 Windows 場景下的前端頁面

文章目錄 為什么需要模擬 Windows 環境&#xff1f;一、修改 User-Agent 模擬 Windows 瀏覽器方法 1&#xff1a;通過 Chrome 開發者工具修改 UA方法 2&#xff1a;使用瀏覽器插件 二、模擬 Windows 的字體和滾動條樣式1. 模擬 Windows 字體2. 強制顯示滾動條&#xff08;模擬 …

如何刪除豆包本地大模型

由于無法選擇大模型的安裝位置&#xff0c;因此會占用C盤大量空間&#xff0c;然后又找到不卸載的地方&#xff0c;經排查豆包大模型安裝位為&#xff1a;C:\Users\[當前電腦用戶]\AppData\Local\Doubao\User Data&#xff0c;只能進行手動卸載。

Linux C語言線程編程入門筆記

目錄 開發環境準備 線程基礎概念 進程與線程的關系 線程生命周期 創建線程 等待線程結束 線程函數和參數 互斥鎖與共享資源保護 總結 開發環境準備 操作系統&#xff1a;以 Linux 為例&#xff08;Ubuntu/CentOS 等主流發行版&#xff09;。請確保系統已安裝 GNU C 編…

levelDB的數據查看(非常詳細)

起因:.net大作業天氣預報程序(WPF)答辯時&#xff0c;老師問怎么維持數據持久性的&#xff0c;啟動時加載的數據存在哪里&#xff0c;我明白老師想考的應該是json文件的解析&#xff08;正反&#xff09;&#xff0c;半天沒答上來存那個文件了&#xff08;老師默認這個文件是自…

數據分析怎么做?高效的數據分析方法有哪些?

目錄 一、數據分析的對象和目的 &#xff08;一&#xff09;數據分析的常見對象 &#xff08;二&#xff09;數據分析的目的 二、數據分析怎么做&#xff1f; &#xff08;一&#xff09;明確問題 &#xff08;二&#xff09;收集數據 &#xff08;三&#xff09;清洗和…