系统架构之高可用和高并发粗略处理方案

来源:互联网 发布:python 聚类算法 编辑:程序博客网 时间:2024/05/26 02:20
一、负载均衡
企业web项目架构图

1、客户端通过企业防火墙发送请求
2、在App服务器如tomcat接收客户端请求前,面对高并发大数据量访问的企业架构,会通过加入负载均衡主备服务器将请求进行转发到不同web服务其中。
3、服务器通过访问数据库进行交互,同样高并发大数据会涉及到数据库处理高并发的方案
4、另外会添加多台服务器用作缓存、消息处理等
高并发一般会发生在1、负载均衡处 2、数据库高并发
高并发初期解决方案:
应对高并发,解决方案大多从服务器级别和应用程序级别【硬件和软件】两个方向进行,如增大服务器的CPU,增加内存,或者直接购买高性能服务器。但随着业务的不断增加,服务器性能也达到瓶颈。第二个方向就是从应用程序级别也就是软件设计编码方向,如HTML静态化、图片服务器分离、分布式缓存,减少客户端访问时并发请求的数据。
下面有几种利用负载均衡方式
1、F5(硬件负载均衡器)
直接在服务器和外部网络间安装负载均衡设备,这种设备我们通常称之为负载均衡器。由于专门的设备完成专门的任务,独立于操作系统,整体性能得到大量提高,加上多样化的负载均衡策略,智能化的流量管理,可达到最佳的负载均衡需求。 一般而言,硬件负载均衡在功能、性能上优于软件方式,不过成本昂贵,比如最常见的就是F5负载均衡器。
F5负载均衡器是应用交付网络的全球领导者F5 Networks公司提供的一个负载均衡器专用设备,F5 BIG-IP LTM 的官方名称叫做本地流量管理器,可以做4-7层负载均衡,具有负载均衡、应用交换、会话交换、状态监控、智能网络地址转换、通用持续性、响应错误处理、IPv6网关、高级路由、智能端口镜像、SSL加速、智能HTTP压缩、TCP优化、第7层速率整形、内容缓冲、内容转换、连接加速、高速缓存、Cookie加密、选择性内容加密、应用攻击过滤、拒绝服务(DoS)攻击和SYN Flood保护、防火墙—包过滤、包消毒等功能。
2、DNS
简单理解:Domain Name System,域名系统是因特网上作为域名和IP地址相互映射的一个分布式数据库,能够使用户更方便的访问互联网。例如我们将程序发布到192.168.55.145 和144两台服务器上,通过DNS可以设置一个统一的访问入口,如www.Max1209.com对这两台服务器上的服务进行访问。用户直接访问www.Max1209.com主机名而不需记住机器IP,通过主机名,最终得到该主机名进行域名解析得到对应的IP地址进行访问。
在DNS服务器中,可以为多个不同的IP配置同一个名字,这个数据被发送给其他名字服务器,而最终查询这个名字的客户机将在解析这个名字时随机使用其中一个地址。因此,对于同一个名字,不同的客户机会得到不同的地址,因此不同的客户访问的也就是不同地址的Web服务器。简单说,也就是一个外观,给部署了同一个网站的n多台服务器设置同一个名字,不同地区或者不同特点的用户访问同一个名字,实际接收客户请求的是外观里的不同ip的服务器,从而达到负载均衡的目的。
同时面对更高访问量需求,DNS可以以设置成树状,多个DNS服务器将请求分发给下一个DNS服务器,N层解析之后再访问到应用服务器,这样就可以增加应用服务器的个数,应对更大并发数据请求。

