基于Redis實現限流的幾種方式

限流盡可能在滿足需求的情況下越簡單越好!

分布式限流是指在分布式系統中對請求進行限制,以防止系統過載或濫用資源。以下是常見的分布式限流策略及其實現方式:

1、基于 Redis 的固定窗口限流

原理

  • 設定一個時間窗口(如 1 秒)
  • 使用 Redis 維護一個計數器,存儲當前窗口的請求數
  • 當請求到來時,INCR 計數器,如果超過閾值則拒絕
  • 過期后自動刪除鍵,進入下一個窗口

優缺點: ? 簡單易實現
? 在窗口交界處可能會出現短時間的突發流量("臨界突增")

public class RedisRateLimiter {private final StringRedisTemplate redisTemplate;// 命令前綴private final String key;private final int rate;private final int window;public RedisRateLimiter(StringRedisTemplate redisTemplate, String key, int rate, int window) {this.redisTemplate = redisTemplate;this.key = key;this.rate = rate;this.window = window;}// 檢查并獲取令牌public boolean acquire() {String currentKey = key + "_" + (getCurrentSeconds() / window);Long currentCount = redisTemplate.opsForValue().increment(currentKey);redisTemplate.expire(currentKey, window, TimeUnit.SECONDS);log.info("當前獲取到的令牌數 key {}  count {} result {} ",currentKey,currentCount,currentCount > rate);if (currentCount > rate){return false;}return true;}private long getCurrentSeconds() {return System.currentTimeMillis()/1000;}public void acquireSleep()  {int count = 0;while (!acquire()){sleep(1);count++;}}private void sleep(int second) {try {TimeUnit.SECONDS.sleep(second);} catch (InterruptedException e) {e.printStackTrace();}}public boolean acquireSleep(int waitSecond) {int count = 0;while (!acquire()){if (count >= waitSecond){return false;}sleep(1);count++;log.info("RedisRateLimiter[{}] try acquire sleep {}",key,count);}return true;}public static void main(String[] args) throws InterruptedException {ch.qos.logback.classic.Logger logger=(ch.qos.logback.classic.Logger)LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);logger.setLevel(Level.OFF);StringRedisTemplate stringRedisTemplate=getStringRedisTemplate();RedisRateLimiter redisRateLimiter = new RedisRateLimiter(stringRedisTemplate,"request_interface",16,10);// 模擬 50 個并發線程,每個線程嘗試獲取 10 次令牌final int threadCount = 50;ExecutorService executor = Executors.newFixedThreadPool(threadCount);CountDownLatch latch = new CountDownLatch(threadCount);for (int i = 0; i < threadCount; i++) {executor.submit(() -> {// 每個線程嘗試多次調用限流方法for (int j = 0; j < 10; j++) {redisRateLimiter.acquireSleep();System.out.println("當前線程:"+Thread.currentThread().getName()+",獲取到令牌,時間"+ DateFormatUtils.format(new Date(),"yyyy-MM-dd HH:mm:ss"));// 模擬每次請求間隔 100 毫秒redisRateLimiter.milliseconds(100);}latch.countDown();});}latch.await();executor.shutdown();}private static StringRedisTemplate getStringRedisTemplate() {// 1. 創建單機模式的配置RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();redisStandaloneConfiguration.setHostName("127.0.0.1");redisStandaloneConfiguration.setPort(6379);// 2. 構造 LettuceConnectionFactory,并初始化LettuceConnectionFactory factory = new LettuceConnectionFactory(redisStandaloneConfiguration);factory.afterPropertiesSet();  // 初始化連接工廠// 3. 創建 StringRedisTemplate 并設置連接工廠StringRedisTemplate stringRedisTemplate = new StringRedisTemplate();stringRedisTemplate.setConnectionFactory(factory);stringRedisTemplate.afterPropertiesSet();  // 初始化模板return stringRedisTemplate;}private void milliseconds(long millis) {try {Thread.sleep(millis);} catch (InterruptedException e) {e.printStackTrace();}}}

main方法中的計算結果可以看到在并發環境下嚴格的執行10s16次請求(也就是1分鐘96次請求),這個就有個弊端,在并發環境下他們一拿到令牌同一秒就執行請求了。這個就是突發流量。

我的業務就是1分鐘允許請求100次對方接口,像這種雖然嚴格按照1分鐘不超過100次請求但是有突發流量對方還是返回了頻率過高,可能對方計算頻率方式不一樣吧。所以這種方式不太可取。

當前線程:pool-1-thread-30,獲取到令牌,時間2025-03-15 00:17:11
當前線程:pool-1-thread-6,獲取到令牌,時間2025-03-15 00:17:11
當前線程:pool-1-thread-18,獲取到令牌,時間2025-03-15 00:17:11
當前線程:pool-1-thread-35,獲取到令牌,時間2025-03-15 00:17:11
當前線程:pool-1-thread-38,獲取到令牌,時間2025-03-15 00:17:11
當前線程:pool-1-thread-37,獲取到令牌,時間2025-03-15 00:17:11
當前線程:pool-1-thread-33,獲取到令牌,時間2025-03-15 00:17:11
當前線程:pool-1-thread-44,獲取到令牌,時間2025-03-15 00:17:11
當前線程:pool-1-thread-3,獲取到令牌,時間2025-03-15 00:17:11
當前線程:pool-1-thread-45,獲取到令牌,時間2025-03-15 00:17:11
當前線程:pool-1-thread-5,獲取到令牌,時間2025-03-15 00:17:11
當前線程:pool-1-thread-20,獲取到令牌,時間2025-03-15 00:17:11
當前線程:pool-1-thread-11,獲取到令牌,時間2025-03-15 00:17:11
當前線程:pool-1-thread-43,獲取到令牌,時間2025-03-15 00:17:11
當前線程:pool-1-thread-15,獲取到令牌,時間2025-03-15 00:17:11
當前線程:pool-1-thread-29,獲取到令牌,時間2025-03-15 00:17:11
當前線程:pool-1-thread-4,獲取到令牌,時間2025-03-15 00:17:20
當前線程:pool-1-thread-16,獲取到令牌,時間2025-03-15 00:17:20
當前線程:pool-1-thread-12,獲取到令牌,時間2025-03-15 00:17:20
當前線程:pool-1-thread-24,獲取到令牌,時間2025-03-15 00:17:20
當前線程:pool-1-thread-26,獲取到令牌,時間2025-03-15 00:17:20
當前線程:pool-1-thread-8,獲取到令牌,時間2025-03-15 00:17:20
當前線程:pool-1-thread-14,獲取到令牌,時間2025-03-15 00:17:20
當前線程:pool-1-thread-49,獲取到令牌,時間2025-03-15 00:17:20
當前線程:pool-1-thread-42,獲取到令牌,時間2025-03-15 00:17:20
當前線程:pool-1-thread-21,獲取到令牌,時間2025-03-15 00:17:20
當前線程:pool-1-thread-1,獲取到令牌,時間2025-03-15 00:17:20
當前線程:pool-1-thread-10,獲取到令牌,時間2025-03-15 00:17:20
當前線程:pool-1-thread-31,獲取到令牌,時間2025-03-15 00:17:20
當前線程:pool-1-thread-50,獲取到令牌,時間2025-03-15 00:17:20
當前線程:pool-1-thread-36,獲取到令牌,時間2025-03-15 00:17:20
當前線程:pool-1-thread-48,獲取到令牌,時間2025-03-15 00:17:20
當前線程:pool-1-thread-9,獲取到令牌,時間2025-03-15 00:17:30
當前線程:pool-1-thread-19,獲取到令牌,時間2025-03-15 00:17:30
當前線程:pool-1-thread-47,獲取到令牌,時間2025-03-15 00:17:30
當前線程:pool-1-thread-2,獲取到令牌,時間2025-03-15 00:17:30
當前線程:pool-1-thread-34,獲取到令牌,時間2025-03-15 00:17:30
當前線程:pool-1-thread-46,獲取到令牌,時間2025-03-15 00:17:30
當前線程:pool-1-thread-41,獲取到令牌,時間2025-03-15 00:17:30
當前線程:pool-1-thread-22,獲取到令牌,時間2025-03-15 00:17:30
當前線程:pool-1-thread-17,獲取到令牌,時間2025-03-15 00:17:30
當前線程:pool-1-thread-27,獲取到令牌,時間2025-03-15 00:17:30
當前線程:pool-1-thread-28,獲取到令牌,時間2025-03-15 00:17:30
當前線程:pool-1-thread-32,獲取到令牌,時間2025-03-15 00:17:30
當前線程:pool-1-thread-25,獲取到令牌,時間2025-03-15 00:17:30
當前線程:pool-1-thread-13,獲取到令牌,時間2025-03-15 00:17:30
當前線程:pool-1-thread-40,獲取到令牌,時間2025-03-15 00:17:30
當前線程:pool-1-thread-23,獲取到令牌,時間2025-03-15 00:17:30
當前線程:pool-1-thread-39,獲取到令牌,時間2025-03-15 00:17:40
當前線程:pool-1-thread-7,獲取到令牌,時間2025-03-15 00:17:40
當前線程:pool-1-thread-34,獲取到令牌,時間2025-03-15 00:17:40
當前線程:pool-1-thread-13,獲取到令牌,時間2025-03-15 00:17:40
當前線程:pool-1-thread-2,獲取到令牌,時間2025-03-15 00:17:40
當前線程:pool-1-thread-22,獲取到令牌,時間2025-03-15 00:17:40
當前線程:pool-1-thread-28,獲取到令牌,時間2025-03-15 00:17:40
當前線程:pool-1-thread-46,獲取到令牌,時間2025-03-15 00:17:40
當前線程:pool-1-thread-25,獲取到令牌,時間2025-03-15 00:17:40
當前線程:pool-1-thread-19,獲取到令牌,時間2025-03-15 00:17:40
當前線程:pool-1-thread-9,獲取到令牌,時間2025-03-15 00:17:40
當前線程:pool-1-thread-32,獲取到令牌,時間2025-03-15 00:17:40
當前線程:pool-1-thread-39,獲取到令牌,時間2025-03-15 00:17:40
當前線程:pool-1-thread-17,獲取到令牌,時間2025-03-15 00:17:40
當前線程:pool-1-thread-47,獲取到令牌,時間2025-03-15 00:17:40
當前線程:pool-1-thread-41,獲取到令牌,時間2025-03-15 00:17:40

2. 基于 Redis 的滑動窗口限流

原理

