优点:考虑了服务器处理能力的不同
-
sh 原地址散列:提取用户IP,根据散列函数得出一个key,再根据静态映射表,查出对应的value,即目标服务器IP。如果目标机器超负荷,则返回空。
-
dh 目标地址散列:同上,只是现在用提取的是目标地址的IP来做哈希。
优点:以上两种算法都能实现同一个用户访问同一个服务器。
- lc 最少连接。优先把请求转发给连接数少的服务器。
优点:使得集群中各个服务器的负载更加均匀。
- wlc 加权最少连接。在lc的基础上,为每台服务器加上权值。算法为:(活动连接数*256+非活动连接数)÷权重 ,计算出来的值小的服务器优先被选择。
优点:可以根据服务器的能力分配请求。
-
sed 最短期望延迟。其实sed跟wlc类似,区别是不考虑非活动连接数。算法为:(活动连接数+1)*256÷权重,同样计算出来的值小的服务器优先被选择。
-
nq 永不排队。改进的sed算法。我们想一下什么情况下才能“永不排队”,那就是服务器的连接数为0的时候,那么假如有服务器连接数为0,均衡器直接把请求转发给它,无需经过sed的计算。
-
LBLC 基于局部性的最少连接。均衡器根据请求的目的IP地址,找出该IP地址最近被使用的服务器,把请求转发之;若该服务器超载,最采用最少连接数算法。
-
LBLCR 带复制的基于局部性的最少连接。均衡器根据请求的目的IP地址,找出该IP地址最近使用的“服务器组”。注意,并不是具体某个服务器,然后采用最少连接数从该组中挑出具体的某台服务器出来,把请求转发之。若该服务器超载,那么根据最少连接数算法,在集群的非本服务器组的服务器中,找出一台服务器出来,加入本服务器组,然后把请求转发之。
-
第三个问题是集群模式问题,一般3种解决方案:
-
NAT:负载均衡器接收用户的请求,转发给具体服务器,服务器处理完请求返回给均衡器,均衡器再重新返回给用户。
-
DR:负载均衡器接收用户的请求,转发给具体服务器,服务器处理完请求后直接返回给用户。需要系统支持IP Tunneling协议,难以跨平台。
-
TUN:同上,但无需IP Tunneling协议,跨平台性好,大部分系统都可以支持。
-
第四个问题是session问题,一般有以下4种解决方案:
-
Session Sticky。session sticky就是把同一个用户在某一个会话中的请求,都分配到固定的某一台服务器中,这样我们就不需要解决跨服务器的session问题了,常见的算法有ip_hash法,即上面提到的两种散列算法。
优点:实现简单。
缺点:应用服务器重启则session消失。
- Session Replication。session replication就是在集群中复制session,使得每个服务器都保存有全部用户的session数据。
优点:减轻负载均衡服务器的压力,不需要实现ip_hasp算法来转发请求。
缺点:复制时宽带开销大,访问量大的话session占用内存大且浪费。
- Session数据集中存储:session数据集中存储就是利用数据库来存储session数据,实现了session和应用服务器的解耦。
优点:相比session replication的方案,集群间对于宽带和内存的压力减少了很多。
缺点:需要维护存储session的数据库。
- Cookie Base:cookie base就是把session存在cookie中,有浏览器来告诉应用服务器我的session是什么,同样实现了session和应用服务器的解耦。
优点:实现简单,基本免维护。
缺点:cookie长度限制,安全性低,宽带消耗。
值得一提的是:
-
nginx目前支持的负载均衡算法有wrr、sh(支持一致性哈希)、fair(本人觉得可以归结为lc)。但nginx作为均衡器的话,还可以一同作为静态资源服务器。
-
keepalived+ipvsadm比较强大,目前支持的算法有:rr、wrr、lc、wlc、lblc、sh、dh
-
keepalived支持集群模式有:NAT、DR、TUN
-
nginx本身并没有提供session同步的解决方案,而apache则提供了session共享的支持。
好了,解决了以上的问题之后,系统的结构如下:
上面我们总是假设数据库负载正常,但随着访问量的的提高,数据库的负载也在慢慢增大。那么可能有人马上就想到跟应用服务器一样,把数据库一份为二再负载均衡即可。
但对于数据库来说,并没有那么简单。假如我们简单的把数据库一分为二,然后对于数据库的请求,分别负载到A机器和B机器,那么显而易见会造成两台数据库数据不统一的问题。那么对于这种情况,我们可以先考虑使用读写分离的方式。
读写分离后的数据库系统结构如下:
这个结构变化后也会带来两个问题:
-
主从数据库之间数据同步问题
-
应用对于数据源的选择问题
解决问题方案:
-
我们可以使用MYSQL自带的master+slave的方式实现主从复制。
-
采用第三方数据库中间件,例如mycat。mycat是从cobar发展而来的,而cobar是阿里开源的数据库中间件,后来停止开发。mycat是国内比较好的mysql开源数据库分库分表中间件。
数据库做读库的话,常常对模糊查找力不从心,即使做了读写分离,这个问题还未能解决。以我们所举的交易网站为例,发布的商品存储在数据库中,用户最常使用的功能就是查找商品,尤其是根据商品的标题来查找对应的商品。对于这种需求,一般我们都是通过like功能来实现的,但是这种方式的代价非常大。此时我们可以使用搜索引擎的倒排索引来完成。
搜索引擎具有以下优点:
- 它能够大大提高查询速度。
引入搜索引擎后也会带来以下的开销:
-
带来大量的维护工作,我们需要自己实现索引的构建过程,设计全量/增加的构建方式来应对非实时与实时的查询需求。
-
需要维护搜索引擎集群
-
搜索引擎并不能替代数据库,他解决了某些场景下的“读”的问题,是否引入搜索引擎,需要综合考虑整个系统的需求。
引入搜索引擎后的系统结构如下:
1、后台应用层和数据库层的缓存
随着访问量的增加,逐渐出现了许多用户访问同一部分内容的情况,对于这些比较热门的内容,没必要每次都从数据库读取。我们可以使用缓存技术,例如可以使用google的开源缓存技术guava或者使用memcacahe作为应用层的缓存,也可以使用redis作为数据库层的缓存。
另外,在某些场景下,关系型数据库并不是很适合,例如我想做一个“每日输入密码错误次数限制”的功能,思路大概是在用户登录时,如果登录错误,则记录下该用户的IP和错误次数,那么这个数据要放在哪里呢?
假如放在内存中,那么显然会占用太大的内容;假如放在关系型数据库中,那么既要建立数据库表,还要简历对应的java bean,还要写SQL等等。而分析一下我们要存储的数据,无非就是类似{ip:errorNumber}这样的key:value数据。对于这种数据,我们可以用NOSQL数据库来代替传统的关系型数据库。
2、页面缓存
除了数据缓存,还有页面缓存。比如使用HTML5的localstroage或者cookie。
优点:
-
减轻数据库的压力
-
大幅度提高访问速度
缺点:
-
需要维护缓存服务器
-
提高了编码的复杂性
值得一提的是:
缓存集群的调度算法不同与上面提到的应用服务器和数据库。最好采用“一致性哈希算法”,这样才能提高命中率。这个就不展开讲了,有兴趣的可以查阅相关资料。
加入缓存后的结构:
我们的网站演进到现在,交易、商品、用户的数据都还在同一个数据库中。尽管采取了增加缓存,读写分离的方式,但随着数据库的压力继续增加,数据库的瓶颈越来越突出,此时,我们可以有数据垂直拆分和水平拆分两种选择。
7.1、数据垂直拆分
垂直拆分的意思是把数据库中不同的业务数据拆分道不同的数据库中,结合现在的例子,就是把交易、商品、用户的数据分开。
优点:
-
解决了原来把所有业务放在一个数据库中的压力问题。
-
可以根据业务的特点进行更多的优化
缺点:
- 需要维护多个数据库
问题:
-
需要考虑原来跨业务的事务
-
跨数据库的join
解决问题方案:
-
我们应该在应用层尽量避免跨数据库的事物,如果非要跨数据库,尽量在代码中控制。
-
我们可以通过第三方应用来解决,如上面提到的mycat,mycat提供了丰富的跨库join方案,详情可参考mycat官方文档。
垂直拆分后的结构如下:
7.2、数据水平拆分
数据水平拆分就是把同一个表中的数据拆分到两个甚至多个数据库中。产生数据水平拆分的原因是某个业务的数据量或者更新量到达了单个数据库的瓶颈,这时就可以把这个表拆分到两个或更多个数据库中。
优点:
- 如果我们能客服以上问题,那么我们将能够很好地对数据量及写入量增长的情况。
问题:
-
访问用户信息的应用系统需要解决SQL路由的问题,因为现在用户信息分在了两个数据库中,需要在进行数据操作时了解需要操作的数据在哪里。
-
主键的处理也变得不同,例如原来自增字段,现在不能简单地继续使用了。
-
如果需要分页,就麻烦了。
解决问题方案:
-
我们还是可以通过可以解决第三方中间件,如mycat。mycat可以通过SQL解析模块对我们的SQL进行解析,再根据我们的配置,把请求转发到具体的某个数据库。
-
我们可以通过UUID保证唯一或自定义ID方案来解决。
-
mycat也提供了丰富的分页查询方案,比如先从每个数据库做分页查询,再合并数据做一次分页查询等等。
数据水平拆分后的结构:
8.1、拆分应用
随着业务的发展,业务越来越多,应用越来越大。我们需要考虑如何避免让应用越来越臃肿。这就需要把应用拆开,从一个应用变为俩个甚至更多。还是以我们上面的例子,我们可以把用户、商品、交易拆分开。变成“用户、商品”和“用户,交易”两个子系统。
拆分后的结构:
HTTP
-
HTTP 报文结构是怎样的?
-
HTTP有哪些请求方法?
-
GET 和 POST 有什么区别?
-
如何理解 URI?
-
如何理解 HTTP 状态码?
-
简要概括一下 HTTP 的特点?HTTP 有哪些缺点?
-
对 Accept 系列字段了解多少?
-
对于定长和不定长的数据,HTTP 是怎么传输的?
-
HTTP 如何处理大文件的传输?
-
HTTP 中如何处理表单数据的提交?
-
HTTP1.1 如何解决 HTTP 的队头阻塞问题?
-
对 Cookie 了解多少?
-
如何理解 HTTP 代理?
-
如何理解 HTTP 缓存及缓存代理?
-
为什么产生代理缓存?
-
源服务器的缓存控制
-
客户端的缓存控制
-
什么是跨域?浏览器如何拦截响应?如何解决?