但使用DNS负载均衡的时候,如果服务器发生故障,DNS继续把请求发送给故障机器,一直到把故障服务器从DNS中移走为止,这样用户就只能等到DNS连接超时后才能访问到目标网站。所以会有一定的缓存时间并且没有失败重试的机制。
2、LVS(软件负载均衡器)
LVS是Linux Virtual Server的简称,也就是Linux虚拟服务器。通过LVS提供的负载均衡技术和Linux操作系统实现一个高性能、高可用的服务器群集,它具有良好可靠性、可扩展性和可操作性。从而以低廉的成本实现最优的服务性能。
使用LVS架设的服务器集群系统有三个部分组成:最前端的负载均衡层,用Load Balancer表示,中间的服务器群组层,用Server Array表示,最底端的数据共享存储层,用Shared Storage表示,在用户看来,所有的内部应用都是透明的,用户只是在使用一个虚拟服务器提供的高性能服务。

LVS的体系结构
3、Nginx(服务端)
Nginx(发音同engine x)是一个网页服务器,它能反向代理HTTP, HTTPS, SMTP, POP3, IMAP的协议链接,以及一个负载均衡器和一个HTTP缓存。
(1)上游服务器配置
使用upstream server配置上游服务器。
upstream server配置主要有IP地址和端口权重
然后我们配置proxy_pass来处理用户请求。
http {
upstream myServer {   
    server 127.0.0.1:9090 down; 
    server 127.0.0.1:8080 weight=2; 
}
server {
listen 80;
server_name  localhost;
location /webautotest/ {
       proxy_buffering off;
       proxy_pass http://myServer;
  }
}
}
(2)负载均衡算法
nginx 支持几种方式的分配 
1)、轮询(默认):round-robin
      每个请求按时间顺序逐一分配到不同的后端服务器,如果后端服务器down掉,能自动剔除。 
2)、权重:weight 
      指定轮询几率,weight和访问比率成正比,用于后端服务器性能不均的情况。 
3)、ip_hash 
      每个请求按访问ip的hash结果分配,这样每个访客固定访问一个后端服务器,可以解决session的问题。  
4)、hash key
      对某一个key进行哈希($url)或者使用一致性哈希算法($consistent_key动态指定)
