深入理解缓存淘汰策略:LRU vs LFU 完全解析

深入理解缓存淘汰策略:LRU vs LFU 完全解析

文章目录

  • 深入理解缓存淘汰策略:LRU vs LFU 完全解析
    • 前言
    • 一、基础概念解析
      • 1.1 LRU(Least Recently Used)- 最近最少使用
      • 1.2 LFU(Least Frequently Used)- 最少使用频率
    • 二、核心差异对比
    • 三、LRU算法实现
      • 3.1 基于双向链表 + HashMap的经典实现
      • 3.2 基于LinkedHashMap的简化实现
    • 四、LFU算法实现
      • 4.1 基于双HashMap的高效实现
      • 4.2 简化版LFU实现
    • 五、实际应用场景分析
      • 5.1 LRU适用场景
      • 5.2 LFU适用场景
    • 六、性能测试与对比
      • 6.1 性能测试框架
    • 七、实际项目中的最佳实践
      • 7.1 缓存策略选择指南
      • 7.2 混合缓存策略实现
      • 7.3 生产环境配置建议
    • 八、常见问题与解决方案
      • 8.1 LRU缓存的常见问题
      • 8.2 LFU缓存的常见问题
    • 九、总结与建议
      • 9.1 选择决策树
      • 9.2 最佳实践总结
      • 9.3 性能调优建议
    • 结语

前言

在现代软件系统中,缓存是提高性能的关键技术之一。然而,由于内存资源有限,我们需要合理的淘汰策略来决定哪些数据应该被移除。LRU(Least Recently Used)和LFU(Least Frequently Used)是两种最常见的缓存淘汰算法。本文将深入探讨这两种算法的原理、实现、应用场景以及它们之间的核心差异。

在这里插入图片描述

一、基础概念解析

1.1 LRU(Least Recently Used)- 最近最少使用

LRU算法的核心思想是:最近使用的数据很可能在近期再次被使用,而最久未使用的数据被再次访问的概率较低

工作原理:

  • 维护数据的访问时间顺序
  • 每次访问数据时,将其移动到最近使用的位置
  • 当需要淘汰数据时,移除最久未使用的数据

时间复杂度: O(1) 访问和更新

1.2 LFU(Least Frequently Used)- 最少使用频率

LFU算法的核心思想是:使用频率高的数据在未来被访问的概率更大,应该优先保留在缓存中

工作原理:

  • 维护每个数据的访问频率计数
  • 每次访问数据时,增加其频率计数
  • 当需要淘汰数据时,移除访问频率最低的数据

时间复杂度: O(1) 访问,但实现复杂度较高

二、核心差异对比

维度LRULFU
关注点访问时间的新旧访问频率的高低
适用场景时间局部性强的场景热点数据相对固定的场景
内存开销较低(只需维护访问顺序)较高(需要维护频率计数)
实现复杂度中等较高
对突发访问的响应敏感(立即调整优先级)不敏感(需要累积频率)
冷启动处理较好较差(新数据频率为0)

三、LRU算法实现

3.1 基于双向链表 + HashMap的经典实现

public class LRUCache<K, V> {// 双向链表节点private static class Node<K, V> {K key;V value;Node<K, V> prev;Node<K, V> next;Node(K key, V value) {this.key = key;this.value = value;}}private final int capacity;private final Map<K, Node<K, V>> cache;private final Node<K, V> head;private final Node<K, V> tail;public LRUCache(int capacity) {this.capacity = capacity;this.cache = new HashMap<>(capacity);// 创建虚拟头尾节点this.head = new Node<>(null, null);this.tail = new Node<>(null, null);head.next = tail;tail.prev = head;}public V get(K key) {Node<K, V> node = cache.get(key);if (node == null) {return null;}// 移动到头部(最近使用)moveToHead(node);return node.value;}public void put(K key, V value) {Node<K, V> existingNode = cache.get(key);if (existingNode != null) {// 更新现有节点existingNode.value = value;moveToHead(existingNode);} else {// 添加新节点Node<K, V> newNode = new Node<>(key, value);if (cache.size() >= capacity) {// 移除最久未使用的节点Node<K, V> last = removeTail();cache.remove(last.key);}cache.put(key, newNode);addToHead(newNode);}}private void addToHead(Node<K, V> node) {node.prev = head;node.next = head.next;head.next.prev = node;head.next = node;}private void removeNode(Node<K, V> node) {node.prev.next = node.next;node.next.prev = node.prev;}private void moveToHead(Node<K, V> node) {removeNode(node);addToHead(node);}private Node<K, V> removeTail() {Node<K, V> last = tail.prev;removeNode(last);return last;}// 获取当前缓存状态(用于调试)public void printCacheState() {System.out.print("LRU Cache: [");Node<K, V> current = head.next;while (current != tail) {System.out.print(current.key + ":" + current.value);if (current.next != tail) {System.out.print(" -> ");}current = current.next;}System.out.println("]");}
}

