已文大神可以略过。
当一台服务器已不能满足日益膨胀的站点时,就应该对数据库进行扩展。
一、主从复制
几乎所有的主流数据都支持复制,在mysql中,配置如下
1、开户服务器上的二进制日志(log-bin)
2、在主服务器和从服务器上分别进行简单的配置和授权。
具体可参看官方手册。
MySQL支持单向、异步复制,复制过程中一个服务器充当主服务器,而一个或多个其它服务器充当从服务器。主服务器将更新写入二进制日志文件,并维护文件的一个索引以跟踪日志循环。这些日志可以记录发送到从服务器的更新。当一个从服务器连接主服务器时,它通知主服务器从服务器在日志中读取的最后一次成功更新的位置。从服务器接收从那时起发生的任何更新,然后封锁并等待主服务器通知新的更新。
注意:
1、从服务器本身也可以充当主服务器,从而可以构成链式复制服务器,采用多级复制策略,避免多个从服务器同时复制一个主服务器带来的磁盘压力。
2、当你进行复制时,所有对复制中的表的更新都必须在主服务器上进行,否则可能会造成主从数据的冲突。
二、读写分离
当主服务器的压力不断增大时,由于更新操作必须在主服务器上进行,因此我们应当尽量让主服务器不做读操作,以减轻它的压力,读操作就交给从服务器。
那么问题来了,程序如何决定使用哪台从服务器进行read呢?这可以看作是一个从服务器的负载均衡的操作,程序必须合理地发送不同的SQL到从服务器,让它们的工作量相对均衡,更重要的是,如果某台服务器发生了故障,程序得知道并做出相应的操作。这样程序代码看来需要修改很多,且实现不简单,幸运的是,我们可以将这工作交给数据库反向代理。
针对mysql可以尝试使用mysql proxy,它就像WEB反向代理服务器一样,可以在SQL语句转发到后端的mysql服务器之前对它进行修改,比如将对数据库a的操作修改为数据库b,更厉害的是,mysql proxy还对可以多个从服务器实现负载均衡以及可用性检测。
三、垂直分区
对于数据库写操作频繁的站点来说,仅仅采用主从和读写分离并不能对主服务器负载有多少影响。这个时候最简单的方法就是将数据根据业务不相关性进行垂直分区。比如一个商城可以将商品数据库和用户数据库分别存储到独立的数据库服务器上。
四、水平分区
现在我们的商城越来越大,用户越来越多,发现用户服务器虽然独立一台服务器也有点吃不消了,用户太多了。这时,我们不得不对其进行水平分区。当然在此之前,经常会先尝试进行分表,将用户表按照取余法分为10个或者100个用户表,如果这样仍然不能满足站点的需要,分区便派上用场。注意这里的分区,并不是partition,而是分布式分区。
那么应该如何分区呢?
首先我们得先确定分区索引字段,比如userId,它必须和所有的记录都存在关系。如果使用主键,你得保证它不能使用auto_increment自增类型,接下来要考虑就是采用什么分区算法了,这个算法必须拥有良好的扩展性,并且让各个分区的工作量相对均衡。
1、哈希算法
前面提到的userid%10取余,是最简单的哈希,它可以对分区均衡地分配工作量。但是它对于扩展并不友好。一旦我们需要从10个分区扩展到20个,这便需要将所有数据重新分区,你不得不暂停站点,waiting。
2、范围
这种算法是指按照分区索引字段的范围进行分区,比如将userId0-100000的记录存储在一个分区中,将100000-200000存储在另一个分区中,以此类推。显然这样扩展性很好,但是各个分区的工作量会存在较大的差异,比如老用户所在的分区压力相对较大,或者一部分ID比较靠近的热点用户导致所在的分区压力过大。
3、映射关系
这种算法将对分区索引字段的每个可能的结果创建一个分区映射关系,可以想像,userId有多少,那么映射关系就有多少,因此需要将它写入到数据库,通过查询数据库得到用户的分区,当然,我们会使用缓存来提高性能。
这样映射关系,使得各个分区具有较强的可伸缩性,我们可以灵活地控制它们的规模,并且轻松地将数据从一个分区迁移到另一个分区,这也使得各个分区通过灵活的动态调节来保持平衡。
幸运的是分区也可以反向代理,在读写分离时提到了mysql proxy,另一个开源产品spock proxy可以帮助应用程序实现水平分区的访问调度,这意味着我们不需要在应用程序中维护那些分区的对应关系了。