5)、least_conn
将请求负载均衡到最少活跃连接的上游服务器。如果配置的服务器较少,则将转而使用基于权重的轮训算法。
6)、least_time
最小平均响应时间进行负载均衡
(3)失败重试机制
通过配置上游服务器的max_failsfail_timeout,来指定每个上游服务器,当fail_timeout时间内失败了max_fails次请求,则认为该服务器不可用或者不存活,然后摘掉该服务器,时间后会再次将该服务器加入到存活的服务器列表进行重试。
配置如下:
upstream myServer {   
    server 127.0.0.1:9090 max_fails=2 fail_timeout=10s weight=1;  
    server 127.0.0.1:8080 max_fails=2 fail_timeout=10s weight=1; 
}
然后进行proxy_next_upstream相关配置,当遇到配置的错误是,会重试下一台上游服务器。
(4)健康检测
nginx反向代理实现包含服务器健康检查。如果来自特定服务器的响应失败,报错,nginx将标记该服务器为failed,一段时间内尽量避免选择此服务器作为随后请求的分发服务器。
Nginx对上游服务器的健康检查默认采用的是惰性策略。我们可以采用集成的nginx_upstream_check_module模块来进行主动健康检查,它支持TCP心跳和HTTP心跳来实现健康检查。
TCP心跳检查:
upstream myServer {   
    server 127.0.0.1:9090 max_fails=2 fail_timeout=10s weight=1;  
    server 127.0.0.1:8080 max_fails=2 fail_timeout=10s weight=1; 
check interval=3000 rise=1 fall=3 timeout=2000 type=tcp;
}
interval:检测时间,每隔3秒检测一次。
fall:检测失败多少次后,上游服务器标记为不存活。
rise:检查成功多少次后,上游服务器标记为存活,并可以处理请求。
timeout:检测请求超时时间配置。
HTTP心跳检查:
upstream myServer {   
    server 127.0.0.1:9090 max_fails=2 fail_timeout=10s weight=1;  
    server 127.0.0.1:8080 max_fails=2 fail_timeout=10s weight=1; 
check interval=3000 rise=1 fall=3 timeout=2000 type=http;
check_http_send "HEAD /status HTTP/1.0\r\n\r\n";
check_http_expert_alive http_2xx http_3xx;
}
check_http_send: 检查是发的http请求内容
check_http_expert_alive: 当上游服务器返回匹配的响应状态码时,则认为上游服务器存活。
注意:检查时间间隔不能太短,否则可能因为心跳检查包太多造成上游服务器挂掉,同事设置合理的超时间。
(5)其他配置
1)域名上游服务器:当域名对应的IP发生变化时,该upstream不会更新,商业版才支持更新,不过,proxy_pass http://w3c.com 是支持动态域名解析的。
upstream myServer {   
    server w3c.com;  
    server w4c.com; 
}
2)备份上游服务器和不可用上游服务器:
upstream myServer {   
    server 127.0.0.1:9090 weight=1;  
    server 127.0.0.1:9090 weight=1down;  
    server 127.0.0.1:8080 weight=2backup
}
(6)nginx缓存
nginx提供缓存来减少上游服务器的压力,开启proxy buffer,缓存内容将放到tmpfs(内存文件系统)以提升性能,设置超时时间。
开启proxy_pass_request_bodyproxy_pass_request_henders,禁止向服务器传递请求头和内容体,从而使得上游服务器不受请求头攻击,也不需要解析;如果需要传递,则使用proxy_set_header配置即可(如同spring cloud zuul 服务网关一样,可以设置请求头的信息)。
建议开启gzip压缩,减少网络传输数据的数据包大小,gzip_comp_level压缩级别要根据实际压测决定(带宽和吞吐量),gzip_min_length需要压缩的最小文件大小。
(7)HTTP动态负载均衡
每次有upstream列表有变更,都需要到服务器进行修改,首先管理容易出现问题,而且对于upsteream服务上线无法自动注册到nginx upstream列表。提供Consul+Consul-template实现nginx动态配置。使用OpenResty balance_by_lua实现无reload动态负载均衡(如spring_cloud_bus一样)。
注意:consul-template和nginx必须装到一台机器,因为consul-template需要动态修改nginx配置文件。
(8)nginx基本使用
启动Nginx服务
进入安装目录,默认进入cd /usr/local/Nginx/sbin/ ,查看该目录所有文件,执行./Nginx 开启Nginx服务。
本地启动
浏览器访问Linux的IP,如:http://192.168.182.129:8080/,默认是80端口
关闭Nginx服务
可以使用lsof -i:80查看80端口占用情况,找到nginx启动进程号,执行kill -9 进程ID进行关闭服务。另外也可在安装目录下执行./nginx -s quit(进程处理完毕后停止服务)或./nginx -s stop(同kill 强行停止)两个命令进行关闭服务。

二、隔离
隔离是指将系统或资源分割开,系统隔离是为了在系统发生故障时能限定传播范围和影响范围,即发生故障后不会出现滚雪球效应(造成雪崩),从而保证只有出问题的服务不可用,其他服务还是可用的;而资源隔离有脏数据隔离、通过隔离后减少资源竞争提升性能等。我遇到的比较多的隔离手段有线程隔离、进程隔离、集群隔离、机房隔离、读写隔离、动静隔离、爬虫隔离等。而出现系统问题时可以考虑负载均衡路由、自动/手动切换分组或者降级等手段来提升可用性。
(1)线程隔离
线程隔离主要有线程池隔离,在实际使用时我们会把请求分类,然后交给不同的线程池处理,当一种业务的请求处理发生问题时,不会将故障扩散到其他线程池,从而保证其他服务可用。
首先,当大多数人在使用Tomcat时,多个HTTP服务会共享一个线程池,假设其中一个HTTP服务访问的数据库响应非常慢,这将造成服务响应时间延迟增加,大多数线程阻塞等待数据响应返回,导致整个Tomcat线程池都被该服务占用,甚至拖垮整个Tomcat。因此,如果我们能把不同HTTP服务隔离到不同的线程池,则某个HTTP服务的线程池满了也不会对其他服务造成灾难性故障。


