文章目录
降低数据库压力的主要方式
讨论不基于缓存等方面的使用,单纯从数据库出发。
1.读写分离,一般一主n从(n>=1)。降低了读的压力,但是存储压力并未降低
2.分库分表,降低了读压力,降低存储压力
读写分离
虽然降低了读的压力。但是也引入了新的问题(主从数据同步必定有延时)。
举一个极端例子:
小明注册(写入主库),然后去登录(读取从库),由于延迟,从库没有小明导致登录失败。
常见的解决方法:
1.实效性要求高(根据业务)的直接读主库,比如小明登录就读取主库
2.二次读取(需要封装),当从库没有则去读主库。不建议,浪费资源
分库分表
当数据库数据达到几千万条后,数据库会出现瓶颈:
1.数据量大,索引文件大,导致读写性能下降
2.备份,恢复时间长
3.一旦出现极端情况,丢失数据量大(你就一个库,比如硬盘损坏修不好的那种,那数据就全丢了)
分库分表也引入了新的问题:
1.join问题,同一个库里直接join就行了
2.事务问题,需要一些分布式事务的解决方案
3.成本的增加。原先一台mysql就行,分完需要n台
注意事项:
请不要一上来就分库分表。因为10个业务能有一个活下去就不错了。如果发展的很好,数据库出现瓶颈,再分库也行,你都发展的这么好了。叫公司追加研发投入,顺便顺带连微服务一起做了。
我们之前的电子健康档案就是当数据量突破了5000万条才做的分库分表+微服务。
垂直分表
这个是我们在数据库设计中比较常用的。
比如说商品有商品详情,是富文本(可能使用text这种比较大的类型存储),我们生成订单时查询订单信息,不需要富文本,如果将其分到其它表中,那么订单表的数据变小,导致检索变快
水平分表
假设我现在有一亿个用户,用户id:1~1000000000
水平分表本质是将原本在一个表中的数据行,分布到其它表中(和原始表结构相同)
按照范围分
比如说1000万一个表,我创建10个表(user_1,user_2…)
优点:表好扩充
缺点:数据分布不均,我按照900万一个表,第11张表就10万数据。
hash分表
通常使用 user_id % 基数(表数量,这里假设10张表),那么id=1的用户进user_1,id=2的用户进user_2。
优点:数据均匀
缺点:不好扩充,一般都采用成倍扩充。
hash分表成倍扩充
假设有100000000数据,之前有10张表。
id=19的进user_9。
后进行表扩容,增加10张,总共20张,那么id=19的用户应该在user_19中。
这里增加10张表不是随便增加的。而是按照user_19的表数据拷贝user_9的数据(其余新增的表都按照这种方式)。
然后再执行脚本剔除不应该属于本表的数据(比如:user_9中剔除id % 20 != 9 的数据)
映射
就是通过映射表维护数据,告诉因该去操作那张表
但是不推荐,因为映射数据多了也存在性能瓶颈
水平分表带来的复杂性
合并问题
弄个极端例子:查1亿条数据
原先:一张表一条sql搞定
分表后:查10张表后union
join问题
和合并差不多,都是由于要查询的数据分布在不同的表中造成的。
将原有的join变成多张表join+union
排序问题
来,咱们按照用户年龄排序取年龄最大的10条。
需要将所有表union生成临时表后进行order by(不建议,消耗大)
这种数据量的查询都会带上查询条件。
比如查询姓名都为张三的用户,按照其年龄倒序。
这里union完后,可以在程序中进行排序输出
count 问题
这个也是由于分散在不同表导致的数量统计问题
多个表count相加,串行消耗比较久。
解决方案:增加一张表(记录数表)来统计(表名,数量)
优点:是查询快,从这张表里查
缺点:
1.需要识别哪些操作要修改表记录数(xx表的新增,xx表的删除),漏掉一个就会导致数量不正确
2.需要事务去保证数量的准确性
如果不是要求实时精确的,可以通过定时脚本去统计更新到记录数表。
读写分离实现方式
代码层面封装
像Laravel框架就是封装在代码层面(orm),设置读库,从库
优点:
1.简单,容易扩展
缺点:
1.主从切换后不能主动将连接配置改到新主库上
2.如果换到其它语言也需要重新实现一遍封装的逻辑
中间件
优点:
1.不止能实现读写分离,主从切换,健康检查,还能处理分库分表的查询
2.兼容sql协议,使其能够跨语言
缺点:
1.实现复杂
2.所有sql都通过中间件,要承受访问压力