数据库演化

读写分离

即利用大多主流数据库都具有的“主从热备”功能,主库可同步数据到从库,故,写数据到主库,从库进行读数据,另外,应使用专门的数据访问模块进行数据操作,使数据库的读写分离对应用透明

业务分库

将不同业务的数据库部署在不同的物理服务器上

分布式数据库

数据库拆分的最后手段,使用多台服务器进行数据库集群,一般在单表数据非常大的情况下才进行

应用系统共有业务抽离

单独用一个系统负责共有业务,以减少数据库连接数目,然后通过分布式服务调用共有业务

反向代理与nginx

nginx支持负载均衡和反向代理:

  • 例如tomcat进行集群,可使用server节点配置多tomcat访问的负载均衡,如:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    upstream tomcatserver {
    server 192.168.72.49:8082;
    server 192.168.72.49:8081;
    }
    server {
    listen 80;
    server_name localhost;
    main;
    location / {
    proxy_pass http://tomcatserver;
    index index.html index.htm;
    }
    }
  • 反向代理一般用于加速网站响应(除此之外,还可使用CDN进行加速),即当用户访问网站服务时,如果nginx缓存有用户请求的资源,则将其直接返回给用户。
    要想开启nginx的缓存功能,需要添加此处的两行内容:

    1
    2
    3
    4
    #这一行分别表示:定义缓存存储目录,手动创建;缓存级别,表示缓存目录的第一级目录是1个字符,第二级目录是2个字符;内核中建立用于缓存缓存数据源数据的空间,查找缓存的时候,先从这个内核空间中找到,缓存数据的源数据,然后再到对应目录中查找缓存;这一行分别表示:缓存空间最大值;缓存的数据,60分钟内没有被访问过就删除 
    proxy_cache_path /var/www/cache levels=1:2 keys_zone=mycache:20m max_size=2048m inactive=60m;
    #这一行分别表示:创建缓存的时候可能生成一些临时文件存放的位置,自动创建
    proxy_temp_path /var/www/cache/tmp;

分布式消息队列

定义

在单一服务器内部可通过多线程共享内存队列的方式实现异步,处在业务操作前面的线程将输出写入到队列,后面的线程从队列中读取数据进行处理;在分布式系统中,多个服务器集群通过分布式消息队列实现异步,分布式消息队列可以看作是内存队列的分布式部署。

作用

  • 提高系统可用性
    消费者服务器发生故障是,数据会在消息队列服务器中存储堆积,生产者服务器可以继续处理业务请求,系统整体表现无故障。消费者服务器恢复正常后,继续处理消息队列的数据。

  • 加快网站响应速度
    处在业务处理前端的生产者服务器在处理完业务请求后,将数据写入消息队列,不需要等待消费者服务器处理就可以返回,相应延迟减少。

  • 消除并发访问高峰
    用户访问网站是随机的,存在访问高峰和低谷,即使网站按照一般访问高峰进行规划和部署,也依然会出现突发事件,比如购物网站的促销活动,微博上的热点事件,都会造成网站并发访问突然增大,这可能会造成整个网站负载过重,相应延迟,严重时甚至会出现服务宕机的情况。使用消息队列将突然增加的访问请求数据放入消息队列中,等待消费者服务器依次处理,就不会对整个网站负载造成太大压力。

优化网站性能方案

浏览器端

  • 使用浏览器缓存
  • 使用页面压缩
  • 合理布局页面
  • 减少cookie传输

动静分离

  • 使用CDN将静态内容分发到离用户最近的网络服务商机房
  • 在中心机房部署反向代理服务器,缓存热点文件

应用服务器端

  • 使用分布式缓存
  • 使用本地缓存

其他

  • 异步:使用消息队列支持异步操作,即将用户请求发送到消息队列等待后续任务处理,而当前请求直接响应给用户
  • 集群:对于高并发请求,将多台应用服务器组成集群共同对外访问注意集群下,尽量不将重要的会话数据保存在当前服务器中,而是分布式缓存等地方,为了防止发生宕机,会话数据丢失

代码层面

  • 使用多线程
  • 改善内存管理

数据库服务器端

  • 数据库:索引、缓存、SQL优化等
  • NOSQL优化数据模型、存储结构、伸缩特性等

可用性

即一般的7*24小时可用,主流网站一般满足4个9以上可用性(即99.99%),即叫做高可用;2个9是基本可用;3个9为较高可用;5个9为极高可用。实现高可用的主要手段即是冗余:服务器集群,数据热备,主从替换

