在一个网站访问量大的时候,数据库资源是最宝贵的资源。一般网站来讲,数据库能够承受的并发请求,要远远小于网站页面能够承受的并发请求,因为数据库有大量的IO操作,一台数据库能够应对的连接数,要远远小于IIS或者Tomcat能够应对的连接数。这就是为什么要使用数据库连接池,如果有1000个请求到达了IIS,而数据库连接池的大小只有10个连接,那么这1000个请求就要排队,从连接池中拿到连接,然后从数据库中取得数据。
在高并发情况下,为什么要做读写分离?
首先,读写分离可以增加数据库的个数,相当于对数据库的水平扩展。当网站有1000个请求的时候,可以设置一个主库,9个从库,这样,一台数据库就能处理100个请求。
其次,根据二八原则,网站中有80%的请求是查询的,只有20%是更改数据的。如果在SqlServer数据库的话,事务隔离级别为read-commited. 就是说在一个事务中有对某一行数据的update操作,就会产生排它锁,这种隔离级别下,其他事务是没法读这行数据的,只能等待。所以如果更改与查询都在一个数据库的话,更新操作可能阻塞查询操作。
其次,为了提高查询的效率,会建各种的索引,如果查询语句比较复杂的话,索引会非常庞大。而更新一个数据库表的时候,同时会维护相关的索引,这是非常占用资源的。
如何设计数据库的读写分离?
读写分离的前提是要根据业务的需求。如果业务需求要求实时性不高,最简单的方式就是设置两个不同的数据库,一个数据库为主库,另一个数据库为从库。然后用一个定时的任务,间隔一定的时间,从主库中抓取数据,同步到从库中。然后在网站中代码逻辑用来控制,哪些请求是到主库的,哪些请求是到从库的。假设现在是一个金融公司,隔天要出前一天的报表,那么就可以设置一个同步的任务,可以是C#编写的windows service,也可以是java写的scheduler,凌晨数据库访问量低的时候,从主库拷贝数据到从库。如果网站中要求实时性很高的请求,比如支付结果的查询,那么在代码中找到这块逻辑,直接让它访问主表。这是一个特殊的查询场景,特殊对待,给它访问主库。
如果业务场景合适,也可以使用SQL Server的订阅功能,完成读写分离。其实现的是,它会把主数据库的任何操作,同步到从数据库中。比如主数据库为A,从数据库为B,C. 那么对A的增删改任何操作,都会通过发布中心,同步到B,C,会有一定的延迟。在网站的代码中,就可以根据业务逻辑,对于每一个的数据库请求,都去决定,是要访问A,还是B,还是C。这样就把读写分离了。
数据库的负载均衡也是一种选择。Moebius是SQL Server的负载均衡系统。当有更新请求的时候,这个操作会分发到每一台数据库。这样所有数据库都拥有最新的数据。而有查询操作的时候,它会根据策略转发到一台数据库。这与发布订阅不同的地方在于,没有了数据库间的同步,而是由Moebius转发所有更新操作到所有数据库。