  • 維護一個基于時間的列表(ZSET,有序集合)
  • 每次請求時,記錄當前時間戳到 ZSET
  • 刪除超出窗口時間范圍的請求
  • 統計 ZSET 中當前窗口內的請求數,超出閾值則拒絕

優缺點: ? 解決了固定窗口的臨界突增問題
? 存儲和計算成本比固定窗口稍高

原理說明

  • 利用 Redis 的有序集合(ZSet),以請求的時間戳作為 score,每個請求入隊一個唯一的 member(例如時間戳+UUID)。
  • 每次請求時,先移除時間窗口外的記錄(score 小于當前時間減去窗口長度)。
  • 統計當前窗口內的請求數量,若數量超過設定閾值,則拒絕請求。
@Slf4j
public class RedisSlidingWindowRateLimiter {private final StringRedisTemplate redisTemplate;private final String key;private final int rate;private final int window; // 窗口長度,單位秒public RedisSlidingWindowRateLimiter(StringRedisTemplate redisTemplate, String key, int rate, int window) {this.redisTemplate = redisTemplate;this.key = key;this.rate = rate;// 限制窗口長度在 1 分鐘以內Assert.isTrue(window > 0 && window <= 60, "窗口只支持一分鐘內");this.window = window;}// 檢查并獲取令牌public boolean acquire() {long now = System.currentTimeMillis();// 計算窗口起始時間(單位毫秒)long windowStart = now - window * 1000;// 移除過期的請求記錄redisTemplate.opsForZSet().removeRangeByScore(key, 0, windowStart);// 添加當前請求記錄,member 用當前時間戳加 UUID 保證唯一性,score 為當前時間String member = now + "_" + UUID.randomUUID().toString();redisTemplate.opsForZSet().add(key, member, now);// 統計當前窗口內的請求數量Long count = redisTemplate.opsForZSet().count(key, windowStart, now);// 為了避免 key 永不過期,設置一個過期時間(窗口長度)redisTemplate.expire(key, window, TimeUnit.SECONDS);if (count != null && count > rate) {return false;}return true;}// 采用輪詢方式等待獲取令牌public void acquireSleep() {int count = 0;while (!acquire()){ThreadUtil.sleep(1, TimeUnit.SECONDS);count++;log.info("RedisSlidingWindowRateLimiter[{}] try acquire sleep {}", key, count);}}public boolean acquireSleep(int waitSecond) {int count = 0;while (!acquire()){if (count >= waitSecond){return false;}ThreadUtil.sleep(1, TimeUnit.SECONDS);count++;log.info("RedisSlidingWindowRateLimiter[{}] try acquire sleep {}", key, count);}return true;}
}

代碼說明

