简述高并发解决思路-如何处理海量数据(中)

1、高并发之扩容思路

  • 垂直扩容(纵向扩展):提高系统部件能力。
    增加内存
  • 水平扩容(横向扩展):增加更多系统成员来实现。
    增加服务器,应关注对共享资源的访问,是否有足够的缓冲区以承载起负荷。

数据库扩容

  • 读操作扩展:memcache、redis、CDN等缓存
  • 写操作扩展:Cassandra、Hbase等

2、高并发之缓存
缓存特征:

  • 命中率:命中数/(命中数+没有命中数)
  • 最大元素(空间)
  • 清空策略:FIFO、LFU(最少使用策略)、LRU(最近最少使用策略)、过期时间、随机等。

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

Guava Cache

参考资料: [Google Guava] 3-缓存

Memcache

Memcached 深入分析及内存调优

Redis

特点:

  • 数据的持久性。
  • 数据备份。
  • 丰富的数据类型。

高并发场景下缓存常见问题: 原文出处:缓存在高并发场景下的常见问题

  • 缓存一致性
    当数据时效性要求很高时,需要保证缓存中的数据与数据库中的保持一致,而且需要保证缓存节点和副本中的数据也保持一致,不能出现差异现象。这就比较依赖缓存的过期和更新策略。一般会在数据发生更改的时,主动更新缓存中的数据或者移除对应的缓存。
  • 缓存并发问题
    缓存过期后将尝试从后端数据库获取数据,这是一个看似合理的流程。但是,在高并发场景下,有可能多个请求并发的去从数据库获取数据,对后端数据库造成极大的冲击,甚至导致“雪崩”现象。此外,当某个缓存key在被更新时,同时也可能被大量请求在获取,这也会导致一致性的问题。那如何避免类似问题呢?我们会想到类似“锁”的机制,在缓存更新或者过期的情况下,先尝试获取到锁,当更新或者从数据库获取完成后再释放锁,其他的请求只需要牺牲一定的等待时间,即可直接从缓存中继续获取数据。 redis分布式锁
  • 缓存穿透问题
    缓存穿透在有些地方也称为“击穿”。很多朋友对缓存穿透的理解是:由于缓存故障或者缓存过期导致大量请求穿透到后端数据库服务器,从而对数据库造成巨大冲击。

这其实是一种误解。真正的缓存穿透应该是这样的:

在高并发场景下,如果某一个key被高并发访问,没有被命中,出于对容错性考虑,会尝试去从后端数据库中获取,从而导致了大量请求达到数据库,而当该key对应的数据本身就是空的情况下,这就导致数据库中并发的去执行了很多不必要的查询操作,从而导致巨大冲击和压力。

可以通过下面的几种常用方式来避免缓存传统问题:

1)缓存空对象 对查询结果为空的对象也进行缓存,如果是集合,可以缓存一个空的集合(非null),如果是缓存单个对象,可以通过字段标识来区分。这样避免请求穿透到后端数据库。同时,也需要保证缓存数据的时效性。这种方式实现起来成本较低,比较适合命中不高,但可能被频繁更新的数据。

2)单独过滤处理 对所有可能对应数据为空的key进行统一的存放,并在请求前做拦截,这样避免请求穿透到后端数据库。这种方式实现起来相对复杂,比较适合命中不高,但是更新不频繁的数据。

缓存的颠簸问题


有些地方可能被成为“缓存抖动”,可以看做是一种比“雪崩”更轻微的故障,但是也会在一段时间内对系统造成冲击和性能影响。一般是由于缓存节点故障导致。业内推荐的做法是通过一致性Hash算法来解决。

  • 缓存的雪崩现象

缓存雪崩就是指由于缓存的原因,导致大量请求到达后端数据库,从而导致数据库崩溃,整个系统崩溃,发生灾难。导致这种现象的原因有很多种,上面提到的“缓存并发”,“缓存穿透”,“缓存颠簸”等问题,其实都可能会导致缓存雪崩现象发生。这些问题也可能会被恶意攻击者所利用。还有一种情况,例如某个时间点内,系统预加载的缓存周期性集中失效了,也可能会导致雪崩。为了避免这种周期性失效,可以通过设置不同的过期时间,来错开缓存过期,从而避免缓存集中失效。

从应用架构角度,我们可以通过限流、降级、熔断等手段来降低影响,也可以通过多级缓存来避免这种灾难。

此外,从整个研发体系流程的角度,应该加强压力测试,尽量模拟真实场景,尽早的暴露问题从而防范。

缓存无底洞现象
该问题由 facebook 的工作人员提出的, facebook 在 2010 年左右,memcached 节点就已经达3000 个,缓存数千 G 内容。

他们发现了一个问题---memcached 连接频率,效率下降了,于是加 memcached 节点,

添加了后,发现因为连接频率导致的问题,仍然存在,并没有好转,称之为”无底洞现象”。

目前主流的数据库、缓存、Nosql、搜索中间件等技术栈中,都支持“分片”技术,来满足“高性能、高并发、高可用、可扩展”等要求。有些是在client端通过Hash取模(或一致性Hash)将值映射到不同的实例上,有些是在client端通过范围取值的方式映射的。当然,也有些是在服务端进行的。但是,每一次操作都可能需要和不同节点进行网络通信来完成,实例节点越多,则开销会越大,对性能影响就越大。

主要可以从如下几个方面避免和优化:

数据分布方式 有些业务数据可能适合Hash分布,而有些业务适合采用范围分布,这样能够从一定程度避免网络IO的开销。

IO优化 可以充分利用连接池,NIO等技术来尽可能降低连接开销,增强并发连接能力。

数据访问方式 一次性获取大的数据集,会比分多次去获取小数据集的网络IO开销更小。

3、高并发之消息队列
特性

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

优点

4、应用拆分
基本原则:

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

思考:

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

参考:
Dubbo之入门例子HelloWorld

参考:
解析微服务架构(一):什么是微服务

一个复杂系统的拆分改造实践

5、应用限流
限制某段代码执行速率
常用算法:

  • 计数器法

存在漏洞,如何解决恶意用户在0:59-1:00前后各发送100请求,压垮应用。

  • 滑动窗口

  • 漏桶算法

  • 令牌桶算法

参考: 聊聊高并发系统之限流特技一

6、服务降级与熔断
服务降级分类:

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

降级与熔断

  • 区别:触发原因、管理目标层次、实现方式
  • 共性:目的、最终表现、粒度、自治

Hystrix

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

参考:
谈谈我对服务熔断、服务降级的理解
使用 Hystrix 实现自动降级与依赖隔离

7、高并发数据库切库分库分表
解决数据库的瓶颈。
数据库切库:

  • 实际应用:读写分离
  • 自定义注解实现数据库切库

数据支持多个数据源与分库:

  • 支持多个数据源分库

数据库分表:

  • 横向(水平)分表
  • 纵向(垂直)分表
  • 数据库分表:mybatis分表插件shardbatis2.0

8、高可用的一些手段

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

猜你喜欢

转载自juejin.im/post/5b0b5794f265da0daf3b2f75