(2)进程隔离
在公司发展初期,一般是先进行从0到1,不会一上来就进行系统的拆分,这样就会开发出一些比较大而全的系统,系统中的一个模块/功能出现问题,整个系统就不可用了。首先想到的解决方案是通过部署多个实例,然后通过负载均衡进行路由转发,但是这种情况无法避免某个模块因BUG而出现如OOM导致整个系统不可用的风险。因此此种方案只是一个过渡,较好的解决方案是通过将系统拆分为多个子系统来实现物理隔离。通过进程隔离使得某一个子系统出现问题不会影响到其他子系统。

(3)集群隔离
随着系统的发展,单实例服务无法满足需求了,此时需要服务化技术,通过部署多个服务,形成服务集群来提升系统容量,如下图所示:

随着调用方的增多,当秒杀服务被刷会影响到其他服务的稳定性,此时应该考虑为秒杀提供单独的服务集群,即为服务分组,从而当某一个分组出现问题不会影响到其他分组,从而实现了故障隔离,如下图所示:

比如注册生产者时提供分组名:
<jsf:provider id="myService" interface="com.jd.MyService" alias="${分组名}" ref="myServiceImpl"/>
消费时使用相关的分组名即可:
<jsf:consumer id="myService" interface="com.jd.MyService" alias="${分组名}"/>

(4)机房隔离
随着对系统可用性的要求,会进行多机房部署,每个机房的服务都有自己的服务分组,本机房的服务应该只调用本机房服务,不进行跨机房调用;其中一个机房服务发生问题时可以通过DNS/负载均衡将请求全部切到另一个机房;或者考虑服务能自动重试其他机房的服务从而提升系统可用性。
一种办法是根据IP(不同机房IP段不一样)自动分组,还一种较灵活的办法是通过在分组名中加上机房名解决:
<jsf:provider id="myService" interface="com.jd.MyService" alias="${分组名}-${机房}" ref="myServiceImpl"/><jsf:consumer id="myService" interface="com.jd.MyService" alias="${分组名}-${机房}"/>

(5)读写隔离
如下图所示,通过主从模式将读和写集群分离,读服务只从从Redis集群获取数据,当主Redis集群出现问题时,从Redis集群还是可用的,从而不影响用户访问;而当从Redis集群出现问题时可以进行其他集群的重试。
--先读取从status, resp = slave_get(key)if status == STATUS_OK then return status, valueend--如果从获取失败了,从主获取status, resp = master_get(key)

(6)动静隔离
当用户访问如结算页时,如果JS/CSS等静态资源也在结算页系统中时,很可能因为访问量太大导致带宽被打满导致出现不可用。静态资源和动态资源转静态化(一般使用模板技术thinkphp等) 进行缓存。
因此应该将动态内容和静态资源分离,一般应该将静态资源放在CDN上,如下图所示:

当项目中使用了大量的引用文件时,使用绝对路径通过cdn引用。
•绝对 URL - 指向其他站点(比如 src="www.example.com/example.js")•相对 URL - 指向站点内的文件(比如 src="/scripts/example.js")
使用CDN访问过程如下

与传统访问方式不同,CDN网络则是在用户和服务器之间增加缓存层,将用户的访问请求引导到最优的缓存节点而不是服务器源站点,从而加速访问速度。
CDN应用场景:
1)静态网页
图片小文件、博客
2)大文件下载
软件下载、视频点播或图片存储网站
3)动态加速
直播网站
4)应用加速
手机APP

