假设有这样一个需求:
建设一个类似51.com的社交网站,大概能承载亿级用户,每天大概有百万级活跃用户访问。一期至少主要包括用户注册、认证,用户间相互添加好友关系,
可以建立朋友圈/分组功能。此外,还提供站内信功能,可以分享短篇文字,展示照片,可以对朋友的照片点赞等功能。分享的时候,可以设定可见朋友圈
对象,哪些朋友(分组)可见,哪些朋友(分组)不可见。
用户总量1亿
月均活跃用户量 3000万
日活跃用户峰值100万
数据库QPS约2万
100万人同时在线,峰值每用户触发2000次数据库request, 1000000*2000/86400 = 23148
数据表至少10个,库总量约1T
通行证
• 用户资料,行均1K,100G
• 认证,行均100字节,10G
用户关系
• 好友,行均100字节,10G
• 分组,行均100字节,10G
• 黑名单,行均100字节,10G
消息中心
• 收发消息,正反向2个表,行均1K,100G
• 点赞,行均50字节,5G
个人空间
• 图片,行均100字节,10G
• 微博短消息,行均1K,100G
历史操作日志
• 保守估计每天产生各种操作记录至少1000万,行均100字节,每天新增1G
方案选择关键点
• 单机 vs 多机
• 单实例 vs 多实例
• 单表 vs 多表
• 单节点 vs 高可用
上面这个业务,技术方案的难点
• 单实例负载高,发生宕机时,若备用节点数据未预热,引发雪崩效应;
• 高并发时,简单的SQL(count + 1,或者更新标记位),都可能引发负载飙升,大量锁等待;
• 主库压力太大,从库无法及时跟上主库,发生故障时,无法直接快速切换;
• 数据量大,不适合在主库上实时备份;
• 数据量大,不适合在线实施DDL变更;
为什么要拆分
• 单实例/单库/单表上并发大
• 单实例/单库/单表物理文件大
• 单表大,在线DDL无法容忍
• 出现性能瓶颈
• 单节点出现抖动不稳定现象,影响全局
• 充分利用机器的资源(MySQL利用CPU多核的特点决定)
垂直拆分的原则
1、 根据业务规划,区分不同功能模块;
2、 按不同功能划分数据库/表,必要时,可进行拆分和冗余;
3、 同时,也可能不完全纯粹垂直拆分,可以把几个功能中压力不均的库表放在同一个实例中,提高资源利用率。后期数据库出现性能瓶颈时,再将这些功能模块数据库表再次垂直拆分出去。
垂直拆分的优点
• 分而治之,精细化开发,程序&业务模块解耦
• 应用程序模块清晰,容易定位
• 数据库的拆分简单,容易理解
• 个别功能模块出现问题时,不容易影响全局
垂直拆分的缺点
• 多个功能之间不能进行join,需要靠程序/中间件完成
• 需要考虑在每个功能DB前引入数据访问层,基于API对外提供服务,这样以后每个功能的DB拆分也可以做到对外透明
• 事务控制需要借助于消息队列进行控制(程序层减依赖)
• 对于功能中特别大的表还有可能存在性能瓶颈
• 过渡拆分会造成管理复杂
水平拆分的原则:
(垂直拆分完毕后,或者未拆分时)单表读写压力还是太大,或者单表数据量太大,不利于日常管理和业务扩展,这时候就需要考虑进行水平拆分了。
水平拆分优点:
• 同一个功能内的关连,事务操作都可以进行
• 不会存在超大规模的表
• 应用程序端改动比较小
• 拆分规则设计的好的情况,较难触到性能瓶颈,也比较易扩展
水平拆分的缺点:
• 数据分散,group by ,order by , sum, count等排序,聚集函数不能支持,如果一定需要,需要某一个地方存全量冗余数据/统计数据
• 分片过多,会造成维护难度增加,比较定位(要求分区尽可能的简单)
• 后期迁移、扩容、增加节点较复杂
• 分表的方式需要比较综合权衡考虑,避免对后期再次扩展的影响
user_id%16--(00-15)
group_id hash(user_id)
msg_id range
分布式数据库设计方案介绍
类似用户类数据,可以对user_id按hash或是range进行拆分
拆分的路由规策,可以只细分到库级别,也可细分到表级别,或者同时细分到库和表
例如:根据分区键 user_id 拆分,计划拆分为16个子节点
- 只细分到库级别,例如根据user_id%16=N,对应到N个分库上
- 只细分到表级别,例如根据user_id%100=N,对应到N个分表上
- 如果需要同时分库分表,例如先分10个库,每个库再分10个表,共100个分表,可以先根据 user_id&get;&get;1%10=N分成N个库再根据user_id%10=N分成N个表。
例如,user_id = 12345,则最终定位到上user_DB_02.user_table_05
(12345&get;&get;1)%10 =&get; 2 --位移运算
12345%10 =&get; 5
• 有些情况下,可以基于时间维度进行拆分,例如日志归档类数据,或者冷热数据分离
• 在处理中可以考虑分为,每天一表,每10天一表,月度表,季度表,半年表,年表,历史归档表
redis相比memcached:
优点
1、redis具有持久化机制,可以定期将内存中的数据持久化到硬盘上。
2、redis具备类似binlog功能,可以将所有操作写入日志,当redis出现故障,可依照binlog进行数据恢复
3、redis原生支持的数据类型更多,使用的想象空间更大。
总体来讲,TPS方面redis要高于mongodb
mongodb支持丰富的数据表达,索引,最类似关系型数据库,支持的查询语言非常丰富
适合redis、memcached的场景:
1、频繁重复的简单,数据变更很少的SQL
2、计数器类统计,每次++;
3、缓存一些不经常变化的数据;
4、频繁数据库查询结果缓存;
5、业务端临时处理结果,汇总后批量回写;
6、空结果缓存,避免数据库雪崩;
利用Redis为MySQL减压
- string类型,计数器 count incr 1,快速计数,缓解update压力
- hash类型,K-V数据缓存,user_N-&get;{id:1,name:yejr,age:25},缓解读压力
- list类型,pop/push,可作为消息队列,或者取最新TOP N数据,缓解读压力
- set类型,无需唯一集合,去重操作,缓解读压力
- sort set类型,有序唯一集合,可以作为排行榜用途,缓解分组统计读压力
利用mongodb为MySQL减压
- 非严格schema场景下,甚至可以取代MySQL,例如一些小游戏
- 长记录cache,例如用户数据
- 大尺寸、低价值数据写入、分析,例如操作日志
- 需要快速扩展计算节点
- 分布式运算任务