在日常开发任务中,经常会遇到单张表过1千万,以每天n万条的速度进行递增。sql查询效率下降,前端业务表现为用户操作缓慢,如查sql查询速度超过1秒或者更长,会发生一条sql把整个数据库连接占满,用户看到的就是白页面或报错的页面。
mysql数据库存储是系统最后一道护城河,以最谨慎的态度对待。系统就像一个成长的小树,慢慢的长大,每一天的成长都需要不停的修剪、不停的优化。
关于大表的优化常见的思路就是分表、分库、数据分区,降低单表容量,提高单表查询速度。可是细细想来,优化不仅仅是分表分库简单的操作,而是站在系统设计的角度去考虑。
秩序是自由的第一条件
1 数据量过千万的不同场景
1)数据量千万级,随着时间快速增长。
这样的表大多是订单表、日志表。过亿只是时间问题。
2)数据量千万级,相对变化缓慢
例如用户表在增长的过程中一般不会爆炸式的增长。
3)审视业务
虽然有过千万的数据,这些数据是否都合理,是否要长期维护。与产品业务进 行沟通数据的存在合理性。
2 优化
1)规范设计
2)业务层优化
3)架构层优化
4)数据库优化
5)管理优化
1)规范设计
做java程序开发一般都会参考阿里编程规范,一个技术团队统一的编码规范、编码样式可以有效的规避很多不必要的技术坑。但是数据库是在开发、生产、运维中最重要的一个环节,很多时候忽略了对数据库的重视程度。数据库的设计规范与java编码规范同等重要。
配置规范
(1)具有事务型的产品,数据库存储引擎统一使用InnoDB
(2)创建操作系统、数据库、表统一使用UTF8,jdbc连接字符集、各展示层统一使用UTF8,避免了很多乱码的问题。
(3)数据库单表容量合理的规划、建议单表容量在2000万内。如果超过2000万就要考虑优化数据库架构、程序逻辑。
(4)单库数据表容量不要超过500个,表通过前缀的方式区分业务
建表规范
(1)InnoDB不能使用外键约束,java在逻辑层进行关联。
(2)存储浮点数使用decimal,不要使用float double。
(3)尽量不要使用text、blob。
(4)字段设置not null ,为字段定义默认值。
(5)建立sql上线规范,dba终审上线脚本。
命名规范
(1)库名、表名、字段名、索引名统一小写,单词之间用下划线分割。
(2)对象命名规范
视图
view
view_
函数
function
func_
存储过程
procedure
proc_
触发器
trigger
trig_
普通索引
index
idx_
唯一索引
unique index
uniq_
主键索引
primary key
pk_
索引规范
(1)索引建议命名规则:idx_col1_col2[_colN]、uniq_col1_col2[_colN](如果字段过长建议采用缩写)。
(2)索引中的字段数建议不超过5个。
(3)单张表的索引个数控制在5个以内。
(4)InnoDB表一般都建议有主键列,尤其在高可用集群方案中是作为必须项的。
注:主键设计,如果后续分库、分表不建立使用自增id、建立全局生成唯一id。
建立微服务,统一生成。
(5)建立复合索引时,优先将选择性高的字段放在前面。
(6)UPDATE、DELETE语句需要根据WHERE条件添加索引。
(7)不建议使用%前缀模糊查询,例如LIKE “%weibo”,无法用到索引,会导致全表扫描。
(8)合理利用覆盖索引,例如:
SELECT email,uid FROM user_email WHERE uid=xx,如果uid不是主键,可 以创建覆盖索引idx_uid_email(uid,email)来提高查询效率。
(9)避免在索引字段上使用函数,否则会导致查询时索引失效。
(10)上线前要经过dba评审
2)业务优化
业务拆分
数据拆分
读多写少优化
写多读少优化
业务拆分
业务拆分同样表述为垂直拆分、根据业务把业务表拆分到不同的数据库。例如新浪微博,用户基础信息库、微博信息库、评论库。
通过拆分数据库让业务数据更清晰,同类的数据在同一个库,同时提高整个系统的并发度。上层业务代码层,通过建立用户服务、与各业务微服务建立通信,让各系统间通过rpc进行调用。
数据拆分
1.按日期拆分
按日期进行拆分是比较常见的一种拆分方式,例如把订单历史数据拆分为,当天表、当月表、再按月进行分表
t_order_day
t_order_202010
t_order_202009
如此拆表会产生另外一个程序编写的问题,有可能用户要跨月进行查询订单,一个季度要跨3张表进行查询,可以通过在java层聚合数据。
再深入一点的思考,如果用户有可能在09 07 03这三张表里有数据,其他表里没有数据,就有可能让程序发空的sql进行扫描。可以维护一个用户订单数据在哪张历史表的缓存表,在用户登陆的时候异步把数据缓存到redis中,在查询的时候直接就可以知道去哪些历史表中查询。
2.读多写少优化
增加redis缓存,把数据同步到redis,让请求命中到redis中
3. 写多读少优化
异步提交、消息队列、批量写入
异步提交,可以通过多线程的方式处理任务,即时给客户端响应。
消息队列,可以使用jvm消息队列,分布式消息队列。
批量写入,需要处理的请求一瞬间暴增的时候,可以在消息队列的消费者累计1000 或一定数量的记录,进行batch批量更新。
3)架构优化
系统水平扩展
离线统计业务
1)系统水平扩展
水平扩展采用中间件mycat、如果在阿里云可以采用成熟度比较高的polar-X
中间件的核心就是计算层、存储层分离,把表的数据根据一定的规则,分散到不同的数据库不同的表中,让数据表保持很小的轻量级的运行。
2)离线统计业务
当数据上亿时,同时使用mysql做线上数据库,又做统计数据库就不适合了。
方案1:
同步数据,从库进行只读查询,单独的数据库做统计查询。
方案2:
当数据库再大的时候在mysql统计起来就非常吃力了,就需要把数据同步到hbase中
阿里云MaxCompute配合DataWorks,使用超级舒服,按量付费,成本极低。
MaxCompute可以理解为开源的Hive,提供sql/mapreduce/ai算法/python脚本/shell 脚本等方式操作数据,数据以表格的形式展现,以分布式方式存储,采用定时任务和批处理的方式处理数据DataWorks提供了一种工作流的方式管理你的数据处理任务 和调度监控。
以上所有需要在具体业务中,应对变化,像修剪小树一样照顾应用系统。
千万级大表的优化是根据业务场景,以成本为代价进行优化的,绝对不是孤立的一个 层面的优化。