读写分离
适用于写少读多的场景(博客、朋友圈等,单机并发无法支撑且读请求更多的情形),可以将访问压力分散到集群中的多个节点(但没有分散存储压力)
架构
主从复制(一主一从,一主多从),主负责写,从负责读;
问题
数据复制延迟(1秒到1分钟都有可能)
即数据写入到主服务器后,立刻通过从服务器进行读取,此时主机可能还没有将数据复制到从机(1秒内或大数据量数据同步),导致读取从机时读取不到最新的数据;
解决方法:
(1)写操作后的读操作发送给数据库主服务器;
(2)读从失败后再读一次主机(二次读取);
(3)关键业务读写操作全部指向主服务器,非关键业务采用读写分离;
分配机制
(1)程序代码封装(程序自己封装数据访问层)
淘宝TDDL(Taobao Distributed Data Layer,外号:头都大了)
(2)中间件封装(数据库代理,客户端无需改动)
MySQL proxy, MySQL Router(Mysql官方推荐),360 Atals(基于MySQL Proxy)、Mycat
思考
若能真正做到读写分离,可单独对读库进行索引优化和readonly设置,写库上减少索引(索引对查询有优化但影响写入速度);
若数据库读取速度变慢,应该先考虑:
(1)Sql查询优化(索引优化)、代码优化(减少不必要的查询、减少多次查询(使用join));
(2)考虑使用缓存(例如Redis),若业务过于复杂,可按照2-8原则(最重要的80%只占20%)优先处理这重要的20%;
(3)数据库配置调优;
若以上方式都解决不了,再去考虑读写分离;
分库
随着业务的发展,数据量越来越大(千万甚至上亿条),单台数据库服务器的存储能力(10万用户量级)已无法支撑(性能瓶颈,读写性能下降,数据量太大、索引太大、备份耗时、丢失风险大),需要通过分库(或分表)将存储分散到多台数据库服务器上;
架构
按照业务模块将数据(table)分散到不同的数据库服务器(即将不同业务的表分开放到不同的服务器上);
问题
(1)程序需要控制对不同数据库的访问(多个数据源);
(2)join问题,因为table分散到不同数据库,所以无法做join查询,需要多次查询不同的数据库后对数据进行组合(可先执行返回结果最少的查询,后再根据查询结果继续查询其他数据库,减少每次查询的参数大小(进而减少每次查询的结果集大小));
(3)无法保证事务,需要程序自己控制(回滚);
(4)成本问题,需要添加服务器(不适用初创公司,可日后业务发展大好之后再考虑分库);
分表
业务继续发展,即使分库(百万或千万规模)以后,同一业务的单表数据量的不断增加也会成为单台数据服务器的处理瓶颈;
此时就需要对单表数据进行拆分(垂直分表、水平分表),示意图如下:
垂直分表
适合将不常用且占用大量空间的列拆分出去,
水平分表
适合单表行数特别大(当看到单表数据量超过千万级别,就需要警觉),将一个表拆分成多个表(多个表定义完全相同),以提升访问性能;
问题:
路由:哪条数据属于拆分后的哪个子表,需要增加路由算法进行计算;
(1)范围路由:建议100万到2000万作为分段大小(需结合具体业务),需考虑ID生成策略;
(2)Hash路由:单列(或多列)hash值运算(例如user_id%5),然后根据hash结果分散到不同数据表中;
(3)配置路由表;
join操作:需join查询多次后合并结果;
count操作:多次count操作 或 记录数表(单建一张表,来维护其他每张表(每次新增、修改、删除后更新)的记录数);
order by操作:多次查询子表数据后汇总排序;
以上分库分表操作,均需要通过程序代码封装或中间件封装来实现;