伸缩性

大型网站需要面对大量的用户的高并发访问和存储海量数据,不可能只用一台服务器就处理全部用户请求,存储全部数据,故出现了集群。而所谓伸缩性即通过不断向集群中加入服务器的手段来缓解不断上升的用户并发访问压力和不断增长的数据存储需求。

衡量伸缩性的主要标准即是是否可以用多台服务器构建集群,是否容易向集群中间添加新的服务器。加入新的服务器是否可以提供和原来的服务器无差别的服务,集群中可容纳的总服务器数量是否有限制。

实现伸缩性:

  • 对于应用服务器集群:不在服务器上保存数据(本地缓存),那么所用服务器都是对等的,即具有良好伸缩性。
  • 对于缓存服务器集群:加入新的服务器可能导致缓存路由失效,进而导致集群中的大部分缓存数据无法访问。可通过改进路由算法保证缓存数据的可访问性,即采用一致性hash算法,继续升级:将物理节点分解成多个虚拟节点来进行新增节点操作以保证较均匀地影响原来集群中的服务器。
  • 对于数据库集群:关系型数据库虽支持数据复制,朱从热备,但很难做到可伸缩性,因此只能从数据库外部来实现,通过路由分区等方法将部署多个数据库的服务器组成一个集群。

扩展性

衡量扩展性的主要标准即是网站增加新的业务产品时,是否可以实现对现有产品透明无影响,不同产品之间耦合度很少,一个产品的改动对另一个产品无影响。

实现扩展性:

  • 事件驱动架构:在网站通常利用消息队列实现,将用户请求和其他业务事件构造成消息发布到消息队列中,消息的处理者作为消费者从消费队列中获取消息进行处理。通过这种方式将消息产生和消息处理分离开来,可以透明地增加新的消息生产者任务或者新的消息消费者任务。
  • 分布式服务:即将业务和可复用服务分离开来,通过分布式服务框架调用。新增产品可以通过调用可复用的服务实现自身的业务逻辑,而对现有的产品没有任何影响。可复用服务升级变更的时候,也可以通过提供多版本服务对应用实现透明升级,不需要强制应用同步变更。

网站优化手段

前端

  • 优化页面html式样
  • 利用浏览器的并发和异步特性
  • 减少http请求
  • 启用压缩,HTML、CSS、Javascript文件启用GZip压缩
  • Css放页面最上面,Javascript放最下面
  • 减少Cookie传输
  • 浏览器缓存策略
  • 使用CDN、反向代理

后端

缓存

  1. 频繁修改的数据:读写比在2:1以上,缓存才有意义,即一次写入,再次写入前应至少读取两次
  2. 没有热点的访问:如果访问数据不遵循二八定律,缓存没有意义
  3. 数据不一致与脏读:两种策略,一是设置过期时间,合理过期;二是立即更新缓存
  4. 缓存可用性:缓存宕机,对数据库造成冲击,需要注意。对此,有缓存热备和分布式缓存,前者宕机切换到另一台缓存服务器不推荐,后者建立集群
  5. 缓存预热:新启动的缓存系统如果没有热点数据,应在启动时就将热点数据加载好,这种预加载手段即缓存预热
  6. 缓存穿透:对前端高并发请求不存在的数据,也应自动加到缓存中(其值可以是null),防止对数据库造成压力。
  7. 常见架构:一是以jboss cache为代表的更新同步缓存,每台应用服务器本机都对应有一个jboss cache,各个jboss cache相互更新同步数据;而是以memcached为代表的互不通讯缓存,集群下的mencached共同组成一个大缓存。

集群

使用负载均衡进行集群

异步操作

即使用消息队列,前端的请求发送到应用服务器,服务器再发送到消息队列服务器进行一一处理,形象说就是前端用户排队做的事情(即存在等待),全部交给后端来进行排队,然后前端即时返回消息。