3.2 基于LinkedHashMap的简化实现

public class SimpleLRUCache<K, V> extends LinkedHashMap<K, V> {private final int capacity;public SimpleLRUCache(int capacity) {// accessOrder=true 表示按访问顺序排序super(capacity + 1, 1.0f, true);this.capacity = capacity;}@Overrideprotected boolean removeEldestEntry(Map.Entry<K, V> eldest) {return size() > capacity;}// 线程安全版本public synchronized V getSafe(K key) {return super.get(key);}public synchronized V putSafe(K key, V value) {return super.put(key, value);}
}

四、LFU算法实现

4.1 基于双HashMap的高效实现

public class LFUCache<K, V> {// 缓存节点private static class Node<K, V> {K key;V value;int frequency;Node<K, V> prev;Node<K, V> next;Node(K key, V value) {this.key = key;this.value = value;this.frequency = 1;}}// 频率桶,维护相同频率的节点private static class FrequencyBucket<K, V> {int frequency;Node<K, V> head;Node<K, V> tail;FrequencyBucket(int frequency) {this.frequency = frequency;this.head = new Node<>(null, null);this.tail = new Node<>(null, null);head.next = tail;tail.prev = head;}void addNode(Node<K, V> node) {node.prev = head;node.next = head.next;head.next.prev = node;head.next = node;}void removeNode(Node<K, V> node) {node.prev.next = node.next;node.next.prev = node.prev;}Node<K, V> removeLast() {Node<K, V> last = tail.prev;if (last != head) {removeNode(last);return last;}return null;}boolean isEmpty() {return head.next == tail;}}private final int capacity;private final Map<K, Node<K, V>> cache;private final Map<Integer, FrequencyBucket<K, V>> frequencyBuckets;private int minFrequency;public LFUCache(int capacity) {this.capacity = capacity;this.cache = new HashMap<>();this.frequencyBuckets = new HashMap<>();this.minFrequency = 1;}public V get(K key) {Node<K, V> node = cache.get(key);if (node == null) {return null;}// 更新频率updateFrequency(node);return node.value;}public void put(K key, V value) {if (capacity <= 0) {return;}Node<K, V> existingNode = cache.get(key);if (existingNode != null) {// 更新现有节点existingNode.value = value;updateFrequency(existingNode);} else {// 添加新节点if (cache.size() >= capacity) {// 移除最低频率的节点removeLFUNode();}Node<K, V> newNode = new Node<>(key, value);cache.put(key, newNode);// 添加到频率为1的桶中FrequencyBucket<K, V> bucket = frequencyBuckets.computeIfAbsent(1, k -> new FrequencyBucket<>(1));bucket.addNode(newNode);minFrequency = 1;}}private void updateFrequency(Node<K, V> node) {int oldFreq = node.frequency;int newFreq = oldFreq + 1;// 从旧频率桶中移除FrequencyBucket<K, V> oldBucket = frequencyBuckets.get(oldFreq);oldBucket.removeNode(node);// 如果旧桶为空且是最小频率,更新最小频率if (oldBucket.isEmpty() && oldFreq == minFrequency) {minFrequency++;}// 更新节点频率node.frequency = newFreq;// 添加到新频率桶FrequencyBucket<K, V> newBucket = frequencyBuckets.computeIfAbsent(newFreq, k -> new FrequencyBucket<>(newFreq));newBucket.addNode(node);}private void removeLFUNode() {FrequencyBucket<K, V> minBucket = frequencyBuckets.get(minFrequency);Node<K, V> nodeToRemove = minBucket.removeLast();if (nodeToRemove != null) {cache.remove(nodeToRemove.key);}}// 获取当前缓存状态(用于调试)public void printCacheState() {System.out.println("LFU Cache State:");for (Map.Entry<Integer, FrequencyBucket<K, V>> entry : frequencyBuckets.entrySet()) {int freq = entry.getKey();FrequencyBucket<K, V> bucket = entry.getValue();if (!bucket.isEmpty()) {System.out.print("Frequency " + freq + ": [");Node<K, V> current = bucket.head.next;while (current != bucket.tail) {System.out.print(current.key + ":" + current.value);if (current.next != bucket.tail) {System.out.print(", ");}current = current.next;}System.out.println("]");}}System.out.println("Min Frequency: " + minFrequency);}
}

4.2 简化版LFU实现

public class SimpleLFUCache<K, V> {private final int capacity;private final Map<K, V> cache;private final Map<K, Integer> frequencies;private final Map<Integer, LinkedHashSet<K>> frequencyGroups;private int minFrequency;public SimpleLFUCache(int capacity) {this.capacity = capacity;this.cache = new HashMap<>();this.frequencies = new HashMap<>();this.frequencyGroups = new HashMap<>();this.minFrequency = 1;}public V get(K key) {if (!cache.containsKey(key)) {return null;}updateFrequency(key);return cache.get(key);}public void put(K key, V value) {if (capacity <= 0) {return;}if (cache.containsKey(key)) {cache.put(key, value);updateFrequency(key);} else {if (cache.size() >= capacity) {evictLFU();}cache.put(key, value);frequencies.put(key, 1);frequencyGroups.computeIfAbsent(1, k -> new LinkedHashSet<>()).add(key);minFrequency = 1;}}private void updateFrequency(K key) {int oldFreq = frequencies.get(key);int newFreq = oldFreq + 1;frequencies.put(key, newFreq);frequencyGroups.get(oldFreq).remove(key);if (frequencyGroups.get(oldFreq).isEmpty() && oldFreq == minFrequency) {minFrequency++;}frequencyGroups.computeIfAbsent(newFreq, k -> new LinkedHashSet<>()).add(key);}private void evictLFU() {K keyToRemove = frequencyGroups.get(minFrequency).iterator().next();frequencyGroups.get(minFrequency).remove(keyToRemove);frequencies.remove(keyToRemove);cache.remove(keyToRemove);}
}

五、实际应用场景分析

5.1 LRU适用场景

1. Web应用中的页面缓存

// 用户最近访问的页面更可能再次访问
public class WebPageCache {private final LRUCache<String, String> pageCache;public WebPageCache(int capacity) {this.pageCache = new LRUCache<>(capacity);}public String getPage(String url) {String content = pageCache.get(url);if (content == null) {content = loadPageFromDatabase(url);pageCache.put(url, content);}return content;}private String loadPageFromDatabase(String url) {// 模拟从数据库加载页面内容return "Page content for " + url;}
}

2. 操作系统页面置换

// 模拟操作系统的页面置换算法
public class OSPageReplacement {private final LRUCache<Integer, String> memoryPages;public OSPageReplacement(int memorySize) {this.memoryPages = new LRUCache<>(memorySize);}public void accessPage(int pageNumber) {String page = memoryPages.get(pageNumber);if (page == null) {// 页面不在内存中,需要从磁盘加载page = loadPageFromDisk(pageNumber);memoryPages.put(pageNumber, page);System.out.println("Page " + pageNumber + " loaded from disk");} else {System.out.println("Page " + pageNumber + " found in memory");}}private String loadPageFromDisk(int pageNumber) {return "Page " + pageNumber + " data";}
}

5.2 LFU适用场景

1. 热点数据缓存

// 商品信息缓存,热门商品被频繁访问
public class ProductCache {private final LFUCache<String, Product> productCache;public ProductCache(int capacity) {this.productCache = new LFUCache<>(capacity);}public Product getProduct(String productId) {Product product = productCache.get(productId);if (product == null) {product = loadProductFromDatabase(productId);productCache.put(productId, product);}return product;}private Product loadProductFromDatabase(String productId) {// 模拟从数据库加载商品信息return new Product(productId, "Product " + productId);}static class Product {String id;String name;Product(String id, String name) {this.id = id;this.name = name;}}
}

2. CDN缓存策略

// CDN边缘节点的内容缓存
public class CDNCache {private final LFUCache<String, byte[]> contentCache;private final Map<String, Long> accessStats;public CDNCache(int capacity) {this.contentCache = new LFUCache<>(capacity);this.accessStats = new ConcurrentHashMap<>();}public byte[] getContent(String url) {// 记录访问统计accessStats.merge(url, 1L, Long::sum);byte[] content = contentCache.get(url);if (content == null) {content = fetchFromOriginServer(url);contentCache.put(url, content);}return content;}private byte[] fetchFromOriginServer(String url) {// 模拟从源服务器获取内容return ("Content for " + url).getBytes();}
}

六、性能测试与对比

6.1 性能测试框架

public class CachePerformanceTest {public static void main(String[] args) {int capacity = 1000;int testSize = 10000;// 测试LRU性能testLRUPerformance(capacity, testSize);// 测试LFU性能testLFUPerformance(capacity, testSize);// 对比不同访问模式下的命中率compareHitRates(capacity);}private static void testLRUPerformance(int capacity, int testSize) {LRUCache<Integer, String> lruCache = new LRUCache<>(capacity);Random random = new Random(42);long startTime = System.nanoTime();for (int i = 0; i < testSize; i++) {int key = random.nextInt(capacity * 2);lruCache.put(key, "value" + key);}for (int i = 0; i < testSize; i++) {int key = random.nextInt(capacity * 2);lruCache.get(key);}long endTime = System.nanoTime();System.out.println("LRU Performance: " + (endTime - startTime) / 1_000_000 + " ms");}private static void testLFUPerformance(int capacity, int testSize) {LFUCache<Integer, String> lfuCache = new LFUCache<>(capacity);Random random = new Random(42);long startTime = System.nanoTime();for (int i = 0; i < testSize; i++) {int key = random.nextInt(capacity * 2);lfuCache.put(key, "value" + key);}for (int i = 0; i < testSize; i++) {int key = random.nextInt(capacity * 2);lfuCache.get(key);}long endTime = System.nanoTime();System.out.println("LFU Performance: " + (endTime - startTime) / 1_000_000 + " ms");}private static void compareHitRates(int capacity) {LRUCache<Integer, String> lruCache = new LRUCache<>(capacity);LFUCache<Integer, String> lfuCache = new LFUCache<>(capacity);// 模拟不同的访问模式testTimeLocalityPattern(lruCache, lfuCache);testHotDataPattern(lruCache, lfuCache);testRandomPattern(lruCache, lfuCache);}private static void testTimeLocalityPattern(LRUCache<Integer, String> lru, LFUCache<Integer, String> lfu) {System.out.println("\n=== 时间局部性访问模式 ===");int lruHits = 0, lfuHits = 0;int totalAccess = 1000;// 模拟时间局部性:最近访问的数据很快会再次被访问for (int i = 0; i < totalAccess; i++) {int key = i % 100; // 限制在较小范围内,形成时间局部性// 先插入数据lru.put(key, "value" + key);lfu.put(key, "value" + key);// 立即或很快再次访问if (i > 10 && Math.random() < 0.7) {int recentKey = Math.max(0, key - (int)(Math.random() * 10));if (lru.get(recentKey) != null) lruHits++;if (lfu.get(recentKey) != null) lfuHits++;}}System.out.println("LRU命中率: " + (lruHits * 100.0 / totalAccess) + "%");System.out.println("LFU命中率: " + (lfuHits * 100.0 / totalAccess) + "%");}private static void testHotDataPattern(LRUCache<Integer, String> lru, LFUCache<Integer, String> lfu) {System.out.println("\n=== 热点数据访问模式 ===");// 清空缓存lru = new LRUCache<>(100);lfu = new LFUCache<>(100);int lruHits = 0, lfuHits = 0;int totalAccess = 1000;// 80%的访问集中在20%的数据上(80-20法则)for (int i = 0; i < totalAccess; i++) {int key;if (Math.random() < 0.8) {key = (int)(Math.random() * 20); // 热点数据} else {key = 20 + (int)(Math.random() * 200); // 冷数据}// 先尝试获取if (lru.get(key) != null) lruHits++;if (lfu.get(key) != null) lfuHits++;// 如果不存在则插入lru.put(key, "value" + key);lfu.put(key, "value" + key);}System.out.println("LRU命中率: " + (lruHits * 100.0 / totalAccess) + "%");System.out.println("LFU命中率: " + (lfuHits * 100.0 / totalAccess) + "%");}private static void testRandomPattern(LRUCache<Integer, String> lru, LFUCache<Integer, String> lfu) {System.out.println("\n=== 随机访问模式 ===");// 清空缓存lru = new LRUCache<>(100);lfu = new LFUCache<>(100);int lruHits = 0, lfuHits = 0;int totalAccess = 1000;Random random = new Random(42);for (int i = 0; i < totalAccess; i++) {int key = random.nextInt(300); // 完全随机访问// 先尝试获取if (lru.get(key) != null) lruHits++;if (lfu.get(key) != null) lfuHits++;// 如果不存在则插入lru.put(key, "value" + key);lfu.put(key, "value" + key);}System.out.println("LRU命中率: " + (lruHits * 100.0 / totalAccess) + "%");System.out.println("LFU命中率: " + (lfuHits * 100.0 / totalAccess) + "%");}
}

七、实际项目中的最佳实践

7.1 缓存策略选择指南

public class CacheStrategySelector {public enum CacheType {LRU, LFU, HYBRID}public static CacheType recommendStrategy(AccessPattern pattern) {switch (pattern) {case TEMPORAL_LOCALITY:// 时间局部性强:用户浏览历史、最近文档等return CacheType.LRU;case FREQUENCY_BASED:// 基于频率:热门商品、流行内容等return CacheType.LFU;case MIXED_PATTERN:// 混合模式:大型Web应用return CacheType.HYBRID;default:return CacheType.LRU; // 默认选择LRU}}public enum AccessPattern {TEMPORAL_LOCALITY,    // 时间局部性FREQUENCY_BASED,      // 基于频率MIXED_PATTERN        // 混合模式}
}

7.2 混合缓存策略实现

public class HybridCache<K, V> {private final LRUCache<K, V> lruCache;private final LFUCache<K, V> lfuCache;private final double lruRatio; // LRU缓存占总容量的比例public HybridCache(int totalCapacity, double lruRatio) {this.lruRatio = lruRatio;int lruCapacity = (int) (totalCapacity * lruRatio);int lfuCapacity = totalCapacity - lruCapacity;this.lruCache = new LRUCache<>(lruCapacity);this.lfuCache = new LFUCache<>(lfuCapacity);}public V get(K key) {// 首先尝试从LRU缓存获取(时间敏感数据)V value = lruCache.get(key);if (value != null) {return value;}// 然后尝试从LFU缓存获取(频率敏感数据)value = lfuCache.get(key);if (value != null) {// 将热点数据提升到LRU缓存promoteToLRU(key, value);return value;}return null;}public void put(K key, V value) {// 新数据优先放入LRU缓存lruCache.put(key, value);}private void promoteToLRU(K key, V value) {// 将频繁访问的数据从LFU提升到LRUlruCache.put(key, value);// 注意:这里可能需要从LFU中移除,具体策略可以调整}public void printCacheState() {System.out.println("=== Hybrid Cache State ===");System.out.println("LRU part:");lruCache.printCacheState();System.out.println("LFU part:");lfuCache.printCacheState();}
}

7.3 生产环境配置建议

public class ProductionCacheConfig {// Redis配置示例public RedisTemplate<String, Object> configureRedisCache() {RedisTemplate<String, Object> template = new RedisTemplate<>();// 配置LRU策略template.setConnectionFactory(jedisConnectionFactory());// 设置序列化方式template.setKeySerializer(new StringRedisSerializer());template.setValueSerializer(new GenericJackson2JsonRedisSerializer());return template;}// Caffeine本地缓存配置public Cache<String, Object> configureCaffeineCache() {return Caffeine.newBuilder().maximumSize(10000) // 最大缓存条目数.expireAfterWrite(Duration.ofMinutes(30)) // 写入后30分钟过期.removalListener((key, value, cause) -> {System.out.println("Removed: " + key + ", cause: " + cause);}).build();}// 多级缓存策略@Componentpublic static class MultiLevelCache {@Autowiredprivate Cache<String, Object> l1Cache; // Caffeine本地缓存@Autowiredprivate RedisTemplate<String, Object> l2Cache; // Redis分布式缓存public Object get(String key) {// L1缓存查找Object value = l1Cache.getIfPresent(key);if (value != null) {return value;}// L2缓存查找value = l2Cache.opsForValue().get(key);if (value != null) {// 回填L1缓存l1Cache.put(key, value);return value;}return null;}public void put(String key, Object value) {// 同时写入L1和L2缓存l1Cache.put(key, value);l2Cache.opsForValue().set(key, value, Duration.ofHours(1));}}
}

八、常见问题与解决方案

8.1 LRU缓存的常见问题

1. 缓存污染问题

// 问题:大量一次性访问的数据污染缓存
public class AntiPollutionLRU<K, V> extends LRUCache<K, V> {private final Set<K> probationaryKeys;private final int probationaryCapacity;public AntiPollutionLRU(int capacity) {super(capacity);this.probationaryCapacity = capacity / 4; // 25%作为试用区this.probationaryKeys = new LinkedHashSet<>();}@Overridepublic V get(K key) {if (probationaryKeys.contains(key)) {// 第二次访问,提升到主缓存probationaryKeys.remove(key);return super.get(key);}return super.get(key);}@Overridepublic void put(K key, V value) {if (!containsKey(key)) {// 新数据先放入试用区if (probationaryKeys.size() >= probationaryCapacity) {Iterator<K> iterator = probationaryKeys.iterator();iterator.next();iterator.remove();}probationaryKeys.add(key);} else {super.put(key, value);}}
}

8.2 LFU缓存的常见问题

1. 新数据难以进入缓存

public class AdaptiveLFU<K, V> extends LFUCache<K, V> {private final double decayFactor = 0.9; // 衰减因子private long lastDecayTime = System.currentTimeMillis();private final long decayInterval = 3600000; // 1小时衰减一次public AdaptiveLFU(int capacity) {super(capacity);}@Overridepublic V get(K key) {maybeDecayFrequencies();return super.get(key);}private void maybeDecayFrequencies() {long currentTime = System.currentTimeMillis();if (currentTime - lastDecayTime > decayInterval) {decayAllFrequencies();lastDecayTime = currentTime;}}private void decayAllFrequencies() {// 定期衰减所有频率,给新数据机会// 具体实现需要访问内部数据结构System.out.println("Decaying all frequencies by factor: " + decayFactor);}
}

九、总结与建议

9.1 选择决策树

是否有明显的时间局部性?
├─ 是 → 优先选择 LRU
│   ├─ 用户会话数据
│   ├─ 最近文档访问
│   └─ 浏览历史
│
└─ 否 → 是否有明显的热点数据?├─ 是 → 优先选择 LFU│   ├─ 商品目录│   ├─ 配置信息│   └─ 静态资源│└─ 否 → 考虑混合策略├─ 多级缓存├─ 自适应算法└─ 业务定制化策略

9.2 最佳实践总结

