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

扩容

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

缓存

缓存特征

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

一、影响因素

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

二、缓存分类和应用场景

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

三、常用组件

  • Guava Cache

    1. public class GuavaCacheExample1 {
    2. public static void main(String[] args) {
    3. LoadingCache<String, Integer> cache = CacheBuilder.newBuilder()
    4. .maximumSize(10) // 最多存放10个数据
    5. .expireAfterWrite(10, TimeUnit.SECONDS) // 缓存10秒
    6. .recordStats() // 开启记录状态数据功能
    7. .build(new CacheLoader<String, Integer>() {
    8. @Override
    9. public Integer load(String key) throws Exception {
    10. return -1;
    11. }
    12. });
    13. log.info("{}", cache.getIfPresent("key1")); // null
    14. cache.put("key1", 1);
    15. log.info("{}", cache.getIfPresent("key1")); // 1
    16. cache.invalidate("key1");
    17. log.info("{}", cache.getIfPresent("key1")); // null
    18. try {
    19. log.info("{}", cache.get("key2")); // -1
    20. cache.put("key2", 2);
    21. log.info("{}", cache.get("key2")); // 2
    22. log.info("{}", cache.size()); // 1
    23. for (int i = 3; i < 13; i++) {
    24. cache.put("key" + i, i);
    25. }
    26. log.info("{}", cache.size()); // 10
    27. log.info("{}", cache.getIfPresent("key2")); // null
    28. Thread.sleep(11000);
    29. log.info("{}", cache.get("key5")); // -1
    30. log.info("{},{}", cache.stats().hitCount(), cache.stats().missCount());
    31. log.info("{},{}", cache.stats().hitRate(), cache.stats().missRate());
    32. } catch (Exception e) {
    33. log.error("cache exception", e);
    34. }
    35. }
    36. }
  • Memcache

  • Redis

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

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

FIFO: 先进先出策略

LFO:最少使用策略

LRU:最近最少使用策略

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

缓存一致性

造成这种问题的场景:

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

缓存并发问题

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

缓存穿透问题

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

解决办法:

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

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

缓存雪崩现象

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

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

消息队列

特性

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

为什么需要消息队列

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

消息队列的好处

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

举例

  • Kafka
  • RabbitMQ

应用拆分

拆分原则

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

需要考虑的问题

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

通信工具

  • dubbo
  • springCloud

应用限流

常用限流算法

  • 计数器法

  • 滑动窗口法

  • 漏桶算法

  • 令牌桶算法(Guava RateLimit)

    1. @Slf4j
    2. public class RateLimiterExample1 {
    3. private static RateLimiter rateLimiter = RateLimiter.create(5);
    4. public static void main(String[] args) throws Exception {
    5. for (int index = 0; index < 100; index++) {
    6. if (rateLimiter.tryAcquire(190, TimeUnit.MILLISECONDS)) {
    7. handle(index);
    8. }
    9. }
    10. }
    11. private static void handle(int i) {
    12. log.info("{}", i);
    13. }
    14. }
    1. @Slf4j
    2. public class RateLimiterExample2 {
    3. private static RateLimiter rateLimiter = RateLimiter.create(5);
    4. public static void main(String[] args) throws Exception {
    5. for (int index = 0; index < 100; index++) {
    6. rateLimiter.acquire();
    7. handle(index);
    8. }
    9. }
    10. private static void handle(int i) {
    11. log.info("{}", i);
    12. }
    13. }

    Guava RateLimiter是单机版的限流

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

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

服务降级与服务熔断

服务降级

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

服务熔断

总结

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

区别:

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

服务降级要考虑的问题

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

Hystrix

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

数据库切库、分库、分表

数据库瓶颈

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

数据库切库

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

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

  • 支持多数据源、分库

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

数据库分表

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

高可用的一些手段

  • 任务调度系统分布式:elastic-job+zookeeper
  • 主备切换:apache curator + zookeeper分布式锁实现
  • 监控报警机制