代码优化

  1. 使用多线程
    1. 原因:等待IO操作时,cpu可以做其他事;多cpu情况下,想要最大限度使用cpu,必须启动多线程
    2. 启动线程数=[任务执行时间/(任务执行时间-IO等待时间)]xCPU内核数
      故使用场景在于IO耗时长、CPU内核多的情况
    3. 解决线程安全:将对象设计成无状态对象、使用局部对象(方法内部对象)、并发访问时使用锁
  2. 资源复用

    减少开销很大的资源创建和销毁,如:数据库连接、网络通信连接、线程、复杂对象,故,常用两种方式:单例和对象池,具体如:Service到Dao的单例、连接池、线程池

  3. 数据结构
  4. 垃圾回收

    Young Generation(Eden+From+To)+Old Geneartion
    Eden区创建对象,区满则触发Young GC,Eden区中仍在被使用的对象复制到From区,再次区满,Eden区+From区仍在被使用的对象复制到To区,再次区满,则Eden区和To区仍在被使用对象复制到From区,以此类推。当超过某个阈值对象还未被释放,将复制到Old Generation。如果Old Generation区满,进行Full GC(全量回收,影响性能),应尽量减少Full GC

存储性能优化

  • 机械硬盘 vs 固态硬盘
    机械硬盘对于随机访问时,移动磁头臂次数很多,性能不强;固态硬盘(SSD或Flash硬盘)使用可持久记忆的硅晶体存储,可像内存一样快速随机访问,故SSD性能更佳

  • B+树 vs LSM数
    为保证数据在不断更新、插入、删除后依然有序,传统关系数据库使用B+树,目前许多NoSQL产品使用LSM树,LSM树性能更佳,可以让关系数据库使用LSM数

Session管理

  1. Session复制
    应用服务器开启web容器的Session复制功能,每台服务器同步session数据,大规模下占用服务器和网络大量资源,故适用于小规模集群

  2. Session绑定
    负载均衡服务器总是将同一IP的请求分发给同一台服务器,如nginx负载均衡的ip_hash策略。如果用户IP变化将可能失去Session,故可使用cookie标识以取代IP(此时负载均衡服务器应工作于http协议层)。但是如果服务器宕机,该机存储的session也还是会消失,故高可用要求下,不推荐

  3. cookie保存session
    每次请求时,cookie携带session进行请求,缺点:一是cookie大小可能受限制,二是携带session影响性能,三是用户可能关闭cookie。故也不推荐

  4. session服务器推荐
    按照需求划分两种:一是基于分布式缓存或数据库进行包装,使其满足session存储和访问要求,二是开发专门的session服务平台,用于单点登录等。

负载均衡实现方式

  1. HTTP重定向实现负载均衡
    用户请求到达http重定向服务器(负载均衡器),该服务器计算得到需要访问的真实目标服务器,并通过302状态码返回给用户进行重定向。缺点是客户端需要两次请求才能完成操作,性能较差,且302重定向可能导致搜索引擎判定为SEO作弊,降低搜索排名。

  2. DNS域名解析实现负载均衡
    在DNS配置中(如阿里控制台),同一域名配置多个A记录(A记录即不同目标服务器IP),每次域名解析都会根据负载均衡算法计算一个不同的真实目标IP地址返回给客户端。缺点在于修改A记录(如有服务器下线时),由于缓存原因,生效时间较长(几分钟),并且DSN控制权在域名服务商那里,无法进行更好的改善和管理。

  3. 反向代理实现负载均衡
    反向代理多用于进行网站动静分离,对一些静态资源进行反向代理,而无需再访问web服务器进行拿取,提高响应速度。而反向代理也可用做实现负载均衡,即把“动”的东西进行反向代理。
    如apache、nginx等反向代理服务器都提供负载均衡功能:管理一组web服务器,将请求根据负载均衡算法转发到不同的web服务器上。缺点是所有请求和响应都要经过反向代理服务器,性能成为瓶颈(应用层负载均衡)

  4. IP实现负载均衡
    核心即操作数据包,修改目标IP地址,甚至源地址。请求到达负载均衡服务器时,修改请求数据包的目标IP为通过计算得到的真实目标服务器。另外,将源地址由客户端ip修改为负载均衡服务器IP,这样数据响应将回到负载均衡服务器,继而返回给客户端(或者让负载均衡服务器作为真实目标服务器集群的网关服务器,这样所有响应数据也都会到达负载均衡服务器)

  5. 数据链路层实现负载均衡
    类似IP实现负载均衡,不过此处是在分发过程中不修改IP地址,而是mac地址,不过此处需要配置真实目标服务器集群的所有机器虚拟IP和负载均衡服务器IP一致,这样不需要再通过负载均衡服务器进行地址转换,便可将响应数据直接由真实目标服务器返回给客户端,形成一个三角传输模式,这样,数据响应不经过负载均衡服务器,网络带宽也就将不成为瓶颈。这也是目前大型网站使用最广的一种负载均衡手段,开源产品有:LVS