• java并发编程笔记(十一)——高并发处理思路和手段


    java并发编程笔记(十一)——高并发处理思路和手段

    扩容

    • 垂直扩容(纵向扩展):提高系统部件能力
    • 水平扩容(横向扩容):增加更多系统成员来实现

    缓存

    缓存特征

    命中率:命中数/(命中数+没有命中数)

    一、影响因素

    • 业务场景和业务需求
    • 缓存的设计(粒度和策略)
    • 缓存的容量和基础设施

    二、缓存分类和应用场景

    • 本地缓存:编程实现(成员变量,局部变量,静态变量)、Guava Cache
    • 分布式缓存:Memcache、Redis

    三、常用组件

    • Guava Cache

      public class GuavaCacheExample1 {
      
          public static void main(String[] args) {
      
              LoadingCache<String, Integer> cache = CacheBuilder.newBuilder()
                      .maximumSize(10) // 最多存放10个数据
                      .expireAfterWrite(10, TimeUnit.SECONDS) // 缓存10秒
                      .recordStats() // 开启记录状态数据功能
                      .build(new CacheLoader<String, Integer>() {
                          @Override
                          public Integer load(String key) throws Exception {
                              return -1;
                          }
                      });
      
              log.info("{}", cache.getIfPresent("key1")); // null
              cache.put("key1", 1);
              log.info("{}", cache.getIfPresent("key1")); // 1
              cache.invalidate("key1");
              log.info("{}", cache.getIfPresent("key1")); // null
      
              try {
                  log.info("{}", cache.get("key2")); // -1
                  cache.put("key2", 2);
                  log.info("{}", cache.get("key2")); // 2
      
                  log.info("{}", cache.size()); // 1
      
                  for (int i = 3; i < 13; i++) {
                      cache.put("key" + i, i);
                  }
                  log.info("{}", cache.size()); // 10
      
                  log.info("{}", cache.getIfPresent("key2")); // null
      
                  Thread.sleep(11000);
      
                  log.info("{}", cache.get("key5")); // -1
      
                  log.info("{},{}", cache.stats().hitCount(), cache.stats().missCount());
      
                  log.info("{},{}", cache.stats().hitRate(), cache.stats().missRate());
              } catch (Exception e) {
                  log.error("cache exception", e);
              }
          }
      }
      
    • Memcache

    • Redis

    **最大元素(空间):缓存大小 **

    清空策略:FIFO、LFO、LRU、过期时间,随机等

    FIFO: 先进先出策略

    LFO:最少使用策略

    LRU:最近最少使用策略

    高并发场景下缓存常见问题

    缓存一致性

    造成这种问题的场景:

    • 更新数据库成功--更新缓存失败--数据不一致
    • 更新缓存成功--更新数据库失败--数据不一致
    • 更新数据库成功--淘汰缓存失败--数据不一致
    • 淘汰缓存成功--更新数据库失败--查询缓存miss

    缓存并发问题

    同时大量请求访问缓存未命中,然后大量请求同时访问数据库造成数据库压力增大,解决办法是加锁,避免同时执行

    缓存穿透问题

    某个查询结果为空,导致缓存为存储,因此大量请求会去查询数据库,造成对数据库的压力大增

    解决办法:

    1、缓存空对象,缓存时间要短,适合命中率不高,但是需要频繁修改的数据

    2、进行单独过滤处理,对所有可能结果为空的请求进行统一存放,并且对请求进行拦截,避免请求到数据库对数据库造成大量压力

    缓存雪崩现象

    导致原因:缓存并发,缓存穿透等

    解决办法:过期时间设置随机数,避免同时失效10

    消息队列

    特性

    • 业务无关:只做消息分发
    • FIFO:先投递先到达
    • 容灾:节点的动态增加和消息的持久化
    • 性能:吞吐量提升,系统内部通信效率提高

    为什么需要消息队列

    • 【生产和消费】的速度或稳定性等因素不一致

    消息队列的好处

    • 业务解耦
    • 最终一致性
    • 广播
    • 错峰和流控

    举例

    • Kafka
    • RabbitMQ

    应用拆分

    拆分原则

    • 业务优先
    • 循序渐进
    • 兼顾技术:重构、分层
    • 可靠测试

    需要考虑的问题

    • 应用之间的通信:RPC(dubbo等)、消息队列
    • 应用之间数据数据库设计:每个应用都有独立的数据库
    • 避免事务操作跨应用

    通信工具

    • dubbo
    • springCloud

    应用限流

    常用限流算法

    • 计数器法

    • 滑动窗口法

    • 漏桶算法

    • 令牌桶算法(Guava RateLimit)

      @Slf4j
      public class RateLimiterExample1 {
      
          private static RateLimiter rateLimiter = RateLimiter.create(5);
      
          public static void main(String[] args) throws Exception {
      
              for (int index = 0; index < 100; index++) {
                  if (rateLimiter.tryAcquire(190, TimeUnit.MILLISECONDS)) {
                      handle(index);
                  }
              }
          }
      
          private static void handle(int i) {
             log.info("{}", i);
          }
      }
      
      @Slf4j
      public class RateLimiterExample2 {
      
          private static RateLimiter rateLimiter = RateLimiter.create(5);
      
          public static void main(String[] args) throws Exception {
      
              for (int index = 0; index < 100; index++) {
                  rateLimiter.acquire();
                  handle(index);
              }
          }
      
          private static void handle(int i) {
             log.info("{}", i);
          }
      }
      

      Guava RateLimiter是单机版的限流

      如果是分布式可以采用 分布式限流:

      可以采用Redis作为中间组件,使用Redis的incrby key num 方法

    服务降级与服务熔断

    服务降级

    • 自动降级:超时、失败次数、故障、限流
    • 人工降级:秒杀、双11大促

    服务熔断

    总结

    共性:目的、最终表现、粒度、自治

    区别:

    • 触发原因、管理目标层次、实现方式

    服务降级要考虑的问题

    • 核心服务、非核心服务
    • 是否支持降级、降级策略
    • 业务放通场景,策略

    Hystrix

    • 在通过第三方客户端访问(通常是通过网络)依赖服务出现高延迟或者失败时,为系统提供保护和控制。
    • 在分布式系统中防止级联失败
    • 快速失败(fail fast)同时能快速恢复
    • 提供失败回退(Fallback)和优雅的服务降级机制
    • 提供近实时的监控、报警和运维控制手段

    数据库切库、分库、分表

    数据库瓶颈

    • 单个库数据量太大(1T~2T):多个库
    • 单个数据库服务器压力过大、读写瓶颈:多个库
    • 单个表数据量过大:分表

    数据库切库

    • 切库的基础及实际运用:读写分离

    • 自定义注解完成数据库切库-代码实现

    • 支持多数据源、分库

    • 数据库支持多个数据源-代码实现

    数据库分表

    • 什么时候考虑分表
    • 横向(水平)分表和纵向(垂直)分表
    • 数据库分表:mybatis分表插件shardbatis2.0

    高可用的一些手段

    • 任务调度系统分布式:elastic-job+zookeeper
    • 主备切换:apache curator + zookeeper分布式锁实现
    • 监控报警机制
  • 相关阅读:
    设备坐标与逻辑坐标
    4个设备上下文DC
    VC6.0智能提示消失恢复
    VC
    JavaWeb_设置Cookie的作用路径
    JavaWeb_Cookie显示最近浏览的商品
    JavaWeb_Cookie
    MVC案例——通过配置切换底层存储源
    MVC案例——修改用户
    MVC案例——删除操作
  • 原文地址:https://www.cnblogs.com/xujie09/p/11710675.html
Copyright © 2020-2023  润新知