高并发_高并发处理 - CSDN
精华内容
参与话题
  • 什么是高并发

    千次阅读 2019-08-05 20:52:15
    什么是高并发高并发(High Concurrency)是一种系统运行过程中遇到的一种“短时间内遇到大量操作请求”的情况,主要发生在web系统集中大量访问收到大量请 求(例如:12306的抢票情况;天猫双十一活动)。该情况的...

    什么是高并发?

    高并发(High Concurrency)是一种系统运行过程中遇到的一种“短时间内遇到大量操作请求”的情况,主要发生在web系统集中大量访问收到大量请

    求(例如:12306的抢票情况;天猫双十一活动)。该情况的发生会导致系统在这段时间内执行大量操作,例如对资源的请求,数据库的操作等。

    高并发的处理指标

    高并发相关常用的一些指标有:
    1.响应时间(Response Time)

    响应时间:系统对请求做出响应的时间。例如系统处理一个HTTP请求需要200ms,这个200ms就是系统的响应时间

    2.吞吐量(Throughput)

    吞吐量:单位时间内处理的请求数量。

    3.每秒查询率QPS(Query Per Second)

    QPS:每秒响应请求数。在互联网领域,这个指标和吞吐量区分的没有这么明显。

    4.并发用户数

    并发用户数:同时承载正常使用系统功能的用户数量。例如一个即时通讯系统,同时在线量一定程度上代表了系统的并发用户数。

    高并发和多线程的关系和区别?

    “高并发和多线程”总是被一起提起,给人感觉两者好像相等,实则 高并发 ≠ 多线程

    1.多线程

    多线程是java的特性,因为现在cpu都是多核多线程的,可以同时执行几个任务,为了提高jvm的执行效率,java提供了这种多线程的机制,以增强数据

    处理效率。多线程对应的是cpu,高并发对应的是访问请求,可以用单线程处理所有访问请求,也可以用多线程同时处理访问请求。

    在过去单CPU时代,单任务在一个时间点只能执行单一程序。之后发展到多任务阶段,计算机能在同一时间点并行执行多任务或多进程。虽然并不是真

    正意义上的“同一时间点”,而是多个任务或进程共享一个CPU,并交由操作系统来完成多任务间对CPU的运行切换,以使得每个任务都有机会获得一

    定的时间片运行。

    再后来发展到多线程技术,使得在一个程序内部能拥有多个线程并行执行。一个线程的执行可以被认为是一个CPU在执行该程序。当一个程序运行在多

    线程下,就好像有多个CPU在同时执行该程序。

    总之,多线程即可以这么理解:多线程是处理高并发的一种编程方法,即并发需要用多线程实现。

    2.高并发

    高并发不是JAVA的专有的东西,是语言无关的广义的,为提供更好互联网服务而提出的概念。

    典型的场景,例如:12306抢火车票,天猫双十一秒杀活动等。该情况的发生会导致系统在这段时间内执行大量操作,例如对资源的请求,数据库的操

    作等。如果高并发处理不好,不仅仅降低了用户的体验度(请求响应时间过长),同时可能导致系统宕机,严重的甚至导致OOM异常,系统停止工作等

    如果要想系统能够适应高并发状态,则需要从各个方面进行系统优化,包括,硬件、网络、系统架构、开发语言的选取、数据结构的运用、算法优化、

    数据库优化等……而多线程只是其中解决方法之一。

    展开全文
  • 高并发解决方案】高并发解决方案汇总

    万次阅读 多人点赞 2018-05-18 17:18:09
    什么是秒杀秒杀场景一般会在电商网站举行一些活动或者节假日在12306网站上抢票时遇到。对于电商网站中一些稀缺或者特价商品,电商网站一般会在约定时间点对其进行限量销售,因为这些商品的特殊性,会吸引大量用户...

    【高并发解决方案】1、高并发解决方案汇总

    一、对于被频繁调用,更新频率较低的页面,可以采用HTML静态化技术

    二、图片服务器分离

    三、数据库集群和库表散列

          mysql主从。m-m-s-s-s...(2个主,多个从。多个从使用负载均衡。主写入数据,从读取数据)

    四、缓存。众多的缓存框架

    五、负载均衡。nginx,lvs,F5

    六、搜索用单独的服务器,搜索框架

    七、使用MQ服务器

    【高并发解决方案】2、集群概述

    1.什么是集群

    集群是一组协同工作的服务实体,用以提供比单一服务实体更具扩展性与可用性的服务平台。在客户端看来,一个集群就象是一个服务实体,但 事实上集群由一组服务实体组成。

    2.集群的特性 
    与单一服务实体相比较,集群提供了以下两个关键特性: 
    1.可扩展性--集群的性能不限于单一的服务实体,新的服 务实体可以动态地加入到集群,从而增强集群的性能。 
    2. 高可用性--集群通过服务实体冗余使客户端免于轻易遇到out of service的警告。在集群中,同样的服务可以由多个服务实体提供。如果一个服务实体失败了,另一个服务实体会接管失败的服务实体。集群提供的从一个出 错的服务实体恢复到另一个服务实体的功能增强了应用的可用性。 
    为了具有可扩展性和高可用性特点,集群的必须具备以下两大能力: 
    (1) 负 载均衡--负载均衡能把任务比较均衡地分布到集群环境下的计算和网络资源。 
    (2) 错误恢复--由于某种原因,执行某个任务的资源出现故障,另一服 务实体中执行同一任务的资源接着完成任务。这种由于一个实体中的资源不能工作,另一个实体中的资源透明的继续完成任务的过程叫错误恢复。 
    负载均衡 和错误恢复都要求各服务实体中有执行同一任务的资源存在,而且对于同一任务的各个资源来说,执行任务所需的信息视图(信息上下文)必须是一样的。

     

    3 集群的分类 
    集群主要分成三大类:高可用集群(High Availability Cluster/HA), 负载均衡集群(Load Balance Cluster),高性能计算集群(High Performance Computing Cluster/HPC) 
    (1) 高可用集群(High Availability Cluster/HA):一般是指当集群中有某个节点失效的情况下,其上的任务会自动转移到其他正常的节点上。还指可以将集群中的某节点进行离线维护再上线,该过程并不影响整个集群的运行。常见的就是2个节点做 成的HA集群,有很多通俗的不科学的名称,比如"双机热备", "双机互备", "双机",高可用集群解决的是保障用户的应用程序持续对外提供服 务的能力。 
    (2) 负载均衡集群(Load Balance Cluster):负载均衡集群运行时一般通过一个或者多个前端负载均衡器将工作负载分发到后端的一组服务器上,从而达到将工作负载分发。这样的计算机集群有时也被称为服务器群(Server Farm)。一般web服务器集群、数据库集群 和应用服务器集群都属于这种类型。这种集群可以在接到请求时,检查接受请求较少,不繁忙的服务器,并把请求转到这些服务器 上。从检查其他服务器状态这一点上 看,负载均衡和容错集群很接近,不同之处是数量上更多。 
    (3) 高性能计算集群(High Performance Computing Cluster/HPC):高性能计算集群采用将计算任务分配到集群的不同计算节点而提高计算能力,因而主要应用在科学计算领域。这类集群致力于提供单个计算机所不能提供的强大的计算能力

    【高并发解决方案】3、消息队列软件产品大比拼

    转自:http://www.oschina.net/news/17973/message-queue-shootout

    我花了一周的时间评估比较了一下各种消息队列产品,非常的有趣。我做这个事的动机是因为一个客户有一个很高性能需求。他们的消息信息突破了1百万个并发。目前他们使用的是SQL server,并不理想,我建议他们使用消息队列服务器。

    为了对一些相似的候选产品获得一个全面的但是粗浅的性能上的了解,我们它们放在一起做了个测试。我让每个消息产品各发送和接受1百万千条1K的消 息。测试准备的有些仓促,我并没有修改任何的配置,只是快速的看了一下它们的安装文档,安装好每种软件,然后就让它们做这些最简单的收发信息的操作。所以 这是一次真正的“开箱即装即用”的性能表现。我完全理解,这对那些初始配置十分保守的消息队列产品将会是个惩罚。

    候选产品有:

    • MSMQ.
      这是微软的产品里唯一被认为有价值的东西。对我的客户来说,如果MSMQ能证明可以应对这种任务,他们将选择使用它。关键是这个东西并不复杂,除了接收和 发送,没有别的;它有一些硬性限制,比如最大消息体积是4MB。然而,通过和一些像MassTransit 或 NServiceBus这样的软件的连接,它完全可以解决这些问题。
    • ActiveMQ.
      Java世界的中坚力量。它有很长的历史,而且被广泛的使用。它还是跨平台的,给那些非微软平台的产品提供了一个天然的集成接入点。然而,它只有跑过了MSMQ才有可能被考虑。
    • RabbitMQ.
      我听说了很多关于这个用Erlang写成的消息中间件的优秀的特性。它支持开放的高级消息队列协议 (AMQP,Advanced Message Queuing Protocol),从根本上避免了生产厂商的封闭,使用任何语言的各种客户都可以从中受益。这种协议提供了相当复杂的消息传输模式,所以基本上不需要 MassTransit 或 NServiceBus 的配合。它还具有“企业级”的适应性和稳定性。这些东西对我的客户来说十分的有吸引力。
    • ZeroMQ.
      我在研究AMQP时从发现了这个产品。开发这个产品的公司是AMQP集团的一部分,并且还有一个叫做OpenAMQ的产品。然而,他们却戏剧性的从AMQP分离的出去,并抱怨说这这个产品迷失了方向、变的越来越复杂。你可以到这里阅 读Dear John的关于此事的文章。ZeroMQ具有一个独特的非中间件的模式,也就是说,跟其它几个接受测试的产品不同,你不需要安装和运行一个消息服务器,或 中间件。你只需要简单的引用ZeroMQ程序库,可以使用NuGet安装,然后你就可以愉快的在应用程序之间发送消息了。非常有趣的是,他们也同样使用这 方式在任何利用ZeroMQ进行强大的进程内通信的语言里创建Erlang风格的这种执行角色。

    把这四个MQ产品装上、跑起来是一个很有趣的工作。当你需要安装一个非Windows平台的产品时,下一定的功夫那是必须的。ActiveMQ需要 在目标机器上安装Java,RabbitMQ需要Erlang环境。安装这两个产品都没有遇到麻烦,但我想这是否给系统的维护增加了一层任务。如果这个中 的一个被选中,我需要让系统维护的人去理解和维护他们以前不熟悉的运行库。ActiveMQ,
    RabbitMQ 和 MSMQ 都需要启动服务进程,这些都可以监控和配置,另外一个就有问题了。

    ZeroMQ,它没有中间件架构,不需要任何服务进程和运行时。事实上,你的应用程序端点扮演了这个服务角色。这让部署起来非常简单,但担心的是, 你没有地方可以观察它是否有问题出现。就目前我知道的,ZeroMQ仅提供非持久性的队列。你可以在需要的地方实现自己的审计和数据恢复功能。老实说,我 甚至不确信是否该把它列在此次测试中,它的运行原理和其它几种差别太大了。

    我就不瞎扯了,下面是测试结果。显示的是发送和接受的每秒钟的消息数。整个过程共产生1百万条1K的消息。测试的执行是在一个Windows Vista上进行的。

    Message Queue

    就像你看到的,ZeroMQ和其它的不是一个级别。它的性能惊人的高。公平的说,ZeroMQ跟其它几个比起来像头巨兽,尽管这样,结论很清楚:如果你希望一个应用程序发送消息越快越好,你选择ZeroMQ。当你不太在意偶然会丢失某些消息的情况下更有价值。

    老实讲,我更希望使用Rabbit。但这种事情是应该做更多的测试,你最终会有一个最爱,我所听到的、读到的各种关于Rabbit的事情让我觉得它应该是最佳选择。但使用这个测试结果,我很难说服他们不去使用MSMQ。

    如果你想自己跑一下这些测试,我的测试代码都放在了GitHub上。我很感兴趣(但不是非常非常感兴趣)想知道如何优化这些测试,所以,如果你能做到一个更好的测试结果,请告诉我。谢谢。


    【高并发解决方案】如何设计一个秒杀系统

    什么是秒杀

    秒杀场景一般会在电商网站举行一些活动或者节假日在12306网站上抢票时遇到。对于电商网站中一些稀缺或者特价商品,电商网站一般会在约定时间点对其进行限量销售,因为这些商品的特殊性,会吸引大量用户前来抢购,并且会在约定的时间点同时在秒杀页面进行抢购。

    秒杀系统场景特点

    • 秒杀时大量用户会在同一时间同时进行抢购,网站瞬时访问流量激增。
    • 秒杀一般是访问请求数量远远大于库存数量,只有少部分用户能够秒杀成功。
    • 秒杀业务流程比较简单,一般就是下订单减库存。

    秒杀架构设计理念

    限流: 鉴于只有少部分用户能够秒杀成功,所以要限制大部分流量,只允许少部分流量进入服务后端。

    削峰:对于秒杀系统瞬时会有大量用户涌入,所以在抢购一开始会有很高的瞬间峰值。高峰值流量是压垮系统很重要的原因,所以如何把瞬间的高流量变成一段时间平稳的流量也是设计秒杀系统很重要的思路。实现削峰的常用的方法有利用缓存和消息中间件等技术。

    异步处理:秒杀系统是一个高并发系统,采用异步处理模式可以极大地提高系统并发量,其实异步处理就是削峰的一种实现方式。

    内存缓存:秒杀系统最大的瓶颈一般都是数据库读写,由于数据库读写属于磁盘IO,性能很低,如果能够把部分数据或业务逻辑转移到内存缓存,效率会有极大地提升。

    可拓展:当然如果我们想支持更多用户,更大的并发,最好就将系统设计成弹性可拓展的,如果流量来了,拓展机器就好了。像淘宝、京东等双十一活动时会增加大量机器应对交易高峰。

    架构方案

    一般秒杀系统架构

    这里写图片描述

    设计思路

    将请求拦截在系统上游,降低下游压力:秒杀系统特点是并发量极大,但实际秒杀成功的请求数量却很少,所以如果不在前端拦截很可能造成数据库读写锁冲突,甚至导致死锁,最终请求超时。 
    充分利用缓存:利用缓存可极大提高系统读写速度。 
    消息队列:消息队列可以削峰,将拦截大量并发请求,这也是一个异步处理过程,后台业务根据自己的处理能力,从消息队列中主动的拉取请求消息进行业务处理。

    前端方案

    浏览器端(js):

    页面静态化:将活动页面上的所有可以静态的元素全部静态化,并尽量减少动态元素。通过CDN来抗峰值。 
    禁止重复提交:用户提交之后按钮置灰,禁止重复提交 
    用户限流:在某一时间段内只允许用户提交一次请求,比如可以采取IP限流

    后端方案

    服务端控制器层(网关层)

    限制uid(UserID)访问频率:我们上面拦截了浏览器访问的请求,但针对某些恶意攻击或其它插件,在服务端控制层需要针对同一个访问uid,限制访问频率。

    服务层

    上面只拦截了一部分访问请求,当秒杀的用户量很大时,即使每个用户只有一个请求,到服务层的请求数量还是很大。比如我们有100W用户同时抢100台手机,服务层并发请求压力至少为100W。

    1. 采用消息队列缓存请求:既然服务层知道库存只有100台手机,那完全没有必要把100W个请求都传递到数据库啊,那么可以先把这些请求都写到消息队列缓存一下,数据库层订阅消息减库存,减库存成功的请求返回秒杀成功,失败的返回秒杀结束。

    2. 利用缓存应对读请求:对类似于12306等购票业务,是典型的读多写少业务,大部分请求是查询请求,所以可以利用缓存分担数据库压力。

    3. 利用缓存应对写请求:缓存也是可以应对写请求的,比如我们就可以把数据库中的库存数据转移到Redis缓存中,所有减库存操作都在Redis中进行,然后再通过后台进程把Redis中的用户秒杀请求同步到数据库中。

    数据库层

    数据库层是最脆弱的一层,一般在应用设计时在上游就需要把请求拦截掉,数据库层只承担“能力范围内”的访问请求。所以,上面通过在服务层引入队列和缓存,让最底层的数据库高枕无忧。

    案例:利用消息中间件和缓存实现简单的秒杀系统

    Redis是一个分布式缓存系统,支持多种数据结构,我们可以利用Redis轻松实现一个强大的秒杀系统。

    我们可以采用Redis 最简单的key-value数据结构,用一个原子类型的变量值(AtomicInteger)作为key,把用户id作为value,库存数量便是原子变量的最大值。对于每个用户的秒杀,我们使用 RPUSH key value插入秒杀请求, 当插入的秒杀请求数达到上限时,停止所有后续插入。

    然后我们可以在台启动多个工作线程,使用 LPOP key 读取秒杀成功者的用户id,然后再操作数据库做最终的下订单减库存操作。

    当然,上面Redis也可以替换成消息中间件如ActiveMQRabbitMQ等,也可以将缓存和消息中间件 组合起来,缓存系统负责接收记录用户请求,消息中间件负责将缓存中的请求同步到数据库。

    【高并发解决方案】6、数据库水平切分的实现原理解析

     

    第1章 引言

    随着互联网应用的广泛普及,海量数据的存储和访问成为了系统设计的瓶颈问题。对于一个大型的互联网应用,每天几十亿的PV无疑对数据库造成了相当高的负载。对于系统的稳定性和扩展性造成了极大的问题。通过数据切分来提高网站性能,横向扩展数据层已经成为架构研发人员首选的方式。

    • 水平切分数据库:可以降低单台机器的负载,同时最大限度的降低了宕机造成的损失;
    • 负载均衡策略:可以降低单台机器的访问负载,降低宕机的可能性;
    • 集群方案:解决了数据库宕机带来的单点数据库不能访问的问题;
    • 读写分离策略:最大限度了提高了应用中读取数据的速度和并发量;

    第2章 基本原理和概念

    什么是数据切分

    "Shard" 这个词英文的意思是"碎片",而作为数据库相关的技术用语,似乎最早见于大型多人在线角色扮演游戏中。"Sharding" 姑且称之为"分片"。Sharding 不是一个某个特定数据库软件附属的功能,而是在具体技术细节之上的抽象处理,是水平扩展(Scale Out,亦或横向扩展、向外扩展)的解决方案,其主要目的是为突破单节点数据库服务器的 I/O 能力限制,解决数据库扩展性问题。通过一系列的切分规则将数据水平分布到不同的DB或table中,在通过相应的DB路由或者table路由规则找到需要查询的具体的DB或者table,以进行Query操作。“sharding”通常是指“水平切分”,这也是本文讨论的重点。接下来举个简单的例子:我们针对一个Blog应用中的日志来说明,比如日志文章(article)表有如下字段:

    面对这样的一个表,我们怎样切分呢?怎样将这样的数据分布到不同的数据库中的表中去呢?我们可以这样做,将user_id为1~10000的所有的文章信息放入DB1中的article表中,将user_id为10001~20000的所有文章信息放入DB2中的 article表中,以此类推,一直到DBn。这样一来,文章数据就很自然的被分到了各个数据库中,达到了数据切分的目的。

    接下来要解决的问题就是怎样找到具体的数据库呢?其实问题也是简单明显的,既然分库的时候我们用到了区分字段user_id,那么很自然,数据库路由的过程当然还是少不了user_id的。就是我们知道了这个blog的user_id,就利用这个user_id,利用分库时候的规则,反过来定位具体的数据库。比如user_id是234,利用刚才的规则,就应该定位到DB1,假如user_id是12343,利用该才的规则,就应该定位到DB2。以此类推,利用分库的规则,反向的路由到具体的DB,这个过程我们称之为“DB路由”。

    平常我们会自觉的按照范式来设计我们的数据库,考虑到数据切分的DB设计,将违背这个通常的规矩和约束。为了切分,我们不得不在数据库的表中出现冗余字段,用作区分字段或者叫做分库的标记字段。比如上面的article的例子中的user_id这样的字段(当然,刚才的例子并没有很好的体现出user_id的冗余性,因为user_id这个字段即使就是不分库,也是要出现的,算是我们捡了便宜吧)。当然冗余字段的出现并不只是在分库的场景下才出现的,在很多大型应用中,冗余也是必须的,这个涉及到高效DB的设计,本文不再赘述。

    为什么要数据切分

    上面对什么是数据切分做了个概要的描述和解释,读者可能会疑问,为什么需要数据切分呢?像 Oracle这样成熟稳定的数据库,足以支撑海量数据的存储与查询了?为什么还需要数据切片呢?

    的确,Oracle的DB确实很成熟很稳定,但是高昂的使用费用和高端的硬件支撑不是每一个公司能支付的起的。试想一下一年几千万的使用费用和动辄上千万元的小型机作为硬件支撑,这是一般公司能支付的起的吗?即使就是能支付的起,假如有更好的方案,有更廉价且水平扩展性能更好的方案,我们为什么不选择呢?

    我们知道每台机器无论配置多么好它都有自身的物理上限,所以当我们应用已经能触及或远远超出单台机器的某个上限的时候,我们惟有寻找别的机器的帮助或者继续升级的我们的硬件,但常见的方案还是横向扩展,通过添加更多的机器来共同承担压力。我们还得考虑当我们的业务逻辑不断增长,我们的机器能不能通过线性增长就能满足需求?Sharding可以轻松的将计算,存储,I/O并行分发到多台机器上,这样可以充分利用多台机器各种处理能力,同时可以避免单点失败,提供系统的可用性,进行很好的错误隔离。

    综合以上因素,数据切分是很有必要的。 我们用免费的MySQL和廉价的Server甚至是PC做集群,达到小型机+大型商业DB的效果,减少大量的资金投入,降低运营成本,何乐而不为呢?所以,我们选择Sharding,拥抱Sharding。

    怎么做到数据切分

    数据切分可以是物理上的,对数据通过一系列的切分规则将数据分布到不同的DB服务器上,通过路由规则路由访问特定的数据库,这样一来每次访问面对的就不是单台服务器了,而是N台服务器,这样就可以降低单台机器的负载压力。

    数据切分也可以是数据库内的,对数据通过一系列的切分规则,将数据分布到一个数据库的不同表中,比如将article分为article_001,article_002等子表,若干个子表水平拼合有组成了逻辑上一个完整的article表,这样做的目的其实也是很简单的。举个例子说明,比如article表中现在有5000w条数据,此时我们需要在这个表中增加(insert)一条新的数据,insert完毕后,数据库会针对这张表重新建立索引,5000w行数据建立索引的系统开销还是不容忽视的。但是反过来,假如我们将这个表分成100 个table呢,从article_001一直到article_100,5000w行数据平均下来,每个子表里边就只有50万行数据,这时候我们向一张 只有50w行数据的table中insert数据后建立索引的时间就会呈数量级的下降,极大了提高了DB的运行时效率,提高了DB的并发量。当然分表的好处还不知这些,还有诸如写操作的锁操作等,都会带来很多显然的好处。

    综上,分库降低了单点机器的负载;分表,提高了数据操作的效率,尤其是Write操作的效率。行文至此我们依然没有涉及到如何切分的问题。接下来,我们将对切分规则进行详尽的阐述和说明。

    上文中提到,要想做到数据的水平切分,在每一个表中都要有相冗余字符作为切分依据和标记字段,通常的应用中我们选用user_id作为区分字段,基于此就有如下三种分库的方式和规则:(当然还可以有其他的方式)

    (1) 号段分区

    user_id为1~1000的对应DB1,1001~2000的对应DB2,以此类推;

    优点:可部分迁移

    缺点:数据分布不均

    (2)hash取模分区

    对user_id进行hash(或者如果user_id是数值型的话直接使用user_id 的值也可),然后用一个特定的数字,比如应用中需要将一个数据库切分成4个数据库的话,我们就用4这个数字对user_id的hash值进行取模运算,也就是user_id%4,这样的话每次运算就有四种可能:结果为1的时候对应DB1;结果为2的时候对应DB2;结果为3的时候对应DB3;结果为0的时候对应DB4。这样一来就非常均匀的将数据分配到4个DB中。

    优点:数据分布均匀

    缺点:数据迁移的时候麻烦,不能按照机器性能分摊数据

    (3)在认证库中保存数据库配置

    就是建立一个DB,这个DB单独保存user_id到DB的映射关系,每次访问数据库的时候都要先查询一次这个数据库,以得到具体的DB信息,然后才能进行我们需要的查询操作。

    优点:灵活性强,一对一关系

    缺点:每次查询之前都要多一次查询,性能大打折扣

    以上就是通常的开发中我们选择的三种方式,有些复杂的项目中可能会混合使用这三种方式。 通过上面的描述,我们对分库的规则也有了简单的认识和了解。当然还会有更好更完善的分库方式,还需要我们不断的探索和发现。

    第3章 本课题研究的基本轮廓

    分布式数据方案提供功能如下:

    (1)提供分库规则和路由规则(RouteRule简称RR);

    (2)引入集群(Group)的概念,保证数据的高可用性;

    (3)引入负载均衡策略(LoadBalancePolicy简称LB);

    (4)引入集群节点可用性探测机制,对单点机器的可用性进行定时的侦测,以保证LB策略的正确实施,以确保系统的高度稳定性;

    (5)引入读/写分离,提高数据的查询速度;

    仅仅是分库分表的数据层设计也是不够完善的,当我们采用了数据库切分方案,也就是说有N台机器组成了一个完整的DB 。如果有一台机器宕机的话,也仅仅是一个DB的N分之一的数据不能访问而已,这是我们能接受的,起码比切分之前的情况好很多了,总不至于整个DB都不能访问。

    一般的应用中,这样的机器故障导致的数据无法访问是可以接受的,假设我们的系统是一个高并发的电子商务网站呢?单节点机器宕机带来的经济损失是非常严重的。也就是说,现在我们这样的方案还是存在问题的,容错性能是经不起考验的。当然了,问题总是有解决方案的。我们引入集群的概念,在此我称之为Group,也就是每一个分库的节点我们引入多台机器,每台机器保存的数据是一样的,一般情况下这多台机器分摊负载,当出现宕机情况,负载均衡器将分配负载给这台宕机的机器。这样一来,就解决了容错性的问题。

    如上图所示,整个数据层有Group1,Group2,Group3三个集群组成,这三个集群就是数据水平切分的结果,当然这三个集群也就组成了一个包含完整数据的DB。每一个Group包括1个Master(当然Master也可以是多个)和 N个Slave,这些Master和Slave的数据是一致的。 比如Group1中的一个slave发生了宕机现象,那么还有两个slave是可以用的,这样的模型总是不会造成某部分数据不能访问的问题,除非整个 Group里的机器全部宕掉,但是考虑到这样的事情发生的概率非常小(除非是断电了,否则不易发生吧)。

    在没有引入集群以前,我们的一次查询的过程大致如下:请求数据层,并传递必要的分库区分字段 (通常情况下是user_id)。数据层根据区分字段Route到具体的DB,在这个确定的DB内进行数据操作。

    这是没有引入集群的情况,当时引入集群会 是什么样子的呢?我们的路由器上规则和策略其实只能路由到具体的Group,也就是只能路由到一个虚拟的Group,这个Group并不是某个特定的物理服务器。接下来需要做的工作就是找到具体的物理的DB服务器,以进行具体的数据操作。

    基于这个环节的需求,我们引入了负载均衡器的概念 (LB),负载均衡器的职责就是定位到一台具体的DB服务器。具体的规则如下:负载均衡器会分析当前sql的读写特性,如果是写操作或者是要求实时性很强的操作的话,直接将查询负载分到Master,如果是读操作则通过负载均衡策略分配一个Slave。

    我们的负载均衡器的主要研究方向也就是负载分发策略,通常情况下负载均衡包括随机负载均衡和加权负载均衡。随机负载均衡很好理解,就是从N个Slave中随机选取一个Slave。这样的随机负载均衡是不考虑机器性能的,它默认为每台机器的性能是一样的。假如真实的情况是这样的,这样做也是无可厚非的。假如实际情况并非如此呢?每个Slave的机器物理性能和配置不一样的情况,再使用随机的不考虑性能的负载均衡,是非常不科学的,这样一来会给机器性能差的机器带来不必要的高负载,甚至带来宕机的危险,同时高性能的数据库服务器也不能充分发挥其物理性能。基于此考虑从,我们引入了加权负载均衡,也就是在我们的系统内部通过一定的接口,可以给每台DB服务器分配一个权值,然后再运行时LB根据权值在集群中的比重,分配一定比例的负载给该DB服务器。当然这样的概念的引入,无疑增大了系统的复杂性和可维护性。有得必有失,我们也没有办法逃过的。

    有了分库,有了集群,有了负载均衡器,是不是就万事大吉了呢? 事情远没有我们想象的那么简单。虽然有了这些东西,基本上能保证我们的数据层可以承受很大的压力,但是这样的设计并不能完全规避数据库宕机的危害。假如Group1中的slave2 宕机了,那么系统的LB并不能得知,这样的话其实是很危险的,因为LB不知道,它还会以为slave2为可用状态,所以还是会给slave2分配负载。这样一来,问题就出来了,客户端很自然的就会发生数据操作失败的错误或者异常。

    这样是非常不友好的!怎样解决这样的问题呢? 我们引入集群节点的可用性探测机制 ,或者是可用性的数据推送机制。这两种机制有什么不同呢?首先说探测机制吧,顾名思义,探测即使,就是我的数据层客户端,不定时对集群中各个数据库进行可用性的尝试,实现原理就是尝试性链接,或者数据库端口的尝试性访问,都可以做到。

    那数据推送机制又是什么呢?其实这个就要放在现实的应用场景中来讨论这个问题了,一般情况下应用的DB 数据库宕机的话我相信DBA肯定是知道的,这个时候DBA手动的将数据库的当前状态通过程序的方式推送到客户端,也就是分布式数据层的应用端,这个时候在更新一个本地的DB状态的列表。并告知LB,这个数据库节点不能使用,请不要给它分配负载。一个是主动的监听机制,一个是被动的被告知的机制。两者各有所长。但是都可以达到同样的效果。这样一来刚才假设的问题就不会发生了,即使就是发生了,那么发生的概率也会降到最低。

    上面的文字中提到的Master和Slave ,我们并没有做太多深入的讲解。一个Group由1个Master和N个Slave组成。为什么这么做呢?其中Master负责写操作的负载,也就是说一切写的操作都在Master上进行,而读的操作则分摊到Slave上进行。这样一来的可以大大提高读取的效率。在一般的互联网应用中,经过一些数据调查得出结论,读/写的比例大概在 10:1左右 ,也就是说大量的数据操作是集中在读的操作,这也就是为什么我们会有多个Slave的原因。

    但是为什么要分离读和写呢?熟悉DB的研发人员都知道,写操作涉及到锁的问题,不管是行锁还是表锁还是块锁,都是比较降低系统执行效率的事情。我们这样的分离是把写操作集中在一个节点上,而读操作其其他 的N个节点上进行,从另一个方面有效的提高了读的效率,保证了系统的高可用性。

     转自:http://www.cnblogs.com/zhongxinWang/p/4262650.html

    【高并发解决方案】7、HAProxy安装和配置

    简介

    HAProxy提供高可用性、负载均衡以及基于TCP和HTTP应用的代理,支持虚拟主机,它是免费、快速并且可靠的一种解决方案。

    HAProxy特别适用于那些负载特大的web站点,这些站点通常又需要会话保持或七层处理。

    HAProxy运行在当前的硬件上,完全可以支持数以万计的并发连接。并且它的运行模式使得它可以很简单安全的整合进您当前的架构中, 同时可以保护你的web服务器不被暴露到网络上。

    HAProxy实现了一种事件驱动, 单一进程模型,此模型支持非常大的并发连接数。多进程或多线程模型受内存限制 、系统调度器限制以及无处不在的锁限制,很少能处理数千并发连接。事件驱动模型因为在有更好的资源和时间管理的用户空间(User-Space) 实现所有这

    些任务,所以没有这些问题。此模型的弊端是,在多核系统上,这些程序通常扩展性较差。这就是为什么他们必须进行优化以 使每个CPU时间片(Cycle)做更多的工作。

    安装

    复制代码
    #下载
    wget http://fossies.org/linux/misc/haproxy-1.6.9.tar.gz
    #解压
    tar -zxvf haproxy-1.6.9.tar.gz
    cd haproxy-1.6.9
    #安装
    make TARGET=linux2628 ARCH=x86_64 PREFIX=/usr/local/haproxy
    make install PREFIX=/usr/local/haproxy
    
    #参数说明
    TARGET=linux26 #内核版本,使用uname -r查看内核,如:2.6.18-371.el5,此时该参数就为linux26;kernel 大于2.6.28的用:TARGET=linux2628
    ARCH=x86_64 #系统位数
    PREFIX=/usr/local/haprpxy #/usr/local/haprpxy为haprpxy安装路径
    复制代码

    配置

    复制代码
    ###########全局配置#########
    global
      log 127.0.0.1 local0 #[日志输出配置,所有日志都记录在本机,通过local0输出]
      log 127.0.0.1 local1 notice #定义haproxy 日志级别[error warringinfo debug]
      daemon #以后台形式运行harpoxy
      nbproc 1 #设置进程数量
      maxconn 4096 #默认最大连接数,需考虑ulimit-n限制
      #user haproxy #运行haproxy的用户
      #group haproxy #运行haproxy的用户所在的组
      #pidfile /var/run/haproxy.pid #haproxy 进程PID文件
      #ulimit-n 819200 #ulimit 的数量限制
      #chroot /usr/share/haproxy #chroot运行路径
      #debug #haproxy 调试级别,建议只在开启单进程的时候调试
      #quiet
    
    ########默认配置############
    defaults
      log global
      mode http #默认的模式mode { tcp|http|health },tcp是4层,http是7层,health只会返回OK
      option httplog #日志类别,采用httplog
      option dontlognull #不记录健康检查日志信息
      retries 2 #两次连接失败就认为是服务器不可用,也可以通过后面设置
      #option forwardfor #如果后端服务器需要获得客户端真实ip需要配置的参数,可以从Http Header中获得客户端ip
      option httpclose #每次请求完毕后主动关闭http通道,haproxy不支持keep-alive,只能模拟这种模式的实现
      #option redispatch #当serverId对应的服务器挂掉后,强制定向到其他健康的服务器,以后将不支持
      option abortonclose #当服务器负载很高的时候,自动结束掉当前队列处理比较久的链接
      maxconn 4096 #默认的最大连接数
      timeout connect 5000ms #连接超时
      timeout client 30000ms #客户端超时
      timeout server 30000ms #服务器超时
      #timeout check 2000 #心跳检测超时
      #timeout http-keep-alive10s #默认持久连接超时时间
      #timeout http-request 10s #默认http请求超时时间
      #timeout queue 1m #默认队列超时时间
      balance roundrobin #设置默认负载均衡方式,轮询方式
      #balance source #设置默认负载均衡方式,类似于nginx的ip_hash
      #balnace leastconn #设置默认负载均衡方式,最小连接数
    
    ########统计页面配置########
    listen stats
      bind 0.0.0.0:1080 #设置Frontend和Backend的组合体,监控组的名称,按需要自定义名称
      mode http #http的7层模式
      option httplog #采用http日志格式
      #log 127.0.0.1 local0 err #错误日志记录
      maxconn 10 #默认的最大连接数
      stats refresh 30s #统计页面自动刷新时间
      stats uri /stats #统计页面url
      stats realm XingCloud\ Haproxy #统计页面密码框上提示文本
      stats auth admin:admin #设置监控页面的用户和密码:admin,可以设置多个用户名
      stats auth Frank:Frank #设置监控页面的用户和密码:Frank
      stats hide-version #隐藏统计页面上HAProxy的版本信息
      stats admin if TRUE #设置手工启动/禁用,后端服务器(haproxy-1.4.9以后版本)
    
    ########设置haproxy 错误页面#####
    #errorfile 403 /home/haproxy/haproxy/errorfiles/403.http
    #errorfile 500 /home/haproxy/haproxy/errorfiles/500.http
    #errorfile 502 /home/haproxy/haproxy/errorfiles/502.http
    #errorfile 503 /home/haproxy/haproxy/errorfiles/503.http
    #errorfile 504 /home/haproxy/haproxy/errorfiles/504.http
    
    ########frontend前端配置##############
    frontend main
      bind *:80 #这里建议使用bind *:80的方式,要不然做集群高可用的时候有问题,vip切换到其他机器就不能访问了。
      acl web hdr(host) -i www.abc.com  #acl后面是规则名称,-i为忽略大小写,后面跟的是要访问的域名,如果访问www.abc.com这个域名,就触发web规则,。
      acl img hdr(host) -i img.abc.com  #如果访问img.abc.com这个域名,就触发img规则。
      use_backend webserver if web   #如果上面定义的web规则被触发,即访问www.abc.com,就将请求分发到webserver这个作用域。
      use_backend imgserver if img   #如果上面定义的img规则被触发,即访问img.abc.com,就将请求分发到imgserver这个作用域。
      default_backend dynamic #不满足则响应backend的默认页面
    
    ########backend后端配置##############
    backend webserver #webserver作用域
      mode http
      balance roundrobin #balance roundrobin 负载轮询,balance source 保存session值,支持static-rr,leastconn,first,uri等参数
      option httpchk /index.html HTTP/1.0 #健康检查, 检测文件,如果分发到后台index.html访问不到就不再分发给它
      server web1 10.16.0.9:8085 cookie 1 weight 5 check inter 2000 rise 2 fall 3
      server web2 10.16.0.10:8085 cookie 2 weight 3 check inter 2000 rise 2 fall 3
      #cookie 1表示serverid为1,check inter 1500 是检测心跳频率 
      #rise 2是2次正确认为服务器可用,fall 3是3次失败认为服务器不可用,weight代表权重
    
    backend imgserver
      mode http
      option httpchk /index.php
      balance roundrobin 
      server img01 192.168.137.101:80 check inter 2000 fall 3
      server img02 192.168.137.102:80 check inter 2000 fall 3
    
    backend dynamic 
      balance roundrobin 
      server test1 192.168.1.23:80 check maxconn 2000 
      server test2 192.168.1.24:80 check maxconn 2000
    
    
    listen tcptest 
      bind 0.0.0.0:5222 
      mode tcp 
      option tcplog #采用tcp日志格式 
      balance source 
      #log 127.0.0.1 local0 debug 
      server s1 192.168.100.204:7222 weight 1 
      server s2 192.168.100.208:7222 weight 1
    复制代码

    负载均衡算法

    复制代码
    一、roundrobin,表示简单的轮询,每个服务器根据权重轮流使用,在服务器的处理时间平均分配的情况下这是最流畅和公平的算法。该算法是动态的,对于实例启动慢的服务器权重会在运行中调整。
    
    二、static-rr,表示根据权重,建议关注;每个服务器根据权重轮流使用,类似roundrobin,但它是静态的,意味着运行时修改权限是无效的。另外,它对服务器的数量没有限制。
    
    三、leastconn,表示最少连接者先处理,建议关注;leastconn建议用于长会话服务,例如LDAP、SQL、TSE等,而不适合短会话协议。如HTTP.该算法是动态的,对于实例启动慢的服务器权重会在运行中调整。
    
    四、source,表示根据请求源IP,建议关注;对请求源IP地址进行哈希,用可用服务器的权重总数除以哈希值,根据结果进行分配。
               只要服务器正常,同一个客户端IP地址总是访问同一个服务器。如果哈希的结果随可用服务器数量而变化,那么客户端会定向到不同的服务器;
               该算法一般用于不能插入cookie的Tcp模式。它还可以用于广域网上为拒绝使用会话cookie的客户端提供最有效的粘连;
               该算法默认是静态的,所以运行时修改服务器的权重是无效的,但是算法会根据“hash-type”的变化做调整。
    五、uri,表示根据请求的URI;表示根据请求的URI左端(问号之前)进行哈希,用可用服务器的权重总数除以哈希值,根据结果进行分配。
            只要服务器正常,同一个URI地址总是访问同一个服务器。
            一般用于代理缓存和反病毒代理,以最大限度的提高缓存的命中率。该算法只能用于HTTP后端;
            该算法一般用于后端是缓存服务器;
            该算法默认是静态的,所以运行时修改服务器的权重是无效的,但是算法会根据“hash-type”的变化做调整。
    六、url_param,表示根据请求的URl参数'balance url_param' requires an URL parameter name
                  在HTTP GET请求的查询串中查找<param>中指定的URL参数,基本上可以锁定使用特制的URL到特定的负载均衡器节点的要求;
                  该算法一般用于将同一个用户的信息发送到同一个后端服务器;
                  该算法默认是静态的,所以运行时修改服务器的权重是无效的,但是算法会根据“hash-type”的变化做调整。
    七、hdr(name),表示根据HTTP请求头来锁定每一次HTTP请求;
                  在每个HTTP请求中查找HTTP头<name>,HTTP头<name>将被看作在每个HTTP请求,并针对特定的节点;
                  如果缺少头或者头没有任何值,则用roundrobin代替;
                  该算法默认是静态的,所以运行时修改服务器的权重是无效的,但是算法会根据“hash-type”的变化做调整。
    八、rdp-cookie(name),表示根据据cookie(name)来锁定并哈希每一次TCP请求。
                         为每个进来的TCP请求查询并哈希RDP cookie<name>;
                         该机制用于退化的持久模式,可以使同一个用户或者同一个会话ID总是发送给同一台服务器。
                         如果没有cookie,则使用roundrobin算法代替;
                         该算法默认是静态的,所以运行时修改服务器的权重是无效的,但是算法会根据“hash-type”的变化做调整。
    
    #其实这些算法各有各的用法,我们平时应用得比较多的应该是roundrobin、source和lestconn。
    
    haproxy负载均衡算法
    复制代码

    ACL规则定义

    复制代码
    ########ACL策略定义#########################
    1、#如果请求的域名满足正则表达式返回true -i是忽略大小写
    acl denali_policy hdr_reg(host) -i ^(www.inbank.com|image.inbank.com)$
    
    2、#如果请求域名满足www.inbank.com 返回 true -i是忽略大小写
    acl tm_policy hdr_dom(host) -i www.inbank.com
    
    3、#在请求url中包含sip_apiname=,则此控制策略返回true,否则为false
    acl invalid_req url_sub -i sip_apiname=#定义一个名为invalid_req的策略
    
    4、#在请求url中存在timetask作为部分地址路径,则此控制策略返回true,否则返回false
    acl timetask_req url_dir -i timetask
    
    5、#当请求的header中Content-length等于0时返回 true
    acl missing_cl hdr_cnt(Content-length) eq 0
    
    #########acl策略匹配相应###################
    1、#当请求中header中Content-length等于0 阻止请求返回403
    block if missing_cl
    
    2、#block表示阻止请求,返回403错误,当前表示如果不满足策略invalid_req,或者满足策略timetask_req,则阻止请求。
    block if !invalid_req || timetask_req
    
    3、#当满足denali_policy的策略时使用denali_server的backend
    use_backend denali_server if denali_policy
    
    4、#当满足tm_policy的策略时使用tm_server的backend
    use_backend tm_server if tm_policy
    
    5、#reqisetbe关键字定义,根据定义的关键字选择backend
    reqisetbe ^Host:\ img dynamic
    reqisetbe ^[^\ ]*\ /(img|css)/ dynamic
    reqisetbe ^[^\ ]*\ /admin/stats stats
    
    6、#以上都不满足的时候使用默认mms_server的backend
    default_backend mms
    
    haproxy acl定义
    复制代码

    配置日志

    复制代码
        1.创建记录日志文件
            mkdir /var/log/haproxy
            chmod a+w /var/log/haproxy
        2.编辑/etc/rsyslog.conf
          a) 开启514 UDP监听
               # Provides UDP syslog reception
               $ModLoad imudp
               $UDPServerRun 514
          b) 指定日志存放地点:
              local0.*      /var/log/haproxy/haproxy.log
        3.haproxy.conf 配置:
           global
             # 配置local2事件
            log    127.0.0.1  local0 info
    
        4.重启 rsyslog服务
           systemctl  restart   rsyslog
    复制代码

    启动

    /usr/local/haproxy/sbin/haproxy -f /usr/local/haproxy/haproxy.cfg 

    查看状态

    复制代码
    http://192.168.1.22:1080/stats
    
    #说明:
    #1080即haproxy配置文件中监听端口
    s#tats 即haproxy配置文件中的监听名称

    监控页面详细说明

    复制代码

    展开全文
  • JAVA高并发的三种实现

    万次阅读 多人点赞 2020-09-16 14:10:37
    是用它可以解决一切并发问题,但是,对于系统吞吐量要求更的话,我们这提供几个小技巧。帮助大家减小锁颗粒度,提高并发能力。 初级技巧-乐观锁 乐观锁使用的场景是,读不会冲突,写会冲突。同时读的频率远大于...

    提到锁,大家肯定想到的是sychronized关键字。是用它可以解决一切并发问题,但是,对于系统吞吐量要求更高的话,我们这提供几个小技巧。帮助大家减小锁颗粒度,提高并发能力。

    初级技巧-乐观锁

    乐观锁使用的场景是,读不会冲突,写会冲突。同时读的频率远大于写。

     悲观锁的实现

    悲观的认为所有代码执行都会有并发问题,所以将所有代码块都用sychronized锁住

    乐观锁的实现

    乐观的认为在读的时候不会产生冲突为题,在写时添加锁。所以解决的应用场景是读远大于写时的场景。

    中级技巧-String.intern()

    乐观锁不能很好的解决大量的写冲突的问题,但是很多场景下,锁只是针对某个用户或者某个订单。 比如一个用户先创建session,才能进行后面的操作,但是由于网络的问题,创建session的请求和后续请求几乎同时到达,而并行线程可能会先处理后面的请求。一般情况需要对用户sessionMap加锁,比如上面的乐观锁。在这样的场景下,可以将锁限定在用户本身上,即原来的

    这个比较类似行锁和数据库表锁的概念。显然行锁的并发能力比表锁的高很多。

    实用String.intern();是这种方式的具体实现。类String维护了一个字符串池。当调用intern方法时,如果池已经包含一个等于此String对象的字符串(该对象由equals(Object)方法确定),则返回池中的字符串。可见,当String 相同时,总返回同一个对象,因此就实现了对同一用户加锁。由于所的颗粒度局限于具体用户,使得系统获得最大程度的并发。

    CopyOnWriteMap?

     

    既然说到了“类似于数据库中的行锁的概念”,就不得不提一下MVCC,Java中CopyOnWrite类实现了MVCC。Copy On Write是这样一种机制。当我们读取共享数据的时候,直接读取,不需要同步。当我们修改数据的时候,我们就把当前数据Copy一份副本,然后在这个副本 上进行修改,完成之后,再用修改后的副本,替换掉原来的数据。这种方法就叫做Copy On Write。

     

    但是,,,JDK并没有提供CopyOnWriteMap,为什么?下面有个很好的回答,那就是已经有了ConcurrentHashMap,为什么还需要CopyOnWriteMap?

     

    高级技巧 - 类ConcurrentHashMap

    String.inter()的缺陷是类 String 维护一个字符串池是放在JVM perm区的,如果用户数特别多,导致放入字符串池的String不可控,有可能导致OOM错误或者过多的Full GC。怎么样能控制锁的个数,同时减小粒度锁呢?直接使用Java ConcurrentHashMap?或者你想加入自己更精细的控制?那么可以借鉴ConcurrentHashMap的方式,将需要加锁的对象分为多个bucket,每个bucket加一个锁,伪代码如下:

     

     

     

     

     

     

    展开全文
  • 高并发详解(一)

    万次阅读 多人点赞 2018-10-16 13:44:34
    高并发是指在同一个时间点,有很多用户同时的访问URL地址,比如:淘宝的双11,双12,就会产生高并发,如贴吧的爆吧,就是恶意的高并发请求,也就是DDOS攻击,再屌丝点的说法就像玩撸啊撸被ADC暴击了一样,那伤害你懂得...

    高并发

    高并发是指在同一个时间点,有很多用户同时的访问URL地址,比如:淘宝的双11,双12,就会产生高并发,如贴吧的爆吧,就是恶意的高并发请求,也就是DDOS攻击,再屌丝点的说法就像玩撸啊撸被ADC暴击了一样,那伤害你懂得(如果你看懂了,这个说法说明是正在奔向人生巅峰的屌丝。

    高并发会来带的后果

    • 服务端:
      导致站点服务器/DB服务器资源被占满崩溃,数据的存储和更新结果和理想的设计是不一样的,比如:出现重复的数据记录,多次添加了用户积分等。
    • 用户角度:
      尼玛,这么卡,老子来参加活动的,刷新了还是这样,垃圾网站,再也不来了。
    • 我的经历:
      在做公司产品网站的过程中,经常会有这样的需求,比如什么搞个活动专题,抽奖,签到,搞个积分竞拍等等,如果没有考虑到高并发下的数据处理,那就Game Over了,很容易导致抽奖被多抽走,签到会发现一个用户有多条记录,签到一次获得了获得了多积分,等等,各种超出正常逻辑的现象,这就是做产品网站必须考虑的问题,因为这些都是面向大量用户的,而不是像做ERP管理系统,OA系统那样,只是面向员工。

    下面我进行实例分析,简单粗暴,动态分析,纯属本人个人经验分享,如有说错,或者有更好的建议或者意见的请留言,大家一起成长。

    并发下的数据处理:

    通过表设计,如:记录表添加唯一约束,数据处理逻辑使用事物防止并发下的数据错乱问题
    通过服务端锁进程防止包并发下的数据错乱问题

    这里主要讲述的是在并发请求下的数据逻辑处理的接口,如何保证数据的一致性和完整性,这里的并发可能是大量用户发起的,也可能攻击者通过并发工具发起的并发请求


    如例子:通过表设计防止并发导致数据错乱

    • 需求点
      【签到功能】 一天一个用户只能签到一次,
      签到成功后用户获取到一个积分
    • 已知表
      用户表,包含积分字段
      高并发意淫分析(属于开发前的猜测):
      在高并发的情况下,会导致,一个用户签到记录会有多条,或者用户签到后不止加一积分。
    • 我的设计
      首先根据需求我会添加一张签到记录表,重点来了,这张表需要把用户唯一标识字段(ID,Token)和签到日期字段添加为唯一约束,或者唯一索引,这样就可以防止并发的时候插入重复用户的签到记录。然后再程序代码逻辑里,先执行签到数据的添加(这里可以防止并发,添加成功后再进行积分的添加,这样就可以防止重复的添加积分了。最后我还是建议所有的数据操作都写在一个sql事务里面, 这样在添加失败,或者编辑用户积分失败的时候可以回滚数据。

    如例子2(事务+通过更新锁 防止并发导致数据错乱 或者事物+Update的锁表机制)

    • 需求点:
      【抽奖功能】 抽奖一次消耗一个积分 抽奖中奖后编辑剩余奖品总数 剩余奖品总数为0,或者用户积分为0的时候无法进行抽奖
    • 已知表:
      用户表,包含积分字段 奖品表,包含奖品剩余数量字段
    • 高并发意淫分析(属于开发前的猜测):
      在高并发的情况下,会导致用户参与抽奖的时候积分被扣除,而奖品实际上已经被抽完了
    • 我的设计:
      在事物里,通过WITH (UPDLOCK) 锁住商品表,或者Update 表的奖品剩余数量和最后编辑时间字段,来把数据行锁住,然后进行用户积分的消耗,都完成后提交事物,失败就回滚。 这样就可以保证,只有可能存在一个操作在操作这件商品的数量,只有等到这个操作事物提交后,其他的操作这个商品行的事物才会继续执行。

    如例子3(通过程序代码防止包并发下的数据错乱问题)

    • 需求点:
      【缓存数据到cache里】, 当缓存不存在的时候,从数据库中获取并保存在cache里,如果存在从cache里获取,每天10点必须更新一次,其他时间点缓存两个小时更新一次 到10点的时候,凡是打开页面的用户会自动刷新页面
    • 问题点:
      这里有个逻辑用户触发缓存的更新,用户刷新页面,当缓存存在的时候,会取到最后一次缓存更新时间,如果当前时间大于十点,并且最后缓存时间是10点前,则会从数据库中重新获取数据保存到cache中。 还有客户端页面会在10点时候用js发起页面的刷新,就是因为有这样的逻辑,导致10点的时候有很多并发请求同时过来,然后就会导致很多的sql查询操作,理想的逻辑是,只有一个请求会去数据库获取,其他都是从缓存中获取数据。(因为这个sql查询很耗服务器性能,所以导致在10点的时候,突然间数据库服务器压力暴增)
    • 解决问题:
      C#通过 (锁)lock,在从数据读取到缓存的那段代码前面加上锁,这样在并发的情况下只会有一个请求是从数据库里获取数据,其他都是从缓存中获取。

    访问量大的数据统计接口

    • 需求: 用户行为数据统计接口,用来记录商品展示次数,用户通过点击图片,或者链接,或者其他方式进入到商品详情的行为次数
    • 问题点:
      这接口是给前端ajax使用,访问量会很大,一页面展示的时候就会有几十件商品的展示,滚动条滚到到页面显示商品的时候就会请求接口进行展示数据的统计,每次翻页又会加载几十件
    • 意淫分析:
      设想如果同时有1W个用户同时在线访问页面,一个次拉动滚动条屏幕页面展示10件商品,这样就会有10W个请求过来,服务端需要把请求数据入库。在实际线上环境可能还会超过这个请求量,如果不经过进行高并发设计处理,服务器分分钟给跪了。
    • 解决问题:
      我们通过nodejs写了一个数据处理接口,把统计数据先存到redis的list里。(使用nodejs写接口的好处是,nodejs使用单线程异步事件机制,高并发处理能力强,不会因为数据逻辑处理问题导致服务器资源被占用而导致服务器宕机) 然后再使用nodejs写了一个脚本,脚本功能就是从redis里出列数据保存到mysql数据库中。这个脚本会一直运行,当redis没有数据需要同步到数据库中的时候,sleep,让在进行数据同步操作

    高并发的下的服务器压力均衡,合理站点架设,DB部署

    以下我所知道的:

    1. 服务器代理nginx,做服务器的均衡负载,把压力均衡到多台服务器
    2. 部署集群 mysql数据库, redis服务器,或者mongodb服务器,把一些常用的查询数据,并且不会经常的变化的数据保存到其他NoSQL DB服务器中,来减少数据库服务器的压力,加快数据的响应速度。
    3. 数据缓存,Cache
    4. 在高并发接口的设计中可以使用具有高并发能力的编程语言去开发,如:nodejs 做web接口
    5. 服务器部署,图片服务器分离,静态文件走CDN
    6. DBA数据库的优化查询条件,索引优化
    7. 消息存储机制,将数据添加到信息队列中(redis list),然后再写工具去入库
    8. 脚本合理控制请求,如,防止用户重复点击导致的ajax多余的请求,等等。

    并发测试神器推荐

    1. Apache JMeter
    2. Microsoft Web Application Stress Tool
    3. Visual Studio 性能负载

    本系列:

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

    高并发经常会发生在有大活跃用户量,用户高聚集的业务场景中,如:秒杀活动,定时领取红包等。
    为了让业务可以流畅的运行并且给用户一个好的交互体验,我们需要根据业务场景预估达到的并发量等因素,来设计适合自己业务场景的高并发处理方案。

    在电商相关产品开发的这些年,我有幸的遇到了并发下的各种坑,这一路摸爬滚打过来有着不少的血泪史,这里进行的总结,作为自己的归档记录,同时分享给大家。


    服务器架构

    业务从发展的初期到逐渐成熟,服务器架构也是从相对单一到集群,再到分布式服务。
    一个可以支持高并发的服务少不了好的服务器架构,需要有均衡负载,数据库需要主从集群,NoSQL缓存需要主从集群,静态文件需要上传cdn,这些都是能让业务程序流畅运行的强大后盾。

    服务器这块多是需要运维人员来配合搭建,具体我就不多说了,点到为止。
    大致需要用到的服务器架构如下:

    • 服务器
      • 均衡负载(如:nginx,阿里云SLB)
      • 资源监控
      • 分布式
    • 数据库
      • 主从分离,集群
      • DBA 表优化,索引优化,等
      • 分布式
    • nosql
      • redis
        • 主从分离,集群
      • mongodb
        • 主从分离,集群
      • memcache
        • 主从分离,集群
    • cdn
      • html
      • css
      • js
      • image

    并发测试

    高并发相关的业务,需要进行并发的测试,通过大量的数据分析评估出整个架构可以支撑的并发量。

    测试高并发可以使用第三方服务器或者自己测试服务器,利用测试工具进行并发请求测试,分析测试数据得到可以支撑并发数量的评估,这个可以作为一个预警参考,俗话说知己自彼百战不殆。

    第三方服务:

    • 阿里云性能测试

    并发测试工具:

    • Apache JMeter
    • Visual Studio性能负载测试
    • Microsoft Web Application Stress Tool

    实战方案

    通用方案

    日用户流量大,但是比较分散,偶尔会有用户高聚的情况;

    场景: 用户签到,用户中心,用户订单,等
    服务器架构图: 通用

    说明:

    场景中的这些业务基本是用户进入APP后会操作到的,除了活动日(618,双11,等),这些业务的用户量都不会高聚集,同时这些业务相关的表都是大数据表,业务多是查询操作,所以我们需要减少用户直接命中DB的查询;优先查询缓存,如果缓存不存在,再进行DB查询,将查询结果缓存起来。

    更新用户相关缓存需要分布式存储,比如使用用户ID进行hash分组,把用户分布到不同的缓存中,这样一个缓存集合的总量不会很大,不会影响查询效率。

    方案如:

    • 用户签到获取积分
      • 计算出用户分布的key,redis hash中查找用户今日签到信息
      • 如果查询到签到信息,返回签到信息
      • 如果没有查询到,DB查询今日是否签到过,如果有签到过,就把签到信息同步redis缓存。
      • 如果DB中也没有查询到今日的签到记录,就进行签到逻辑,操作DB添加今日签到记录,添加签到积分(这整个DB操作是一个事务)
      • 缓存签到信息到redis,返回签到信息
      • 注意这里会有并发情况下的逻辑问题,如:一天签到多次,发放多次积分给用户。
      • 我的博文[大话程序猿眼里的高并发]有相关的处理方案。
    • 用户订单
      • 这里我们只缓存用户第一页的订单信息,一页40条数据,用户一般也只会看第一页的订单数据
      • 用户访问订单列表,如果是第一页读缓存,如果不是读DB
      • 计算出用户分布的key,redis hash中查找用户订单信息
      • 如果查询到用户订单信息,返回订单信息
      • 如果不存在就进行DB查询第一页的订单数据,然后缓存redis,返回订单信息
    • 用户中心
      • 计算出用户分布的key,redis hash中查找用户订单信息
      • 如果查询到用户信息,返回用户信息
      • 如果不存在进行用户DB查询,然后缓存redis,返回用户信息
    • 其他业务
      • 上面例子多是针对用户存储缓存,如果是公用的缓存数据需要注意一些问题,如下
      • 注意公用的缓存数据需要考虑并发下的可能会导致大量命中DB查询,可以使用管理后台更新缓存,或者DB查询的锁住操作。
      • 我的博文[大话Redis进阶]对更新缓存问题和推荐方案的分享。

    以上例子是一个相对简单的高并发架构,并发量不是很高的情况可以很好的支撑,但是随着业务的壮大,用户并发量增加,我们的架构也会进行不断的优化和演变,比如对业务进行服务化,每个服务有自己的并发架构,自己的均衡服务器,分布式数据库,nosql主从集群,如:用户服务、订单服务;


    消息队列

    秒杀、秒抢等活动业务,用户在瞬间涌入产生高并发请求

    场景:定时领取红包,等
    服务器架构图:消息队列

    说明:

    场景中的定时领取是一个高并发的业务,像秒杀活动用户会在到点的时间涌入,DB瞬间就接受到一记暴击,hold不住就会宕机,然后影响整个业务;

    像这种不是只有查询的操作并且会有高并发的插入或者更新数据的业务,前面提到的通用方案就无法支撑,并发的时候都是直接命中DB;

    设计这块业务的时候就会使用消息队列的,可以将参与用户的信息添加到消息队列中,然后再写个多线程程序去消耗队列,给队列中的用户发放红包;

    方案如:

    • 定时领取红包
      • 一般习惯使用 redis的 list
      • 当用户参与活动,将用户参与信息push到队列中
      • 然后写个多线程程序去pop数据,进行发放红包的业务
      • 这样可以支持高并发下的用户可以正常的参与活动,并且避免数据库服务器宕机的危险

    附加:
    通过消息队列可以做很多的服务。
    如:定时短信发送服务,使用sset(sorted set),发送时间戳作为排序依据,短信数据队列根据时间升序,然后写个程序定时循环去读取sset队列中的第一条,当前时间是否超过发送时间,如果超过就进行短信发送。


    一级缓存

    高并发请求连接缓存服务器超出服务器能够接收的请求连接量,部分用户出现建立连接超时无法读取到数据的问题;

    因此需要有个方案当高并发时候时候可以减少命中缓存服务器;

    这时候就出现了一级缓存的方案,一级缓存就是使用站点服务器缓存去存储数据,注意只存储部分请求量大的数据,并且缓存的数据量要控制,不能过分的使用站点服务器的内存而影响了站点应用程序的正常运行,一级缓存需要设置秒单位的过期时间,具体时间根据业务场景设定,目的是当有高并发请求的时候可以让数据的获取命中到一级缓存,而不用连接缓存nosql数据服务器,减少nosql数据服务器的压力

    比如APP首屏商品数据接口,这些数据是公共的不会针对用户自定义,而且这些数据不会频繁的更新,像这种接口的请求量比较大就可以加入一级缓存;

    服务器架构图:通用

    合理的规范和使用nosql缓存数据库,根据业务拆分缓存数据库的集群,这样基本可以很好支持业务,一级缓存毕竟是使用站点服务器缓存所以还是要善用。


    静态化数据

    高并发请求数据不变化的情况下如果可以不请求自己的服务器获取数据那就可以减少服务器的资源压力。

    对于更新频繁度不高,并且数据允许短时间内的延迟,可以通过数据静态化成JSON,XML,HTML等数据文件上传CDN,在拉取数据的时候优先到CDN拉取,如果没有获取到数据再从缓存,数据库中获取,当管理人员操作后台编辑数据再重新生成静态文件上传同步到CDN,这样在高并发的时候可以使数据的获取命中在CDN服务器上。

    CDN节点同步有一定的延迟性,所以找一个靠谱的CDN服务器商也很重要


    其他方案

    • 对于更新频繁度不高的数据,APP,PC浏览器,可以缓存数据到本地,然后每次请求接口的时候上传当前缓存数据的版本号,服务端接收到版本号判断版本号与最新数据版本号是否一致,如果不一样就进行最新数据的查询并返回最新数据和最新版本号,如果一样就返回状态码告知数据已经是最新。减少服务器压力:资源、带宽
    展开全文
  • 如何解决高并发问题

    千次阅读 2018-11-05 11:14:40
    一个小型的网站,比如个人网站,可以...已经细分到很细的方方面面,尤其对于大型网站来说,所采用的技术更是涉及面非常广,从硬件到软件、编程语言、数据库、WebServer、防火墙等各个领域都有了很的要求,已经不是...
  • 什么是高并发 ,详细讲解

    万次阅读 多人点赞 2018-08-30 13:38:58
    一、什么是高并发 高并发(High Concurrency)是互联网分布式系统架构设计中必须考虑的因素之一,它通常是指,通过设计保证系统能够同时并行处理很多请求。   高并发相关常用的一些指标有响应时间(Response ...
  • web项目处理高并发解决思路

    千次阅读 2019-11-08 15:02:56
    一、CDN缓存(网络请求上游) CDN其实是一种资源的分布式存放和备份的方法。解决因分布、带宽、服务器性能带来的访问延迟问题,适用于站点加速、秒杀、点播、直播等场景。使用户可以就近取得所需内容,解决Internet...
  • 到底多大才算高并发

    万次阅读 多人点赞 2019-05-22 20:58:07
    高并发(High Concurrency)是使用技术手段使系统可以并行处理很多请求。 关键指标: -响应时间(Response Time) -吞吐量(Throughput) -每秒查询率QPS(Query Per Second) -每秒事务处理量TPS(Transaction Per ...
  • 处理高并发的六种方法

    万次阅读 2019-03-19 12:35:02
    处理高并发的六种方法 1:系统拆分,将一个系统拆分为多个子系统,用dubbo来搞。然后每个系统连一个数据库,这样本来就一个库,现在多个数据库,这样就可以抗高并发。 2:缓存,必须得用缓存。大部分的高并发场景,...
  • 解决高并发的几种方法

    万次阅读 2017-08-07 13:42:39
    一、将数据存到redis缓存 二、使用高性能的服务器、高性能的数据库、高效率的编程语言、还有高性能的Web...在电商项目中,会有某一件商品许多用户去访问,这个时候就会产生高并发,我解决的方式就是使用redis缓存去解决
  • SpringBoot 高并发调优

    千次阅读 2019-03-29 11:04:06
    1、springboot内置tomcat默认支持线程并发数 https://blog.csdn.net/kouwoo/article/details/83898788 2、解决OutOfMemoryError: unable to create new native thread问题 ...
  • C#高并发处理的集中解决方案

    千次阅读 2018-07-28 22:26:25
    https://blog.csdn.net/mss359681091/article/details/52056105
  • PHP-高并发和大流量的解决方案

    万次阅读 多人点赞 2018-08-18 23:54:24
    高并发的概念 在互联网时代,并发,高并发通常是指并发访问。也就是在某个时间点,有多少个访问同时到来。   二 高并发架构相关概念 1、QPS (每秒查询率) : 每秒钟请求或者查询的数量,在互联网领域,指每秒...
  • Java面试中常见的高并发解决方案

    万次阅读 2019-08-23 11:31:23
    Java面试中常见的高并发解决方案
  • tomcat高并发配置调优

    万次阅读 2017-05-18 21:32:35
    最近部署的tomcat,里面放了一个apk提供给测试人员测试,二有一天压测的时候,他们一致反馈下载不了,结果查看日志才发现如下错误: ...才惊醒这个tomcat根本知识解压就使用的,配置都没动过,肯定不能支持高并发
  • 高并发和多线程有什么关系吗?访问量一多是不是必须要有多线程来实现?
  • Java高并发编程之第一阶段,多线程基础深入浅出

    万次阅读 热门讨论 2017-02-24 23:47:18
    汪文君高并发编程第一阶段01讲-课程大纲及主要内容介绍 汪文君高并发编程第一阶段02讲-简单介绍什么是线程 汪文君高并发编程第一阶段03讲-创建并启动线程 汪文君高并发编程第一阶段04讲-线程生命周期以及start方法...
  • nodejs经典高并发

    万次阅读 2016-07-14 11:42:06
    网上摘的,看得懂么。短短几行代码。功效 perfect! 在一条sql执行的时间内,...node.js 高并发可见一斑。 var EventProxy = require('eventproxy'); var proxy = new EventProxy(); var status = "ready"; var select
  • 浅谈redis如何实现高并发、高性能

    万次阅读 2019-02-18 22:10:42
    2、redis如何实现高并发? 》 1、redis实现高性能 redis是基于内存进行操作的,性能较高; 前端发送请求、后端进行和mysql数据库进行交互,进行sql 查询操作,读写比较慢,这时候引入redis ,把从mysql 的数据放入...
  • 俗语聊高并发保证你能听懂 高并发基础部分 问:先提出几个问题,什么是高并发?现在天天说高并发,自己先思考一下! 答:高并发,其实就是使用技术手段使得系统可以并行处理很多的请求! 问:一个系统的...
1 2 3 4 5 ... 20
收藏数 731,720
精华内容 292,688
关键字:

高并发