  • 移除過期記錄:調用 removeRangeByScore 清理掉窗口外的請求數據。
  • 添加當前請求:將當前請求的時間戳與 UUID 組合后添加到 ZSet 中,score 為當前時間,確保在滑動窗口內計數。
  • 統計計數:通過 count 方法統計當前窗口內的請求數,如果超出限制則返回 false。

3. 基于 Redis 的令牌桶限流

原理

  • 設定一個容量為 max_tokens 的令牌桶,初始裝滿
  • 以固定速率向桶中添加令牌(如每秒 10 個)
  • 每次請求需要消耗一個令牌,沒有令牌時拒絕請求
  • 通常使用 Redis 的 Lua 腳本實現原子操作

優缺點: ? 更加平滑,支持突發流量
? 需要額外的定時任務或后臺線程補充令牌

原理說明

  • 令牌桶算法中,設定一個桶最大容量 capacity,同時以一定速率 refillRate 補充令牌。
  • 每次請求需要消耗一個令牌,若當前桶內令牌不足,則拒絕請求。
  • 為保證原子性,利用 Redis 的 Lua 腳本將令牌獲取和補充過程封裝為原子操作。
@Slf4j
public class RedisTokenBucketRateLimiter {private final StringRedisTemplate redisTemplate;private final String key;// 桶的容量(最大令牌數)private final int capacity;// 令牌補充速率,單位:個/秒private final double refillRate;// Lua 腳本,用于原子化處理令牌桶邏輯private static final String LUA_SCRIPT = "local tokens_key = KEYS[1] .. ':tokens' \n" +"local timestamp_key = KEYS[1] .. ':ts' \n" +"local capacity = tonumber(ARGV[1]) \n" +"local refill_rate = tonumber(ARGV[2]) \n" +"local current_time = tonumber(ARGV[3]) \n" +"local requested = tonumber(ARGV[4]) \n" +"local tokens = tonumber(redis.call('get', tokens_key) or capacity) \n" +"local last_refill = tonumber(redis.call('get', timestamp_key) or current_time) \n" +"local delta = current_time - last_refill \n" +"local tokens_to_add = delta * refill_rate \n" +"tokens = math.min(capacity, tokens + tokens_to_add) \n" +"if tokens < requested then \n" +"   return 0 \n" +"else \n" +"   tokens = tokens - requested \n" +"   redis.call('set', tokens_key, tokens) \n" +"   redis.call('set', timestamp_key, current_time) \n" +"   return 1 \n" +"end";public RedisTokenBucketRateLimiter(StringRedisTemplate redisTemplate, String key, int capacity, double refillRate) {this.redisTemplate = redisTemplate;this.key = key;this.capacity = capacity;this.refillRate = refillRate;}// 檢查并獲取令牌public boolean acquire() {// 當前時間(單位秒)long currentTime = System.currentTimeMillis() / 1000;// 請求消耗 1 個令牌Long result = redisTemplate.execute((RedisCallback<Long>) connection -> {List<byte[]> keys = Collections.singletonList(key.getBytes());List<byte[]> args = Arrays.asList(String.valueOf(capacity).getBytes(),String.valueOf(refillRate).getBytes(),String.valueOf(currentTime).getBytes(),"1".getBytes());return connection.eval(LUA_SCRIPT.getBytes(), ReturnType.INTEGER, keys.size(), keys.toArray(new byte[0][]), args.toArray(new byte[0][]));});return result != null && result == 1;}
}

代碼說明