java并发编程笔记(十一)——高并发处理思路和手段的更多相关文章

  1. java并发编程笔记(三)——线程安全性

    java并发编程笔记(三)--线程安全性 线程安全性: ​ 当多个线程访问某个类时,不管运行时环境采用何种调度方式或者这些进程将如何交替执行,并且在主调代码中不需要任何额外的同步或协同,这个类都能表现 ...

  2. java并发编程笔记(六)——AQS

    java并发编程笔记(六)--AQS 使用了Node实现FIFO(first in first out)队列,可以用于构建锁或者其他同步装置的基础框架 利用了一个int类型表示状态 使用方法是继承 子 ...

  3. java并发编程笔记(五)——线程安全策略

    java并发编程笔记(五)--线程安全策略 不可变得对象 不可变对象需要满足的条件 对象创建以后其状态就不能修改 对象所有的域都是final类型 对象是正确创建的(在对象创建期间,this引用没有逸出 ...

  4. java并发编程笔记(一)——并发编程简介

    java并发编程笔记(一)--简介 线程不安全的类示例 public class CountExample1 { // 请求总数 public static int clientTotal = 500 ...

  5. java并发编程笔记(十)——HashMap与ConcurrentHashMap

    java并发编程笔记(十)--HashMap与ConcurrentHashMap HashMap参数 有两个参数影响他的性能 初始容量(默认为16) 加载因子(默认是0.75) HashMap寻址方式 ...

  6. java并发编程笔记(九)——多线程并发最佳实践

    java并发编程笔记(九)--多线程并发最佳实践 使用本地变量 使用不可变类 最小化锁的作用域范围 使用线程池Executor,而不是直接new Thread执行 宁可使用同步也不要使用线程的wait ...

  7. java并发编程笔记(八)——死锁

    java并发编程笔记(八)--死锁 死锁发生的必要条件 互斥条件 进程对分配到的资源进行排他性的使用,即在一段时间内只能由一个进程使用,如果有其他进程在请求,只能等待. 请求和保持条件 进程已经保持了 ...

  8. java并发编程笔记(七)——线程池

    java并发编程笔记(七)--线程池 new Thread弊端 每次new Thread新建对象,性能差 线程缺乏统一管理,可能无限制的新建线程,相互竞争,有可能占用过多系统资源导致死机或者OOM 缺 ...

  9. java并发编程笔记(四)——安全发布对象

    java并发编程笔记(四)--安全发布对象 发布对象 使一个对象能够被当前范围之外的代码所使用 对象逸出 一种错误的发布.当一个对象还没构造完成时,就使它被其他线程所见 不安全的发布对象 某一个类的构 ...

随机推荐

  1. 关于 pip disreubution setuptools(unable to locate package pip)

    总是报找不到setuptools模块的错误 很是郁闷 安装pip的前提条件是要安装setuptools 或distribute. 安装distribute的方法: $ curl http://pyth ...

  2. ELMO,BERT和GPT简介

    1.Contextualized Word Embedding 同样的单词有不同的意思,比如下面的几个句子,同样有 “bank” ,却有着不同的意思.但是用训练出来的 Word2Vec 得到 “ban ...

  3. python学习那点事---pycharm使用弹框问题如何解决

    学习python的目标:年后可以找一份不错的维护工作. 2019.11.4日是第一天开始学习python,从开始安装python3.6版本和pycharm开始.安装python版本非常顺利的就完成了, ...

  4. ELK日志分析系统之elasticsearch7.x最新版安装与配置

    1.Elasticsearch 1.1.elasticsearch的简介 ElasticSearch是一个基于Lucene的搜索服务器.它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful ...

  5. ash: export: `/usr/lib/jvm/jdk1.8.0_201/lib/dt.jar': 不是有效的标识符

    ash: export: `/usr/lib/jvm/jdk1.8.0_201/lib/dt.jar': 不是有效的标识符 ps: 如果有任何问题可以评论留言,我看到后会及时解答,评论或关注,您的鼓励 ...

  6. [已解决]报错: Error response from daemon: conflict

    报错内容: Error response from daemon: conflict: unable to delete f5b6ef70d79b (must be forced) - image i ...

  7. Cannot modify header information - headers already sent by出错的原因

    <?php ob_start(); setcookie("username","送家",time()+3600); echo "the user ...

  8. go 协程(Goroutine)

    Go 协程是什么? Go 协程是与其他函数或方法一起并发运行的函数或方法.Go 协程可以看作是轻量级线程.与线程相比,创建一个 Go 协程的成本很小.因此在 Go 应用中,常常会看到有数以千计的 Go ...

  9. 66.Subarray Sum Equals K(子数组和为K的个数)

    Level:   Medium 题目描述: Given an array of integers and an integer k, you need to find the total number ...

  10. activiti 5.22 表结构解析及清空流程运行测试数据

    1.结构设计 1.1.    逻辑结构设计 Activiti使用到的表都是ACT_开头的. ACT_RE_*: 'RE'表示repository(存储),RepositoryService接口所操作的 ...