  1. 性能优先场景:选择LRU,实现简单,性能稳定
  2. 热点数据场景:选择LFU,更好地保护高价值数据
  3. 复杂业务场景:考虑混合策略或自定义算法
  4. 监控和调优:定期分析缓存命中率和访问模式
  5. 渐进式优化:从简单策略开始,逐步优化

9.3 性能调优建议

  • 内存使用:LRU < LFU,根据内存预算选择
  • CPU开销:LRU < LFU,高并发场景需要考虑
  • 命中率:根据业务特点选择,定期评估效果
  • 实现复杂度:从简单到复杂,满足需求即可

结语

LRU和LFU作为两种经典的缓存淘汰算法,各有其适用场景和优势。LRU更适合时间局部性强的场景,实现相对简单且性能稳定;LFU更适合有明显热点数据的场景,能更好地保护高价值数据。

在实际项目中,我们应该根据具体的业务特点、性能要求和资源约束来选择合适的策略。同时,随着业务的发展和数据访问模式的变化,也需要定期评估和调整缓存策略,以达到最佳的性能效果。

记住,没有银弹——最好的缓存策略是最适合你业务场景的策略。通过深入理解算法原理、合理设计实现方案、持续监控和优化,我们就能构建出高效可靠的缓存系统。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/93846.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/93846.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【C语言】字符函数与字符串函数详解

文章目录一、字符分类函数二、字符转换函数三、strlen函数&#xff1a;计算字符串长度功能说明使用示例模拟实现四、strcpy函数&#xff1a;字符串拷贝功能说明模拟实现五、strcat函数&#xff1a;字符串追加功能说明模拟实现六、strcmp函数&#xff1a;字符串比较比较规则模拟…

uvicorn 启动重复加载 多次加载

目录 uvicorn 启动重复加载 多次加载 解决方法1&#xff1a; 解决方法2&#xff1a; uvicorn 启动重复加载 多次加载 fastapi_aa 是当前类 解决方法1&#xff1a; import uvicornfrom fastapi import FastAPIapp FastAPI()if __name__ "__main__":if sys.gett…

Bard AI本地部署教程:在自己的服务器上运行谷歌AI

Bard AI本地部署教程:在自己的服务器上运行谷歌AI 关键词:Bard AI、本地部署、服务器、谷歌AI、运行教程 摘要:本文旨在为大家详细介绍如何在自己的服务器上实现Bard AI的本地部署。我们会从背景知识讲起,逐步深入到核心概念、算法原理、操作步骤,还会提供项目实战案例和实…

应急响应处置案例(上)

本文目录 目录 本文目录 Web安全事件 概述 案例1 - webshell 背景 排查情况 天眼 服务器 案例2 - Struts2 排查情况 天眼 服务器 案例3 - Redis未授权 背景 排查情况 天眼 服务器 案例4 - EW内网穿透 背景 排查情况 天眼 服务器 案例5 - 一句话木马 背…

面试官问我:“为什么不能完全用对象替代指针?”我笑了:看看Google和Linux内核代码就知道了!

本篇摘要 本篇将以最通俗易懂的语言&#xff0c;形象的讲述为什么很多情境下&#xff0c;我们优先考虑的使用指针而不是对象本身&#xff0c;本篇将给出你答案&#xff01; 一.从一个生活例子说起&#xff0c;形象秒懂 想象一下&#xff0c;你去图书馆借书&#xff0c;下面你…

CAMx大气污染模拟全流程:Linux编译/多重嵌套配置/SMOKE清单预处理/SA-DDM-PA工具应用与科研绘图结果可视化分析

CAMx模型是一个基于大气化学&#xff0c;针对臭氧、颗粒物和雾霾天气过程的大气污染物计算模型。【目标】&#xff1a;1、掌握CAMx模式的区域空气质量模拟案例配置技术方法2、掌握SMOKE模型的CAMx模式大气排放清单输入准备方法3、掌握CAMx模式污染来源解析工具&#xff08;SA&a…

嵌入式学习笔记-MCU阶段-DAY10ESP8266模块

1.ESP8266概述 官方网址&#xff1a;ESP8266 Wi-Fi MCU I 乐鑫科技 (espressif.com.cn) ESP8266模块---wifi模块 产品特点&#xff1a; 2.ESP8266中的wifi: ESP8266EX ⽀持 TCP/IP 协议&#xff0c;完全遵循 802.11 b/g/n WLAN MAC 协议&#xff0c;⽀持分布式控制功能 (DC…

如何快速通过软件项目验收,第三方软件检测机构的重要性

在客户和开发团队之间&#xff0c;最后临门一脚的项目验收环节总容易出现各种问题&#xff0c;以至于时间无限拉长&#xff0c;久久不见结束&#xff0c;为此给大家准备了一份如何快速通过软件项目验收的内容来帮助大家结束持久战。 一、项目验收准备材料 &#xff08;一&…

洛谷做题3:P5711 【深基3.例3】闰年判断

文章目录题目描述输入格式输出格式输入输出样例分析代码题目描述 输入一个年份&#xff0c;判断这一年是否是闰年&#xff0c;如果是输出 1&#xff0c;否则输出 0。 1582 年以来&#xff0c;闰年的定义&#xff1a; 普通闰年&#xff1a;公历年份是 4 的倍数&#xff0c;且不…

PMP证书可以挂靠吗?怎么挂靠?

哈喽学弟学妹们&#xff0c;作为过来人&#xff0c;今天想跟大家聊聊 PMP 证书挂靠这事儿 —— 可能不少准备考或者刚考完的同学都琢磨过&#xff0c;但学长得跟你们交个底&#xff1a;这事儿真不行&#xff0c;更别提啥挂靠费了。先说说 PMP 证书本身哈&#xff0c;它是美国 P…

91-基于Spark的空气质量数据分析可视化系统

基于Spark的空气质量数据分析可视化系统设计与实现 项目概述 本项目是一个基于Apache Spark的大数据分析和可视化系统&#xff0c;专门用于空气质量数据的采集、分析、预测和可视化展示。系统采用分布式计算架构&#xff0c;结合机器学习算法&#xff0c;实现了对全国12个主要…

leetcode 2419. 按位与最大的最长子数组 中等

给你一个长度为 n 的整数数组 nums 。考虑 nums 中进行 按位与&#xff08;bitwise AND&#xff09;运算得到的值 最大 的 非空 子数组。换句话说&#xff0c;令 k 是 nums 任意 子数组执行按位与运算所能得到的最大值。那么&#xff0c;只需要考虑那些执行一次按位与运算后等于…

Git 命令使用指南:从入门到进阶

目录1. Git 基本操作1.1 添加文件到暂存区1.2 提交更改到本地仓库1.3 查看工作区状态1.4 查看提交历史1.5 查看引用日志&#xff08;包括已删除的记录&#xff09;2. 版本回退与撤销2.1 版本回退2.2 查看已删除的提交记录3. 分支管理3.1 查看分支3.2 创建并切换到新分支3.3 合并…

SQL数据库连接Python实战:疫情数据指挥中心搭建指南

SQL数据库连接Python实战&#xff1a;疫情数据指挥中心搭建指南从WHO数据集到实时仪表盘&#xff0c;构建工业级疫情监控系统一、疫情数据指挥中心&#xff1a;全球健康危机的中枢神经​​疫情数据价值​​&#xff1a;全球每日新增病例&#xff1a;50万疫苗接种数据&#xff1…

参赛单位条件放宽!2025年“数据要素 ×”大赛福建分赛厦门赛区赛事有新调整

各位伙伴们 想抓住数据价值机遇 在行业赛场上崭露头角吗&#xff1f; 2025年“数据要素”大赛 福建分赛厦门赛区已启动 这份超全赛事解读 带你一站式摸清参赛关键&#xff01; 01 参赛单位要求放宽 经省分赛组委会与国家赛事组委会沟通&#xff0c;不具有独立法人资格的…

BasicAuthenticationFilter处理 HTTP 基本认证(Basic Authentication)的核心过滤器详解

BasicAuthenticationFilter处理 HTTP 基本认证&#xff08;Basic Authentication&#xff09;的核心过滤器详解在 Spring Security 中&#xff0c;BasicAuthenticationFilter 是​​处理 HTTP 基本认证&#xff08;Basic Authentication&#xff09;的核心过滤器​​&#xff0…

Next.js 中使用 MongoDB 完整指南

1. 安装依赖npm install mongodb # 或者使用 mongoose&#xff08;ODM&#xff09; npm install mongoose2. 数据库连接配置使用原生 MongoDB 驱动创建 lib/mongodb.js 文件&#xff1a;import { MongoClient } from mongodbconst uri process.env.MONGODB_URI const options …

嵌入式系统教学范式演进:云端仿真平台如何重构温湿度监测实验教学

在嵌入式系统开发的教学中&#xff0c;环境温湿度监测实验是经典的入门项目。它涉及传感器原理、外设驱动、数据采集和通信协议等核心知识点。然而传统实验模式面临硬件成本高、调试周期长、设备易损坏等痛点。学生往往因接线错误或代码bug导致传感器或开发板烧毁&#xff0c;不…

1.6万 Star 的流行容器云平台停止开源

什么是 KubeSphere &#xff1f; KubeSphere 是面向云原生应用的容器混合云。 KubeSphere 愿景是打造一个以 Kubernetes 为内核的云原生分布式操作系统&#xff0c;它的架构可以非常方便地使第三方应用与云原生生态组件进行即插即用&#xff08;plug-and-play&#xff09;的集成…

广东省省考备考(第六十三天8.1)——资料分析、数量(强化训练)

资料分析 错题解析解析解析今日题目正确率&#xff1a;80% 数量关系&#xff1a;数学运算 错题解析解析标记题解析解析解析今日题目正确率&#xff1a;87%