分库分表
描述: 分库分表是一种水平数据拆分,会按照如ID,用户,时间等维度进行数据拆分,拆分算法可以使取模,hash,区间或者使用数据路由表等。 问题: 跨库跨表join,排序分页,自增ID,分布式事务等问题 解决方案: 对于跨库跨表join和排序分页:可以对所有表进行扫描然后做聚合,或者生成全局表、进行查询维度的数据异构(比如,订单库按照查询维度异构出商家订单库,用户订单库),再或者将数据同步到ES搜索。 自增ID问题可以通过分布式ID生成器解决。尽量避免分布式事务。
ObjectId 规则
[0,1,2,3] [4,5,6] [7,8] [9,10,11] 时间戳 |机器码 |PID |计数器 前四位是时间戳,可以提供秒级别的唯一性。 接下来三位是所在主机的唯一标识符,通常是机器主机名的散列值。 接下来两位是产生ObjectId的PID,确保同一台机器上并发产生的ObjectId是唯一的。 前九位保证了同一秒钟不同机器的不同进程产生的ObjectId时唯一的。 最后三位是自增计数器,确保相同进程同一秒钟产生的ObjectId是唯一的。 https://github.com/qianjiahao/MongoDB/wiki/MongoDB%E4%B9%8B_id%E7%94%9F%E6%88%90%E8%A7%84%E5%88%99
选择合适的分布式主键方案
- 数据库自增长序列或字段
- UUID
- 使用UUID to Int64的方法
- Redis生成ID
- Twitter的snowflake算法
- 利用zookeeper生成唯一ID
- MongoDB的ObjectId
因此,业务设计上,如果找到一条降低一致性要求时,还能保证业务的正确性的业务分拆之路。举个例子,火车票查询时,不要显示多少张,而是显示“有”或“无”,或者显示>100张,50~100,小于50等,这样就可以减小状态的更新频率,充分使用缓存数据。
说说分库与分表设计
垂直分表在日常开发和设计中比较常见,通俗的说法叫做“大表拆小表”,拆分是基于关系型数据库中的“列”(字段)进行的。通常情况,某个表中的字段比较多,可以新建立一张“扩展表”,将不经常使用或者长度较大的字段拆分出去放到“扩展表”中。在字段很多的情况下,拆分开确实更便于开发和维护(笔者曾见过某个遗留系统中,一个大表中包含100多列的)。某种意义上也能避免“跨页”的问题(MySQL、MSSQL底层都是通过“数据页”来存储的,“跨页”问题可能会造成额外的性能开销,拆分字段的操作建议在数据库设计阶段就做好。如果是在发展过程中拆分,则需要改写以前的查询语句,会额外带来一定的成本和风险,建议谨慎。 垂直分库在“微服务”盛行的今天已经非常普及了。基本的思路就是按照业务模块来划分出不同的数据库,而不是像早期一样将所有的数据表都放到同一个数据库中。系统层面的“服务化”拆分操作,能够解决业务系统层面的耦合和性能瓶颈,有利于系统的扩展维护。而数据库层面的拆分,道理也是相通的。与服务的“治理”和“降级”机制类似,我们也能对不同业务类型的数据进行“分级”管理、维护、监控、扩展等。 众所周知,数据库往往最容易成为应用系统的瓶颈,而数据库本身属于“有状态”的,相对于Web和应用服务器来讲,是比较难实现“横向扩展”的。数据库的连接资源比较宝贵且单机处理能力也有限,在高并发场景下,垂直分库一定程度上能够突破IO、连接数及单机硬件资源的瓶颈,是大型分布式系统中优化数据库架构的重要手段。 然后,很多人并没有从根本上搞清楚为什么要拆分,也没有掌握拆分的原则和技巧,只是一味的模仿大厂的做法。导致拆分后遇到很多问题(例如:跨库join,分布式事务等)。 水平分表也称为横向分表,比较容易理解,就是将表中不同的数据行按照一定规律分布到不同的数据库表中(这些表保存在同一个数据库中),这样来降低单表数据量,优化查询性能。最常见的方式就是通过主键或者时间等字段进行Hash和取模后拆分。水平分表,能够降低单表的数据量,一定程度上可以缓解查询性能瓶颈。但本质上这些表还保存在同一个库中,所以库级别还是会有IO瓶颈。所以,一般不建议采用这种做法。 水平分库分表与上面讲到的水平分表的思想相同,唯一不同的就是将这些拆分出来的表保存在不同的数据中。这也是很多大型互联网公司所选择的做法。某种意义上来讲,有些系统中使用的“冷热数据分离”(将一些使用较少的历史数据迁移到其他的数据库中。而在业务功能上,通常默认只提供热点数据的查询),也是类似的实践。在高并发和海量数据的场景下,分库分表能够有效缓解单机和单库的性能瓶颈和压力,突破IO、连接数、硬件资源的瓶颈。当然,投入的硬件成本也会更高。同时,这也会带来一些复杂的技术问题和挑战(例如:跨分片的复杂查询,跨分片事务等)。
以上摘抄自: http://www.infoq.com/cn/articles/key-steps-and-likely-problems-of-split-table
分库与分表带来的分布式困境与应对之策
数据迁移与扩容问题 前面介绍到水平分表策略归纳总结为随机分表和连续分表两种情况。连续分表有可能存在数据热点的问题,有些表可能会被频繁地查询从而造成较大压力,热数据的表就成为了整个库的瓶颈,而有些表可能存的是历史数据,很少需要被查询到。连续分表的另外一个好处在于比较容易,不需要考虑迁移旧的数据,只需要添加分表就可以自动扩容。随机分表的数据相对比较均匀,不容易出现热点和并发访问的瓶颈。但是,分表扩展需要迁移旧的数据。 针对于水平分表的设计至关重要,需要评估中短期内业务的增长速度,对当前的数据量进行容量规划,综合成本因素,推算出大概需要多少分片。对于数据迁移的问题,一般做法是通过程序先读出数据,然后按照指定的分表策略再将数据写入到各个分表中。 表关联问题 在单库单表的情况下,联合查询是非常容易的。但是,随着分库与分表的演变,联合查询就遇到跨库关联和跨表关系问题。在设计之初就应该尽量避免联合查询,可以通过程序中进行拼装,或者通过反范式化设计进行规避。 分页与排序问题 一般情况下,列表分页时需要按照指定字段进行排序。在单库单表的情况下,分页和排序也是非常容易的。但是,随着分库与分表的演变,也会遇到跨库排序和跨表排序问题。为了最终结果的准确性,需要在不同的分表中将数据进行排序并返回,并将不同分表返回的结果集进行汇总和再次排序,最后再返回给用户。 分布式事务问题 随着分库与分表的演变,一定会遇到分布式事务问题,那么如何保证数据的一致性就成为一个必须面对的问题。目前,分布式事务并没有很好的解决方案,难以满足数据强一致性,一般情况下,使存储数据尽可能达到用户一致,保证系统经过一段较短的时间的自我恢复和修正,数据最终达到一致。 分布式全局唯一ID 在单库单表的情况下,直接使用数据库自增特性来生成主键ID,这样确实比较简单。在分库分表的环境中,数据分布在不同的分表上,不能再借助数据库自增长特性。需要使用全局唯一 ID,例如 UUID、GUID等。关于如何选择合适的全局唯一 ID,我会在后面的章节中进行介绍。
摘抄自:http://blog.csdn.net/jiangpingjiangping/article/details/78069480
主从复制/读写分离
- master数据库将变更记录写入binlog。
- slave数据库订阅binlog日志,通过io线程从binlog的指定位置拉去日志进行主从同步,此时master会有一个Binlog Dump线程来读取binlog日志与slave io线程进行数据同步。
- slave io线程读取到日志后会先写入relay log重放日志中。
- slave数据库会通过一个sql线程读取relay log进行日志重放,这样就实现了主从数据库之间的同步。
- 读slave,写master.