  • Lua 腳本邏輯
    • 獲取當前桶中剩余令牌數和上次補充時間,若不存在則默認初始化為滿桶狀態。
    • 根據當前時間與上次更新時間的差值計算應補充的令牌數,并更新桶內令牌。
    • 判斷是否有足夠令牌供本次請求(默認請求 1 個令牌),若不足返回 0,否則扣減令牌并更新上次補充時間,返回 1。
  • 原子執行:通過 redisTemplate 的 eval 方法保證 Lua 腳本的原子性,避免并發問題。

4. 基于 Redis 的漏桶限流

原理

  • 設定一個隊列模擬漏桶
  • 按固定速率從隊列取出請求執行
  • 請求過多時,超出隊列長度的請求被丟棄

優缺點: ? 輸出速率穩定,不受突發流量影響
? 可能會丟棄部分流量

原理說明

  • 漏桶算法中,將請求看作向桶中注入的“水”,桶以固定速率漏水(處理請求)。
  • 當桶中水量超過預設容量時,則拒絕新請求。
  • 同樣利用 Lua 腳本保證原子操作。
@Slf4j
public class RedisLeakyBucketRateLimiter {private final StringRedisTemplate redisTemplate;private final String key;// 桶的容量(允許的最大突發請求數)private final int capacity;// 漏水速率,單位:個/秒,表示每秒可處理的請求數private final double leakRate;// Lua 腳本,用于原子化處理漏桶邏輯private static final String LUA_SCRIPT = "local level_key = KEYS[1] .. ':level' \n" +"local timestamp_key = KEYS[1] .. ':ts' \n" +"local capacity = tonumber(ARGV[1]) \n" +"local leak_rate = tonumber(ARGV[2]) \n" +"local current_time = tonumber(ARGV[3]) \n" +"local level = tonumber(redis.call('get', level_key) or '0') \n" +"local last_time = tonumber(redis.call('get', timestamp_key) or current_time) \n" +"local delta = current_time - last_time \n" +"local leaked = delta * leak_rate \n" +// 計算漏水后桶內水量,不能低于 0"level = math.max(0, level - leaked) \n" +"if level + 1 > capacity then \n" +"   return 0 \n" +"else \n" +"   level = level + 1 \n" +"   redis.call('set', level_key, level) \n" +"   redis.call('set', timestamp_key, current_time) \n" +"   return 1 \n" +"end";public RedisLeakyBucketRateLimiter(StringRedisTemplate redisTemplate, String key, int capacity, double leakRate) {this.redisTemplate = redisTemplate;this.key = key;this.capacity = capacity;this.leakRate = leakRate;}// 檢查并獲取請求處理資格public boolean acquire() {// 當前時間(單位秒)long currentTime = System.currentTimeMillis() / 1000;Long result = redisTemplate.execute((RedisCallback<Long>) connection -> {List<byte[]> keys = Collections.singletonList(key.getBytes());List<byte[]> args = Arrays.asList(String.valueOf(capacity).getBytes(),String.valueOf(leakRate).getBytes(),String.valueOf(currentTime).getBytes());return connection.eval(LUA_SCRIPT.getBytes(), ReturnType.INTEGER, keys.size(), keys.toArray(new byte[0][]), args.toArray(new byte[0][]));});return result != null && result == 1;}
}

代碼說明