(7)爬虫隔离
在实际业务中我们曾经统计过一些页面型应用的爬虫比例,爬虫和正常流量的比例能达到5:1,甚至更高。而一些系统是因为爬虫访问量太大而导致服务不可用;一种解决办法是通过限流解决;还一种解决办法是在负载均衡层面将爬虫路由到单独集群,从而保证正常流量可用,爬虫流量尽量可用。
比如最简单的使用Nginx可以这样配置:
set $flag 0; if ($http_user_agent ~* "spider") { set $flag "1"; } if($flag = "0") { //代理到正常集群}if ($flag = "1") { //代理到爬虫集群}

if ($http_user_agent ~* "qihoobot|Baiduspider|Googlebot|Googlebot-Mobile|Googlebot-Image|Mediapartners-Google|Adsbot-Google|Feedfetcher-Google|Yahoo! Slurp|Yahoo! Slurp China|YoudaoBot|Sosospider|Sogou spider|Sogou web spider|MSNBot|ia_archiver|Tomato Bot") 为常用的爬虫方式。
不仅仅对爬虫user-agent过滤,还会过滤一些恶意IP(统计IP访问量,配置阀值),将他们分流到固定分组。还有一种办法是种植Cookie,访问特殊服务前先种植Cookie,访问服务时验证该Cookie,如果没有或者不对可以考虑出验证码或者分流到固定分组。

(8)热点隔离
秒杀、抢购属于非常合适的热点例子;对于这种热点是能提前知道的,所以可以将秒杀和抢购做成独立系统或服务进行隔离,从而保证秒杀/抢购流程出现问题不影响主流程。
还存在一些热点可能是因为价格或突发事件引起的;对于读热点我使用多级缓存搞定;而写热点一般通过缓存+队列模式削峰。

(9)使用Hystrix实现隔离
Hystrix是Netflix开源的一款针对分布式系统的延迟和容错库,目的是用来隔离分布式服务故障。它提供线程和信号量隔离,以减少不同服务之间资源竞争带来的相互影响;提供优雅降级机制;提供熔断机制使得服务可以快速失败,而不是一直阻塞等待服务响应,并能从中快速恢复。Hystrix通过这些机制来阻止级联失败并保证系统弹性、可用。
Hystrix提供主要功能如下:
1.限制调用分布式服务的资源使用,某一个调用的服务出现问题不会影响其他服务调用,通过线程池隔离和信号量隔离实现。
2.Hystrix提供了优雅降级机制:超时降级、资源不足时(线程或信号量)降级,降级后可以配合降级接口返回托底数据。
3.Hystrix也提供了熔断器实现,当失败率达到阀值自动触发降级(如因网络故障/超时造成的失败率高),熔断器触发的快速失败会进行快速恢复。
4.还提供了请求缓存、请求合并实现。
详情参考:https://github.com/Netflix/hystrix

三、限流、降级、缓存
在开发高并发系统时,缓存、降级、限流可以有效的保护系统。缓存可以提升系统访问和增大系统处理能力;降级是当服务出问题或者影响到核心流程的性能是,暂时屏蔽掉部分服务,待高峰过去或者问题解决后再打开服务;限流是通过对并发访问/请求进行限速或者一个时间窗口内的请求进行限速来保护系统,一旦达到限速率则可以拒绝服务(定向到错误页面或者告知资源没有了等其他友情提示)、排队或等待(比如秒杀、评论、下单)、降级。
(1) 限流
......
(2) 降级
......
(3) 缓存
缓存主要有应用级缓存、HTTP缓存、多级缓存。
名词解释:
  • SoR(system-of-record):记录系统,或者可以叫做数据源,即实际存储原始数据的系统。
  • Cache:缓存,是SoR的快照数据,Cache的访问速度比SoR要快,放入Cache的目的是提升访问速度,减少回源到SoR的次数。
  • 回源:即回到数据源头获取数据,Cache没有命中时,需要从SoR读取数据,这叫做回源。
这里简单介绍下应用级缓存的几种模式:
1) Cache-Aside
Cache-Aside 即业务代码围绕着Cache写,是由业务代码直接维护缓存。
  • 读场景:先从缓存获取数据,如果没有命中,则回源到SoR并将源数据放入缓存供下次读取使用。
  • 写场景:先将数据写入SoR,写入成功后立即将数据同步写入缓存。或者先将数据写入SoR,写入成功后将缓存数据过期,下次读取时再加载缓存。
