湖南网站建设公司磐石网络WordPress在服务器什么位置
湖南网站建设公司磐石网络,WordPress在服务器什么位置,山东神华网站建设,东莞网络营销策划培训这些年我参与设计过很多系统#xff0c;越来越深刻地认识到#xff1a;一个系统的性能如何#xff0c;很大程度上取决于缓存用得怎么样。
同样是缓存#xff0c;为何有人用起来系统飞升#xff0c;有人却踩坑不断#xff1f;
有些小伙伴在工作中可能遇到过这样的困惑越来越深刻地认识到一个系统的性能如何很大程度上取决于缓存用得怎么样。同样是缓存为何有人用起来系统飞升有人却踩坑不断有些小伙伴在工作中可能遇到过这样的困惑知道要用缓存但面对本地缓存、Redis、Memcached、CDN等多种选择到底该用哪种今天这篇文章跟大家一起聊聊工作中最常用的6种缓存希望对你会有所帮助。01 为什么缓存如此重要在正式介绍各种缓存之前我们先要明白为什么要用缓存想象这样一个场景你的电商网站首页每次打开都要从数据库中查询轮播图、热门商品、分类信息等数据。如果每秒有1万个用户访问数据库就要承受1万次查询压力。// 没有缓存时的查询 public Product getProductById(Long id) { // 每次都直接查询数据库 return productDao.findById(id); // 每次都是慢速的磁盘IO }这就是典型的无缓存场景。数据库的磁盘IO速度远低于内存当并发量上来后系统响应变慢数据库连接池被占满最终导致服务不可用。缓存的核心价值可以用下面这个公式理解系统性能 (缓存命中率 × 缓存访问速度) ((1 - 缓存命中率) × 后端访问速度)缓存之所以能提升性能基于两个计算机科学的基本原理局部性原理程序访问的数据通常具有时间和空间局部性存储层次结构不同存储介质的速度差异巨大内存比SSD快100倍比HDD快10万倍从用户请求到数据返回数据可能经过的各级缓存路径如下图所示理解了缓存的重要性接下来我们逐一剖析这六种最常用的缓存技术。02 本地缓存最简单直接的性能提升本地缓存指的是在应用进程内部维护的缓存存储数据存储在JVM堆内存中。核心特点访问最快直接内存操作无网络开销实现简单无需搭建额外服务数据隔离每个应用实例独享自己的缓存常用实现1. Guava CacheGoogle提供的优秀本地缓存库// Guava Cache 示例 LoadingCacheLong, Product productCache CacheBuilder.newBuilder() .maximumSize(10000) // 最大缓存项数 .expireAfterWrite(10, TimeUnit.MINUTES) // 写入后10分钟过期 .expireAfterAccess(5, TimeUnit.MINUTES) // 访问后5分钟过期 .recordStats() // 开启统计 .build(new CacheLoaderLong, Product() { Override public Product load(Long productId) { // 当缓存未命中时自动加载数据 return productDao.findById(productId); } }); // 使用缓存 public Product getProduct(Long id) { try { return productCache.get(id); } catch (ExecutionException e) { thrownew RuntimeException(加载产品失败, e); } }2. CaffeineGuava Cache的现代替代品性能更优// Caffeine 示例性能优于Guava Cache CacheLong, Product caffeineCache Caffeine.newBuilder() .maximumSize(10_000) .expireAfterWrite(10, TimeUnit.MINUTES) .refreshAfterWrite(1, TimeUnit.MINUTES) // 支持刷新Guava不支持 .recordStats() .build(productId - productDao.findById(productId)); // 异步获取 public CompletableFutureProduct getProductAsync(Long id) { return caffeineCache.get(id, productId - CompletableFuture.supplyAsync(() - productDao.findById(productId))); }适用场景数据量不大通常不超过10万条数据变化不频繁对访问速度要求极致如配置信息、静态字典、用户会话信息短期优缺点分析优点极速访问、零网络开销、实现简单缺点数据不一致各节点独立、内存限制、重启丢失有些小伙伴在工作中可能会犯一个错误在分布式系统中过度依赖本地缓存导致各节点数据不一致。记住本地缓存适合存储只读或弱一致性的数据。03 分布式缓存之王Redis的深度解析当数据需要在多个应用实例间共享时本地缓存就不够用了这时需要分布式缓存。而Redis无疑是这一领域的王者。Redis的核心优势// Spring Boot Redis 示例 Component publicclass ProductCacheService { Autowired private RedisTemplateString, Object redisTemplate; privatestaticfinal String PRODUCT_KEY_PREFIX product:; privatestaticfinal Duration CACHE_TTL Duration.ofMinutes(30); // 缓存查询 public Product getProduct(Long id) { String key PRODUCT_KEY_PREFIX id; // 1. 先查缓存 Product product (Product) redisTemplate.opsForValue().get(key); if (product ! null) { return product; } // 2. 缓存未命中查数据库 product productDao.findById(id); if (product ! null) { // 3. 写入缓存 redisTemplate.opsForValue().set(key, product, CACHE_TTL); } return product; } // 使用更高效的方式缓存空值防止缓存穿透 public Product getProductWithNullCache(Long id) { String key PRODUCT_KEY_PREFIX id; String nullKey PRODUCT_KEY_PREFIX null: id; // 检查是否是空值防缓存穿透 if (Boolean.TRUE.equals(redisTemplate.hasKey(nullKey))) { returnnull; } Product product (Product) redisTemplate.opsForValue().get(key); if (product ! null) { return product; } product productDao.findById(id); if (product null) { // 缓存空值短时间过期 redisTemplate.opsForValue().set(nullKey, , Duration.ofMinutes(5)); returnnull; } redisTemplate.opsForValue().set(key, product, CACHE_TTL); return product; } }Redis的丰富数据结构Redis不只是简单的Key-Value存储它的多种数据结构适应不同场景数据结构适用场景示例String缓存对象、计数器SET user:1 {name:张三}Hash存储对象属性HSET product:1001 name 手机 price 2999List消息队列、最新列表LPUSH news:latest 新闻标题Set标签、共同好友SADD user:100:tags 数码 科技Sorted Set排行榜、延迟队列ZADD leaderboard 95 玩家ABitmap用户签到、活跃统计SETBIT sign:2023:10 1 1集群模式选择适用场景会话存储分布式Session排行榜、计数器消息队列分布式锁热点数据缓存有些小伙伴在工作中使用Redis时只把它当简单的Key-Value用这就像用瑞士军刀只开瓶盖一样浪费。深入理解Redis的数据结构能让你的系统设计更优雅高效。04 Memcached简单高效的分布式缓存在Redis崛起之前Memcached是分布式缓存的首选。虽然现在Redis更流行但Memcached在某些场景下仍有其价值。Memcached vs Redis 核心区别// Memcached 客户端示例使用XMemcached publicclass MemcachedService { private MemcachedClient memcachedClient; public void init() throws IOException { // 创建客户端 memcachedClient new XMemcachedClientBuilder( AddrUtil.getAddresses(server1:11211 server2:11211)) .build(); } public Product getProduct(Long id) throws Exception { String key product_ id; // 从Memcached获取 Product product memcachedClient.get(key); if (product ! null) { return product; } // 缓存未命中 product productDao.findById(id); if (product ! null) { // 存储到Memcached过期时间30分钟 memcachedClient.set(key, 30 * 60, product); } return product; } }两者的核心差异对比特性RedisMemcached数据结构丰富String、Hash、List等简单Key-Value持久化支持RDB/AOF不支持线程模型单线程多线程内存管理多种策略可持久化纯内存重启丢失使用场景缓存多样化数据结构纯缓存何时选择Memcached纯缓存场景只需要简单的Key-Value缓存超大Value存储Memcached对超大Value支持更好多线程高并发Memcached的多线程模型在极端并发下可能表现更好05 CDN缓存加速静态资源的利器有些小伙伴可能会疑惑CDN也算缓存吗当然算而且是地理位置最近的缓存。CDN的工作原理CDNContent Delivery Network通过在各地部署边缘节点将静态资源缓存到离用户最近的节点。// 在应用中生成CDN链接 publicclass CDNService { private String cdnDomain https://cdn.yourcompany.com; public String getCDNUrl(String relativePath) { // 添加版本号或时间戳防止缓存旧版本 String version getFileVersion(relativePath); return String.format(%s/%s?v%s, cdnDomain, relativePath, version); } // 上传文件到CDN的示例伪代码 public void uploadToCDN(File file, String remotePath) { // 1. 上传到源站 uploadToOrigin(file, remotePath); // 2. 触发CDN预热将文件主动推送到边缘节点 preheatCDN(remotePath); // 3. 刷新旧缓存如果需要 refreshCDNCache(remotePath); } }CDN缓存策略配置# Nginx中的CDN缓存配置示例 location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ { expires 365d; # 缓存一年 add_header Cache-Control public, immutable; # 添加版本号作为查询参数 if ($query_string ~* ^v\d) { expires max; } }适用场景静态资源图片、CSS、JS文件软件下载包视频流媒体全球访问的网站06 浏览器缓存最前端的性能优化浏览器缓存是最容易被忽视但效果最直接的缓存层级。合理利用浏览器缓存可以大幅减少服务器压力。HTTP缓存头详解// Spring Boot中设置HTTP缓存头 RestController publicclass ResourceController { GetMapping(/static/{filename}) public ResponseEntityResource getStaticFile(PathVariable String filename) { Resource resource loadResource(filename); return ResponseEntity.ok() .cacheControl(CacheControl.maxAge(7, TimeUnit.DAYS)) // 缓存7天 .eTag(computeETag(resource)) // ETag用于协商缓存 .lastModified(resource.lastModified()) // 最后修改时间 .body(resource); } GetMapping(/dynamic/data) public ResponseEntityObject getDynamicData() { Object data getData(); // 动态数据设置较短缓存 return ResponseEntity.ok() .cacheControl(CacheControl.maxAge(30, TimeUnit.SECONDS)) // 30秒 .body(data); } }浏览器缓存的两种类型最佳实践静态资源设置长时间缓存如一年通过文件名哈希处理更新动态数据根据业务需求设置合理缓存时间API响应适当使用ETag和Last-Modified07 数据库缓存容易被忽略的内部优化数据库自身也有缓存机制理解这些机制能帮助我们写出更高效的SQL。MySQL查询缓存已废弃但值得了解-- 查看查询缓存状态MySQL 5.7及之前 SHOW VARIABLES LIKE query_cache%; -- 在8.0之前可以通过以下方式利用查询缓存 SELECT SQL_CACHE * FROM products WHERE category_id 10;InnoDB缓冲池Buffer Pool这是MySQL性能的关键缓存的是数据页和索引页。-- 查看缓冲池状态 SHOW ENGINE INNODB STATUS; -- 重要的监控指标 -- 缓冲池命中率 (1 - (innodb_buffer_pool_reads / innodb_buffer_pool_read_requests)) * 100% -- 命中率应尽可能接近100%数据库级缓存最佳实践合理设置缓冲池大小通常是系统内存的50%-70%优化查询避免全表扫描合理使用索引预热缓存重启后主动加载热点数据监控命中率持续优化有些小伙伴可能会过度依赖应用层缓存而忽略了数据库自身的缓存优化。数据库缓存是最后一道防线优化好它能让整个系统更健壮。08 综合对比与选型指南接下来我给大家一个选型指南实战中的多级缓存架构在实际的高并发系统中我们往往会采用多级缓存策略// 多级缓存示例本地缓存 Redis Component publicclass MultiLevelCacheService { Autowired private RedisTemplateString, Object redisTemplate; // 一级缓存本地缓存 private CacheLong, Product localCache Caffeine.newBuilder() .maximumSize(1000) .expireAfterWrite(30, TimeUnit.SECONDS) // 本地缓存时间短 .build(); // 二级缓存Redis privatestaticfinal Duration REDIS_TTL Duration.ofMinutes(10); public Product getProductWithMultiCache(Long id) { // 1. 查本地缓存 Product product localCache.getIfPresent(id); if (product ! null) { return product; } // 2. 查Redis String redisKey product: id; product (Product) redisTemplate.opsForValue().get(redisKey); if (product ! null) { // 回填本地缓存 localCache.put(id, product); return product; } // 3. 查数据库 product productDao.findById(id); if (product ! null) { // 写入Redis redisTemplate.opsForValue().set(redisKey, product, REDIS_TTL); // 写入本地缓存 localCache.put(id, product); } return product; } }09 缓存常见问题与解决方案在使用缓存的过程中我们不可避免地会遇到一些问题1. 缓存穿透问题大量请求查询不存在的数据绕过缓存直接击穿数据库。解决方案// 缓存空值方案 public Product getProductSafe(Long id) { String key product: id; String nullKey product:null: id; // 检查空值标记 if (redisTemplate.hasKey(nullKey)) { returnnull; } Product product (Product) redisTemplate.opsForValue().get(key); if (product ! null) { return product; } product productDao.findById(id); if (product null) { // 缓存空值短时间过期 redisTemplate.opsForValue().set(nullKey, , Duration.ofMinutes(5)); returnnull; } redisTemplate.opsForValue().set(key, product, Duration.ofMinutes(30)); return product; }2. 缓存雪崩问题大量缓存同时过期请求全部打到数据库。解决方案// 差异化过期时间 private Duration getRandomTTL() { // 基础30分钟 随机0-10分钟 long baseMinutes 30; long randomMinutes ThreadLocalRandom.current().nextLong(0, 10); return Duration.ofMinutes(baseMinutes randomMinutes); }3. 缓存击穿问题热点Key过期瞬间大量并发请求同时查询数据库。解决方案// 使用互斥锁分布式锁 public Product getProductWithLock(Long id) { String key product: id; Product product (Product) redisTemplate.opsForValue().get(key); if (product null) { // 尝试获取分布式锁 String lockKey lock:product: id; boolean locked redisTemplate.opsForValue() .setIfAbsent(lockKey, 1, Duration.ofSeconds(10)); if (locked) { try { // 双重检查 product (Product) redisTemplate.opsForValue().get(key); if (product null) { product productDao.findById(id); if (product ! null) { redisTemplate.opsForValue() .set(key, product, Duration.ofMinutes(30)); } } } finally { // 释放锁 redisTemplate.delete(lockKey); } } else { // 未获取到锁等待后重试 try { Thread.sleep(50); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } return getProductWithLock(id); // 递归重试 } } return product; }10 总结通过这篇文章我们系统地探讨了工作中最常用的六种缓存技术。每种缓存都有其独特的价值和应用场景本地缓存适合进程内、变化不频繁的只读数据Redis功能丰富的分布式缓存适合大多数共享缓存场景Memcached简单高效的分布式缓存适合纯Key-Value场景CDN缓存加速静态资源提升全球访问速度浏览器缓存最前端的优化减少不必要的网络请求数据库缓存最后一道防线优化数据库访问性能缓存使用的核心原则可以总结为以下几点分级缓存合理利用多级缓存架构合适粒度根据业务特点选择缓存粒度及时更新设计合理的缓存更新策略监控告警建立完善的缓存监控体系有些小伙伴在工作中使用缓存时容易陷入两个极端要么过度设计所有数据都加缓存要么忽视缓存让数据库承受所有压力。我们需要懂得在合适的地方使用合适的缓存在性能和复杂性之间找到最佳平衡点。