  • Lua 腳本邏輯
    • 從 Redis 中獲取當前桶內水量(即請求數量)和上次更新的時間。
    • 根據當前時間與上次更新時間的差值和設定的漏水速率計算“漏掉”的水量,并更新桶內水量(不能低于 0)。
    • 判斷加入當前請求后是否超過桶的容量,超過則返回 0(拒絕),否則將水量加 1 并更新記錄,返回 1 表示允許。
  • 原子執行:同樣通過 eval 方法保證操作原子性,避免并發修改問題。

總結

  • 滑動窗口:使用 Redis ZSet 記錄請求時間戳,動態統計窗口內請求數,平滑控制突發流量。
  • 令牌桶:通過 Lua 腳本實現令牌的自動補充和扣減,支持一定的突發請求。
  • 漏桶:用固定漏水速率保證請求以均勻的速率被處理,避免瞬間大量請求。

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

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

相關文章

【前端文件下載實現:多種表格導出方案的技術解析】

前端文件下載實現&#xff1a;多種表格導出方案的技術解析 背景介紹 在企業級應用中&#xff0c;數據導出是一個常見需求&#xff0c;特別是表格數據的導出。在我們的管理系統中&#xff0c;不僅需要支持用戶數據的Excel導出&#xff0c;還需要處理多種格式的表格文件下載&am…

堆概念和結構

1. 二叉樹的順序結構 普通的二叉樹是不適合用數組來存儲的&#xff0c;因為可能會存在大量的空間浪費。而完全二叉樹更適合使用順序結構存儲。現實中通常 把堆使用順序結構的數組來存儲 &#xff0c;需要注意的是這里的堆和操作系統虛擬進程地址空間中的堆是兩回事&#xff0c…

VUE的腳手架搭建引入類庫

VUE的小白腳手架搭建 真的好久好久自己沒有發布自己博客了,對于一直在做后端開發的我 ,由于社會卷啊卷只好學習下怎么搭建前端,一起學習成長吧~哈哈哈(最終目的,能夠懂并簡易開發) 文章目錄 VUE的小白腳手架搭建1.下載node.js2.安裝vue腳手架3.創建一個項目4.代碼規范約束配置(…

使用 Arduino 和 ThingSpeak 通過互聯網進行實時溫度和濕度監測

使用 ThingSpeak 和 Arduino 通過 Internet 進行溫度和濕度監控 濕度和溫度是許多地方(如農場、溫室、醫療、工業家庭和辦公室)非常常見的測量參數。我們已經介紹了使用 Arduino 進行濕度和溫度測量,并在 LCD 上顯示數據。 在這個物聯網項目中,我們將使用ThingSpeak在互聯…

論文分享:PL-ALF框架實現無人機低紋理環境自主飛行

在室內倉庫、地下隧道等低紋理復雜場景中&#xff0c;無人機依賴視覺傳感器進行自主飛行時&#xff0c;往往會遇到定位精度低、路徑規劃不穩定等難題。針對這一問題&#xff0c;重慶郵電大學計算機學院雷大江教授團隊在IEEE Trans期刊上提出了一種新型自主飛行框架&#xff1a;…

[Java實戰]性能優化qps從1萬到3萬

一、問題背景 ? 事情起因是項目上springboot項目提供的tps達不到客戶要求,除了增加服務器提高tps之外,作為團隊的技術總監,架構師,技術扛把子,本著我不入地獄誰入地獄的原則,決心從代碼上優化,讓客戶享受到飛一般的感覺。雖然大多數編程工作在寫下第一行代碼時已經完成…

如何篩選能實現共享自助健身房“靈活性”的物聯網框架?

共享自助健身房已經成為一種新興的健身方式&#xff0c;這種模式方便快捷&#xff0c;尤其適合i人健身愛好者&#xff0c;市場接受度還是挺好的。對于無人自助式的健身房要想實現靈活性&#xff0c;要挑選什么樣的物聯網框架呢&#xff1f; 1. 支持多種通信協議 共享自助健身…

【后端】【django】拋棄 Django 自帶用戶管理后,能否使用 `simple-jwt`?

拋棄 Django 自帶用戶管理后&#xff0c;能否使用 simple-jwt&#xff1f; 一、結論 是的&#xff0c;即使拋棄了 Django 自帶的用戶管理&#xff08;AbstractUser 或 AbstractBaseUser&#xff09;&#xff0c;仍然可以使用 django-rest-framework-simplejwt&#xff08;簡稱…

【量化科普】Correlation,相關性

【量化科普】Correlation&#xff0c;相關性 &#x1f680;量化軟件開通 &#x1f680;量化實戰教程 在量化投資領域&#xff0c;相關性&#xff08;Correlation&#xff09;是一個核心概念&#xff0c;用于衡量兩個變量之間的線性關系強度和方向。簡單來說&#xff0c;它告…

大數據學習(68)- Flink和Spark Streaming

&#x1f34b;&#x1f34b;大數據學習&#x1f34b;&#x1f34b; &#x1f525;系列專欄&#xff1a; &#x1f451;哲學語錄: 用力所能及&#xff0c;改變世界。 &#x1f496;如果覺得博主的文章還不錯的話&#xff0c;請點贊&#x1f44d;收藏??留言&#x1f4dd;支持一…

MCU詳解:嵌入式系統的“智慧之心”

在現代電子設備中&#xff0c; MCU&#xff08;Microcontroller Unit&#xff0c;微控制器&#xff09;扮演著至關重要的角色。從智能家居到工業控制&#xff0c;從汽車電子到醫療設備&#xff0c;MCU以其小巧、低功耗和高集成度的特點&#xff0c;成為嵌入式系統的核心組件。 …

(鏈表)24. 兩兩交換鏈表中的節點

給你一個鏈表&#xff0c;兩兩交換其中相鄰的節點&#xff0c;并返回交換后鏈表的頭節點。你必須在不修改節點內部的值的情況下完成本題&#xff08;即&#xff0c;只能進行節點交換&#xff09;。 示例 1&#xff1a; 輸入&#xff1a;head [1,2,3,4] 輸出&#xff1a;[2,1,4…

吳恩達機器學習筆記復盤(三)Jupyter NoteBook

Jupyter NoteBook Jupyter是一個開源的交互式計算環境&#xff1a; 特點 交互式編程&#xff1a;支持以單元格為單位編寫和運行代碼&#xff0c;用戶可以實時看到代碼的執行結果&#xff0c;便于逐步調試和理解代碼邏輯。多語言支持&#xff1a;不僅支持Python&#xff0c;還…

【Linux】從互斥原理到C++ RAII封裝實踐

&#x1f4e2;博客主頁&#xff1a;https://blog.csdn.net/2301_779549673 &#x1f4e2;歡迎點贊 &#x1f44d; 收藏 ?留言 &#x1f4dd; 如有錯誤敬請指正&#xff01; &#x1f4e2;本文由 JohnKi 原創&#xff0c;首發于 CSDN&#x1f649; &#x1f4e2;未來很長&#…

微服務無狀態服務設計

微服務無狀態服務設計是構建高可用、高擴展性系統的核心方法。 一、核心設計原則 請求獨立性 每個請求必須攜帶完整的上下文信息&#xff0c;服務不依賴本地存儲的會話或用戶數據。例如用戶認證通過JWT傳遞所有必要信息&#xff0c;而非依賴服務端Session。 狀態外置化 將會話…

30、map 和 unordered_map的區別和實現機制【高頻】

底層結構 map底層是紅黑樹結構&#xff0c;而unordered_map底層是哈希結構; 有序性 但是紅黑樹其實是一種二叉搜索樹&#xff0c;插入刪除時會自動排序hash因為是把數據映射到數組上的&#xff0c;而且存在哈希沖突&#xff0c;所以不能保證有序存儲 所以有序存儲使用map&a…

大數據-spark3.5安裝部署之local模式

spark&#xff0c;一個數據處理框架和計算引擎。 下載 local模式即本地模式&#xff0c;就是不需要任何其他節點資源就可以在本地執行spark代碼的環境。用于練習演示。 上傳解壓 使用PortX將文件上傳至/opt 進入/opt目錄&#xff0c;創建目錄module&#xff0c;解壓文件至/o…

Manus “Less structure,More intelligence ”獨行云端處理器

根據市場調研機構Statista數據顯示&#xff0c;全球的AR/AR的市場規模預計目前將達到2500億美元&#xff0c;Manus作為VR手套領域的領軍企業&#xff0c;足以顛覆你的認知。本篇文章將帶你解讀Manus產品&#xff0c;針對用戶提出的種種問題&#xff0c;Manus又將如何解決且讓使…

Oracle數據庫存儲結構--邏輯存儲結構

數據庫存儲結構&#xff1a;分為物理存儲結構和邏輯存儲結構。 物理存儲結構&#xff1a;操作系統層面如何組織和管理數據 邏輯存儲結構&#xff1a;Oracle數據庫內部數據組織和管理數據&#xff0c;數據庫管理系統層面如何組織和管理數據 Oracle邏輯存儲結構 數據庫的邏…

芯驛電子 ALINX 亮相德國紐倫堡,Embedded World 2025 精彩回顧

2025年3月13日&#xff0c;全球規模最大的嵌入式行業盛會——德國紐倫堡國際嵌入式展&#xff08;embedded world 2025&#xff09;圓滿落幕。 在這場匯聚全球 950 家展商、3 萬余專業觀眾的科技盛宴中&#xff0c;芯驛電子 ALINX 展位人頭攢動&#xff0c;多款尖端產品吸引客戶…