对于Cache-Aside可能存在并发更新情况,即如果多个应用实例同时更新,那么缓存怎么办?
● 如果是用户维度的数据(如订单数据、用户数据),则出现这种几率非常小,因为并发的情况很少,可以不考虑这个问题,加上过期时间来解决即可。
● 对于如商品这种基础数据,可以考虑使用canal订阅binlog进行增量更新分布式缓存,这样不会存在缓存数据不一致的情况,但是,缓存更新会存在延迟。而本地缓存根据不一致容忍度设置合理的过期时间。
● 读服务场景,可以考虑使用一致性哈希,将相同的操作负载均衡到同一个实例,从而减少并发几率。或者设置比较短的过期时间。
2) Cache-As-SoR
Cache-As-SoR即把Cache看作为SoR,所有操作都是对Cache进行,然后Cache再委托给SoR进行真实的读/写。即业务代码中只看到Cache的操作,看不到关于SoR相关的代码。有三种实现:read-through、write-through、write-behind。
  • read-through
Read-Through,业务代码首先调用Cache,如果Cache不命中由Cache回源到SoR,而不是业务代码(即由Cache读SoR)。使用Read-Through模式,需要配置一个CacheLoader组件用来回源到SoR加载源数据。Guava Cache和Ehcache 3.x都支持该模式。
  • Write-Through
Write-Through,称之为穿透写模式/直写模式,业务代码首先调用Cache写(新增/修改)数据,然后由Cache负责写缓存和写SoR,而不是业务代码。使用Write-Through模式需要配置一个CacheWriter组件用来回写SoR。Guava Cache没有提供支持。Ehcache 3.x支持该模式。Ehcache需要配置一个CacheLoaderWriter,CacheLoaderWriter知道如何去写SoR。当Cache需要写(新增/修改)数据时,首先调用CacheLoaderWriter来同步(立即)到SoR,成功后会更新缓存。
  • Write-Behind
