第4章 存储高性能
4.1 关系数据库
4.1.1 读写分离
基本实现
- 数据库服务器搭建主从集群,一主一从、一主多从都可以
- 主机负责读写,从机值负责读
- 主机通过复制将数据同步到从机
- 业务服务器将写操作发给主机,读操作发给从机
读写分离需要应对复制延带来的复杂性,以Mysql为例,主从复制延迟可能达到1s,常用方法如下
- 写操作后的读操作指定发给数据库主机
- 读从机失败后再读一次主机
- 关键业务读写操作全部指向主机,非关键业务采用读写分离
4.1.2 分库分表
数据量过大会使单台服务器的存储能力成为系统瓶颈
- 数据量太大,读写性能下降,及时有索引,索引也会变得很大,性能下降
- 数据文件会变得很大,数据库备份和恢复需要耗费很长时间
- 数据文件越大,极端情况下丢数据的风险越高
常见分库分表的方案如下
方案一. 分库
业务分库指按业务模块将数据进行拆分。例如将一个电商系统的数据库,拆分为用户、商品、订单三台数据库
虽然业务分库能分散存储和访问压力,但同时带来新的问题
- 无法像单库进行方便的join操作
- 事务问题
- 成本问题 占用的服务器数量上升
方案二. 分表
a. 垂直分表
垂直分表适合将表中某些不常用且占了大量空间的列拆分出去。
b. 水平分表
水平分表适合行数特别大的表,如果单表行数超过5000万(该数字可做参考)就必须分表。 水平分表会引入很多复杂性,主要表现在
- 路由 ,需要增加路由算法计算某条数据具体属于哪个切分后的子表
- 范围路由, 例如按照用户id进行分段,以1~1000w为一张子表
- 优点:可以随数据增加而平滑地扩充新表
- 缺点:数据分布不均匀
- Hash路由 根据某个列的值进行Hash运算,根据结果分散到不同的表中
- 优点:表分布比较均匀
- 缺点:扩充新表很麻烦,所有数据都要重分布
- 配置路由 用一张独立的路由表来记录记录的分布信息
- 优点:设计简单,扩充表灵活
- 缺点:必须多查询一次,影响性能;路由表过大也会成为性能瓶颈
- 范围路由, 例如按照用户id进行分段,以1~1000w为一张子表
- join操作 需要在代码中进行多次join,然后结果合并
- count()操作
- count()相加 在代码中处理
- 记录数表
- 排序操作 只能在代码中处理
4.1.3 实现方法
可应用Sharding-JDBC方便程序进行处理
4.2 NoSql
关系数据库缺点
- 存储的是行记录,无法存储数据结构
- schema扩展很不方便
- 大数据场景下I/O较高
- 全文搜索功能比较弱
4.2.1 K-V存储
解决关系数据库无法存储数据结构问题,以Redis为代表
4.2.2 文档数据库
最大的特点是no-schema,可以存储和读取任意的数据,目前绝大部分文档数据库存储的数据格式是JSON。 文档数据库并不能完全取代关系数据库,更多时候是作为补充。
文档数据库优势
- 新增字段简单
- 历史数据不会出错
- 可以很容易存储复杂数据
主要缺点
- 不支持事务
- 无法实现关系数据库的join操作
4.2.3 列式数据库
列式数据库是按照列来存储数据的数据库,传统关系数据库是按行来存储数据的。
列式数据库优势
- 业务同时读取多个列时效率高
- 能够一次性完成对一行中多个列的写操作,保证了针对行数据写操作的原子性和一致性
- 压缩率高,节省存储空间
缺点
- 随机写效率远低于行式存储的效率
- 高压缩率情况下数据更新效率低
一般列式存储应用在离线大数据分析和统计场景中,因为这种场景主要针对部分列进行操作,且 数据写入后就无须再更新删除
4.2.4 全文搜索引擎
基本原理为倒排索引,典型代表ES
4.3 缓存
缓存的基本原理就是将可能重复使用的数据放到内存中,一次生成,多次使用,避免每次使用都去访问存储系统
4.3.1 缓存穿透
指缓存没有发挥作用,业务系统虽然去缓存中查询数据,但缓存中没有数据,系统需要再次去存储系统中查询。 通常有2中情况
- 存储数据不存在
通常情况下,业务上读取不存在数据的请求量不会太大,如果出现恶意访问情况,可以直接设置一个 默认值(可以是空值)存入缓存中。
- 生成缓存数据需要耗费大量时间或资源
优化系统效率,识别恶意访问并禁止
4.3.1 缓存雪崩
缓存雪崩指当缓存大规模失效后引起系统性能急剧下降的情况。常见解决方案有
- 更新锁
对魂村更新操作进行加锁保护,保证只有一个线程能进行缓存更新,未能获取更新锁的线程要么等待 锁释放后重新读取缓存,要么返回空值或默认值
- 后台更新
由后台线程来更新缓存,而不是由业务线程来更新缓存,缓存本身的有限期设置为永久。
4.3.3 缓存热点
缓存热点的解决方案就是复制多分缓存,将请求分散到多个缓存服务器上,减轻缓存热点导致 的单台缓存服务器的压力
本文由博客一文多发平台 OpenWrite 发布!