数据库的危机
应用程序访问数据库需要与数据库建立连接,每一个访问都需要维持一个连接,每建立一个新的连接都需要开辟一个缓冲区(内存)用来读取以及处理数据,所以数据库能够维持的连接数总是有限的。当用户量爆发增长时,数据库的性能也就出现了瓶颈,怎么处理?
解:
增加数据库服务器的内存,开辟更多的连接(治标不治本,内存的占用还是会到达瓶颈)
将保存数据的机械硬盘换成SSD,提高每个连接的处理速度(SSD的成本太高,无法大规模使用)
在应用程序与数据库之间增加一个中间层(缓存),根据局部性原理,将数据库常用的数据放在缓存中,应用程序先访问缓存查询,没有找到就再查数据库,同时也把查到的数据放在缓存中,以便加快下一次的访问速度。
上面的处理引入了新的问题:之前的应用程序与数据库是通过JDBC接口连接的,那么新增加的缓存层该怎么实现数据的访问?
解:将缓存和应用程序放在同一台主机上,缓存直接保存应用程序中的对象。
随着用户量的继续增大,应用程序这台主机也处理不过来,罢工了,怎么办?
解:拆分:web服务器独用一台主机,应用程序N台主机,缓存一台主机,数据库一台主机
上面的所引入的新问题:缓存和应用程序不在同一台主机上,怎样保存数据到缓存?
解:Reids(高效的键值存储数据库),将java对象转成json格式通过jedis客服端保存到redis服务器
随着用户量的继续增大,缓存服务器也扛不住了,怎么办?
解:多增加几台缓存服务器(质量不够,数量来凑)
上面的解引入新的问题:
这么多缓存服务器,每个 Redis 存的数据都不一样,对于应用程序来说,每次向 Redis 中存放数据的时候,到底选哪个?是存到 1 号缓存服务器,还是 2 号、 3 号?
假设已经存到了 1 号Redis,等到取数据的时候,还得去 1 号 Redis 找,要不然都找不到。
数据存储也要尽可能均匀,别让 1号、 2 号缓存服务器由于存储太多而“撑死”,而 3 号缓存因为没有数据而“饿死”。
解:
余数算法:对于用户要存储的(key,value),计算key的整数Hash值,用Hash值对缓存服务器数目求余数,对在余数号服务器进行存储。要取数的时候也用同样的方式找到那台服务器进行取数。
但是余数算法面对一个新问题时就会导致大量缓存失效:在系统运行时动态的增减缓存服务器,因为服务器的数目变了。
一致性Hash算法
假设有一个圆,在上面映射2^32-1个点;
根据服务器的IP值,用Hash函数求出一个整数值映射到圆上相应的点上;
将要存储的(key,value)的key值,同样用Hash函数取整数,映射到圆上,再顺时针找到遇见的第一个服务器,就是其存储服务器;当然,取数的时候用同样的方式找到服务器
当遇到增加一台服务器时,失效的只有两台服务器之间的缓存数据;
但是这样会导致如果服务器分布不均匀,那么就会出现某些服务器负载过高的情况(key值的映射概率问题)。
解决措施:我们需要足够多的服务器来让服务器更均匀的分布在圆上(当服务器数量越多时,出现大的不均匀的概率越小),但实际上不可能有太多的服务器,并且服务器的数量也不定。所以我们可以抽象出许多虚拟服务器(将一台服务器映射出N台虚拟服务器),再把这些虚拟服务器映射到圆上。
Hash槽算法
假设设定16384个槽,每个服务器均匀分管部分数目的槽;
将key值用CRC16算法(跟Hash函数原理一样的)产生一个整数映射到槽上,进行存储。
当新增一个服务器时,将其它服务器所管理的槽各自分出一些交给新的服务器管理,以保证均匀性。
当根据一个key值查找数据时,可以向任一服务器发出请求,如果没有就重定向到其它的服务器,直到查到为止(这一块涉及到redis之间的通信,也叫redis集群)
集群中的故障转移问题
当涉及到集群时,就能够对一个老问题(数据安全问题)有了比较好的解决方案。原理就是当一台服务器挂掉之后,选一台备份服务器顶上。示例如下:将16384个Hash槽由两组服务器管理(当然也可以是多组服务器),每组服务器由一个master节点N个salve节点组成,其中master节点负责处理查询请求,salve节点负责同步数据进行备份。每一个节点都是一台独立的主机,甚至主从节点如果有条件的话应该分布在不同的机房,以应对有可能整个机房全部挂掉的风险(一般是电力风险),当master节点挂掉之后,在备份组里面选一台salve节点顶上。当然这一块实现起来就涉及到Redis集群的通信问题,服务器在运行时的动态增减问题,数据的备份与故障转移等问题。
Nginx高可用
Nginx是负责处理http请求的web服务器,当访问量达到一定程度时,服务器挂掉怎么办?(也叫单点故障)
解:用两台服务器,一主一从,对外只提供一个IP,当主服务器挂掉后从服务器顶上,具体的实现可以用Keepalived等类似的软件。
tomcat高可用
tomcat是负责逻辑处理的应用服务器,当处理量达到一定程度时,服务器挂掉怎么办?
解:用多台服务器协同处理,为了保证负载均衡,Nginx需要将请求均匀的转发到tomcat服务器上;
引入的新问题:tomcat是需要保存服务状态的,如果一台服务器在处理用户的过程中挂掉了,那么请求就会转移到其它服务器,但是其它服务器又没有该用户的状态信息,这又该如何处理?
解:
各台应用服务器之间进行通信交流,以保证session在各台服务器时刻保持一致(通信成本太高,效率低)
把session集中保存在数据库中,每台应用服务器都能访问查询(数据库的处理速度太慢)
把session集中保存在Redis缓存中
数据库高可用
数据库服务器是最重要的,所有用户的数据都能够永久性的在数据库中固化下来,但是在在一个分布式的环境中,保持数据的强一致性是非常难的,稍有不慎就会乱套,特别是金融数据。怎么处理这个问题?
解:数据库有个特点就是统计出来的结果,读数据的次数远远高于写数据的次数。所以可以设计一主多从的架构,一台master数据库主要负责写数据(当然也可以读数据),多台从机负责读数据;
优点:
能够极大的缓解程序对X锁与S锁的争用(这块后面再深挖)
能够平衡多数据库的负责均衡,提高可用性
在主机挂掉之后,从机成为主机顶上
缺点:
程序对数据库的访问就变得有些麻烦,怎样分离读写操作去访问不同的服务器?
对该缺点的解决措施:再抽象出一个中间层来处理双方产生的矛盾,如mysql proxy(这块后面深挖)