Write-Behind,也叫Write-Back,称之为回写模式,不同于Write-Through是同步写SoR和Cache,Write-Behind是异步写。异步之后可以实现批量写、合并写、延时和限流。
3) Copy Pattern
有两种Copy Pattern,Copy-On-Read(在读时复制)和Copy-On-Write(在写时复制),对于Guava Cache和Ehcache中堆缓存都是基于引用的,这样如果有人拿到缓存数据并修改了它,则可能发生不可预测的问题,笔者就见过因为这种情况造成数据错误。Guava Cache没有提供支持,Ehcache 3.x提供了支持。
public interface Copier<T> { 
  TcopyForRead(T obj); //Copy-On-Read,比如myCache.get() 
   TcopyForWrite(T obj); //Copy-On-Write,比如myCache.put() 
通过如下方法来配置Key和Value的Copier。
CacheConfigurationBuilder.withKeyCopier() 
CacheConfigurationBuilder.withValueCopier() 

四、超时与重试机制
在整个项目里,需要注重每个环境设置超时与重试机制,主要有以下设置的环节。
  • 代理层超时与重试(Nginx)
  • Web容器超时
  • 中间件客户端超时与重试(Mq消息中间件)
  • 数据库客户端超时
  • NoSQL客户端超时
  • 业务超时
  • 前端Ajax超时

五、回滚机制
  • 事务回滚
  • 代码库回滚
  • 部署版本回滚
  • 数据版本回滚
  • 静态资源版本回滚
六、压力测试
  • 系统压测
1、线下压测
通过如Jmeter、Apache ab命令压测系统的某个接口或者某个组件。
如:ab -n 100 -c 10 http://www.baidu.com/
其中-n表示请求数,-c表示并发数
其余命令请参见 http://apache.jz123.cn/programs/ab.html
mysql压力测试 mysqlslap(模拟多个并发客户端访问MySQL来执行测试)
2、线上压测
参考:http://www.jianshu.com/p/d9ac47acfea9
  • 系统优化和容灾
参考:http://www.yunweipai.com/archives/10501.html
  • 应急预案
参考:http://www.yunweipai.com/archives/11187.html
七、数据库处理(主从复制、读写分离、分片)
提高对应网站高并发的主要集中在两处,web服务的高并发数据库高并发;应对web服务的高并发在前面所介绍的负载均衡技术,使用各种负载方式均发访问量到不同的服务器不失为良好的策略。数据库高并发同样可通过负载均衡得以缓解。
(1) 主从复制
在一个服务器集群中,尽可能的平均负载量。通常做法是在服务器前端设置一个负载均衡器(专门的硬件设备),mysql的负载均衡,通常都离不开数据分片(把数据分割成小块,存储到不同的db节点中)、复制等操作。

一、 mysql复制能解决什么问题?
1、数据分布
      在不同的地理位置来分配数据备份,产生各地数据中心。
2、负载均衡
      通过mysql复制可将读操作分布到多台服务器,实现对读高并发的应用优化。如何实现mysql负载均衡在下文中会进行介绍。
3、高可用和故障切换
     复制能避免mysql单点故障,在故障发生时,可执行切库操作缩短宕机时间。
二、mysql复制原理
1、主库将所有的数据操作记录在二进制日志文件中(Binary log)。
2、备库启动一个IO线程,将主库的二进制文件转储(binlog dump)到自己的中继日志中(Relay log)。
3、备库读取中继文件中的操作,执行结果放到备份服务器上。

注意:
1、数据一致性延迟问题:
     在同一时间,数据的一致性会有延迟,这取决于二进制文件的大小,一些大的语句可能会导致备库产生几秒、几分钟甚至几小时的延迟。
解决方案:
A、在所有的insert和update之后,强制sleep几秒钟。这是非常粗鲁的方式,对于更新操作不是很高的中小型系统,此方式基本能解决问题(或者是针对同一个用户的读操作,读取主数据库里,非自己的数据读取从数据库,如发表博客)。  
B、应用程序把被更新的数据保存在本机的内存(或者集中式缓存)中,如果在写入数据完成后需要直接读取数据,则从本机内存中读取。这种方式的缺点是极大的增加了应用程序的复杂度,而且可靠性并不能完全得到保障。 
C、(使用MySQL Proxy作为读写分离的情况下)在Master上增加一个自增表Count_table,这个表仅含有1个的字段。当Master接收到任何数据更新的请求时,均会触发这个触发器,该触发器更新自增表中的记录。如下图所示:  

由于Count_table也参与Mysq的主从同步,因此在Master上作的 Update更新也会同步到Slave上。当Client通过Proxy进行数据读取时,Proxy可以先向Master和Slave的 Count_table表发送查询请求,当二者的数据相同时,Proxy可以认定 Master和Slave的数据状态是一致的,然后把select请求发送到Slave服务器上,否则就发送到Master上。如下图所示:  

通过这种方式,就可以比较完美的结果MySQL的同步延迟不可控问题。之所以所“比较完美”,是因为这种方案double了查询请求,对Master和Slave构成了额外的压力。不过由于Proxy与真实的Mysql Server采用连接池的方式连接,因此额外的压力还是可以接受的。
2、mysql复制的版本问题:
     由于老版本可能无法解析新版本产生的二进制文件中采用的新特性或语法,所以一般使用同一版本做复制或老版本作为主库,新版本做备库。
三、文件演示
a.获取binlog文件列表
mysql>show binary logs;

b.查看当前正在写入的binlog文件mysql> show master status \g;
+-------------------+----------+--------------+------------------+-------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+-------------------+----------+--------------+------------------+-------------------+
| master-bin.000001 |358 | | | |
+-------------------+----------+--------------+------------------+-------------------+
1 row in set (0.00 sec)
c.查看指定binlog文件的内容语法:命令:SHOW BINLOG EVENTS [IN 'log_name'] [FROM pos] [LIMIT [offset,] row_count]
mysql>show binlog events in 'master-bin.000001' \G
*************************** 1. row ***************************
Log_name: master-bin.000001
Pos: 4
Event_type: Format_desc
Server_id: 11
End_log_pos: 120
Info: Server ver: 5.6.36-log, Binlog ver: 4
*************************** 2. row ***************************
Log_name: master-bin.000001
Pos: 120
Event_type: Query
Server_id: 11
End_log_pos: 214
Info: create database mydb
*************************** 3. row ***************************
Log_name: master-bin.000001
Pos: 214
Event_type: Query
Server_id: 11
End_log_pos: 358
Info: use `mydb`; CREATE TABLE `test` (
`id` int NULL ,
`name` varchar(255) NULL
)
3 rows in set (0.02 sec)

(2) 读写分离
一、Mysql实现负载均衡的方式
1、mysql读写分离:
       mysql复制时,产生了多个数据副本(备库),为减少服务器压力,备库用于处理读操作,主库可同时处理读写是mysql集群实现读写分离的常用策略。
      由于备库的复制是异步的,无法实时同步,读写分离的主要难点也在于备库上的脏数据。通常如果使用备库进行读,一般对数据的实时性要求不能太高。对此,mysql提供了几种常见的读写分离方式,例如基于查询的读写分离、基于脏数据、基于会话等。
    mysql设置的读写分离,减少了主库的请求量,将大量读的操作发送给备库,实现负载均衡。
2、修改DNS
     在之前详细介绍了DNS以及DNS如何实现负载,简言之,通过n个服务器IP指定到一个域名,根据请求的不同标识特征,将请求发送给不同的IP服务器进行处理。
3、引入中间件
       mysql官方提供了一个mysql负载的中间件,mysql_proxy,也需要在服务器上进行安装,修改配置文件(mysql的服务器IP),实质与nginx类似,也是一个代理服务器。
还有当当的Sharding-JDBC,Sharding-JDBC定位为轻量级java框架,使用客户端直连数据库,以jar包形式提供服务,未使用中间层,无需额外部署,无其他依赖,DBA也无需改变原有的运维方式。SQL解析使用Druid解析器,是目前性能最高的SQL解析器。
二、负载均衡实战
下面以spring aop中动态代理的方式实现数据库读写分离(目前只配置了一个主和一个从)。
代码:略......
使用Druid的配置可以查询数据链接信息:http://localhost:8080/rwdb/druid/index.html(账户密码均为默认为druid
数据库并发配置
  • 查看mysql数据库的服务器的最大连接数
show variables like 'max_connections';(查看目前的最大连接数)
show global status like 'Max_used_connections';(查看数据库历史出现的最大连接)
  • 修改mysql配置文件my.cnf
max_connections = 3000
尽量保持 Max_used_connections/max_connections = 85%左右
  • 修改程序中数据源的配置
<property name="maxActive" value="500"/>
<property name="maxIdle" value="10"/>
 <property name="initialSize" value="10"/>

(3) 分库分表分区(分片)
数据库层的负载均衡,其实是软件方面的主从分表和硬件方面的分表分区(分成多个小的文件)。
日常开发中我们经常会遇到大表的情况,所谓的大表是指存储了百万级乃至千万级条记录的表。这样的表过于庞大,导致数据库在查询和插入的时候耗时太长,性能低下,如果涉及联合查询的情况,性能会更加糟糕。分表和表分区的目的就是减少数据库的负担,提高数据库的效率,通常点来讲就是提高表的增删改查效率。
...............

八、网站服务流量监控
1、服务监控(Apache web、mysql数据库监控,如druid检测工具、磁盘空间监控)
2、流量监控(网站流量监控)
3、邮件报警(Linux邮件服务系统)
  • Postfix发邮件系统(常用,默认25端口)
  • sendmail发邮件系统
  • devecot收邮件系统
最终由流量监控+脚本(shell或者php)发送邮件

原创粉丝点击