MySQL单表数据量过千万,采坑优化记录,完美解决⽅案
问题概述
使⽤阿⾥云rds for MySQL数据库(就是MySQL5.6版本),有个⽤户上⽹记录表6个⽉的数据量近2000万,保留最近⼀年的数据量达到4000万,查询速度极慢,⽇常卡死。严重影响业务。
问题前提:⽼系统,当时设计系统的⼈⼤概是⼤学没毕业,表设计和sql语句写的不仅仅是垃圾,简直⽆法直视。原开发⼈员都已离职,到我来维护,这就是传说中的维护不了就跑路,然后我就是掉坑的那个!!!
我尝试解决该问题,so,有个这个⽇志。
⽅案概述
⽅案⼀:优化现有mysql数据库。优点:不影响现有业务,源程序不需要修改代码,成本最低。缺点:有优化瓶颈,数据量过亿就玩完了。
⽅案⼆:升级数据库类型,换⼀种100%兼容mysql的数据库。优点:不影响现有业务,源程序不需要修改代码,你⼏乎不需要做任何操作就能提升数据库性能,缺点:多花钱
⽅案三:⼀步到位,⼤数据解决⽅案,更换newsql/nosql数据库。优点:没有数据容量瓶颈,缺点:需要修改源程序代码,影响业务,总成本最⾼。
以上三种⽅案,按顺序使⽤即可,数据量在亿级别⼀下的没必要换nosql,开发成本太⾼。三种⽅案我都试了⼀遍,⽽且都形成了落地解决⽅案。该过程⼼中慰问跑路的那⼏个开发者⼀万遍 :)
⽅案⼀详细说明:优化现有mysql数据库
跟阿⾥云数据库⼤佬电话沟通 and Google解决⽅案 and 问⾥⼤佬,总结如下(都是精华):
1.数据库设计和表创建时就要考虑性能
2.sql的编写需要注意优化
4.分区
4.分表
5.分库
1.数据库设计和表创建时就要考虑性能
mysql数据库本⾝⾼度灵活,造成性能不⾜,严重依赖开发⼈员能⼒。也就是说开发⼈员能⼒⾼,则mysql性能⾼。这也是很多关系型数据库的通病,所以公司的dba通常⼯资巨⾼。
设计表时要注意:
表字段避免null值出现,null值很难查询优化且占⽤额外的索引空间,推荐默认数字0代替null。
尽量使⽤INT⽽⾮BIGINT,如果⾮负则加上UNSIGNED(这样数值容量会扩⼤⼀倍),当然能使⽤TINYINT、SMALLINT、
MEDIUM_INT更好。
使⽤枚举或整数代替字符串类型
尽量使⽤TIMESTAMP⽽⾮DATETIME
varchar2最大长度单表不要有太多字段,建议在20以内
⽤整型来存IP
索引
索引并不是越多越好,要根据查询有针对性的创建,考虑在WHERE和ORDER BY命令上涉及的列建⽴索引,可根据EXPLAIN来查看是否⽤了索引还是全表扫描
应尽量避免在WHERE⼦句中对字段进⾏NULL值判断,否则将导致引擎放弃使⽤索引⽽进⾏全表扫描
值分布很稀少的字段不适合建索引,例如"性别"这种只有两三个值的字段
字符字段只建前缀索引
字符字段最好不要做主键
不⽤外键,由程序保证约束
尽量不⽤UNIQUE,由程序保证约束
使⽤多列索引时主意顺序和查询条件保持⼀致,同时删除不必要的单列索引
简⾔之就是使⽤合适的数据类型,选择合适的索引
# 选择合适的数据类型
(1)使⽤可存下数据的最⼩的数据类型,整型 < date,time < char,varchar < blob
(2)使⽤简单的数据类型,整型⽐字符处理开销更⼩,因为字符串的⽐较更复杂。如,int类型存储时间类型,bigint类型转ip函数
(3)使⽤合理的字段属性长度,固定长度的表会更快。使⽤enum、char⽽不是varchar
(4)尽可能使⽤not null定义字段
(5)尽量少⽤text,⾮⽤不可最好分表
# 选择合适的索引列
(1)查询频繁的列,在where,group by,order by,on从句中出现的列
(2)where条件中<,<=,=,>,>=,between,in,以及like 字符串+通配符(%)出现的列
(3)长度⼩的列,索引字段越⼩越好,因为数据库的存储单位是页,⼀页中能存下的数据越多越好
(4)离散度⼤(不同的值多)的列,放在联合索引前⾯。查看离散度,通过统计不同的列值来实现,count越⼤,离散程度越⾼:
原开发⼈员已经跑路,该表早已建⽴,我⽆法修改,故:该措辞⽆法执⾏,放弃!
2.sql的编写需要注意优化
使⽤limit对查询结果的记录进⾏限定
避免select *,将需要查的字段列出来
使⽤连接(join)来代替⼦查询
拆分⼤的delete或insert语句
可通过开启慢查询⽇志来出较慢的SQL
不做列运算:SELECT id WHERE age + 1 = 10,任何对列的操作都将导致表扫描,它包括数据库教程函数、计算表达式等等,查询时要尽可能将操作移⾄等号右边
sql语句尽可能简单:⼀条sql只能在⼀个cpu运算;⼤语句拆⼩语句,减少锁时间;⼀条⼤sql可以堵死整个库
OR改写成IN:OR的效率是n级别,IN的效率是log(n)级别,in的个数建议控制在200以内
不⽤函数和触发器,在应⽤程序实现
避免%xxx式查询
少⽤JOIN
使⽤同类型进⾏⽐较,⽐如⽤'123'和'123'⽐,123和123⽐
尽量避免在WHERE⼦句中使⽤!=或<>操作符,否则将引擎放弃使⽤索引⽽进⾏全表扫描
对于连续数值,使⽤BETWEEN不⽤IN:SELECT id FROM t WHERE num BETWEEN 1 AND 5
列表数据不要拿全表,要使⽤LIMIT来分页,每页数量也不要太⼤
原开发⼈员已经跑路,程序已经完成上线,我⽆法修改sql,故:该措辞⽆法执⾏,放弃!
引擎
引擎
⽬前⼴泛使⽤的是MyISAM和InnoDB两种引擎:
1. MyISAM
2. MyISAM引擎是MySQL 5.1及之前版本的默认引擎,它的特点是:
不⽀持⾏锁,读取时对需要读到的所有表加锁,写⼊时则对表加排它锁
不⽀持事务
不⽀持外键
不⽀持崩溃后的安全恢复
在表有读取查询的同时,⽀持往表中插⼊新纪录
⽀持BLOB和TEXT的前500个字符索引,⽀持全⽂索引
⽀持延迟更新索引,极⼤提升写⼊性能
对于不会进⾏修改的表,⽀持压缩表,极⼤减少磁盘空间占⽤
1. InnoDB
2. InnoDB在MySQL 5.5后成为默认索引,它的特点是:
⽀持⾏锁,采⽤MVCC来⽀持⾼并发
⽀持事务
⽀持外键
⽀持崩溃后的安全恢复
不⽀持全⽂索引
总体来讲,MyISAM适合SELECT密集型的表,⽽InnoDB适合INSERT和UPDATE密集型的表
MyISAM速度可能超快,占⽤存储空间也⼩,但是程序要求事务⽀持,故InnoDB是必须的,故该⽅案⽆法执⾏,放弃!
3.分区
MySQL在5.1版引⼊的分区是⼀种简单的⽔平拆分,⽤户需要在建表的时候加上分区参数,对应⽤是透明的⽆需修改代码
对⽤户来说,分区表是⼀个独⽴的逻辑表,但是底层由多个物理⼦表组成,实现分区的代码实际上是通
过对⼀组底层表的对象封装,但对SQL层来说是⼀个完全封装底层的⿊盒⼦。MySQL实现分区的⽅式也意味着索引也是按照分区的⼦表定义,没有全局索引
⽤户的SQL语句是需要针对分区表做优化,SQL条件中要带上分区条件的列,从⽽使查询定位到少量的分区上,否则就会扫描全部分区,可以通过EXPLAIN PARTITIONS来查看某条SQL语句会落在那些分区上,从⽽进⾏SQL优化,我测试,查询时不带分区条件的列,也会提⾼速度,故该措施值得⼀试。
分区的好处是:
可以让单表存储更多的数据
分区表的数据更容易维护,可以通过清楚整个分区批量删除⼤量数据,也可以增加新的分区来⽀持新插⼊的数据。另外,还可以对⼀个独⽴分区进⾏优化、检查、修复等操作
部分查询能够从查询条件确定只落在少数分区上,速度会很快
分区表的数据还可以分布在不同的物理设备上,从⽽搞笑利⽤多个硬件设备
可以使⽤分区表赖避免某些特殊瓶颈,例如InnoDB单个索引的互斥访问、ext3⽂件系统的inode锁竞争
可以备份和恢复单个分区
分区的限制和缺点:
⼀个表最多只能有1024个分区
如果分区字段中有主键或者唯⼀索引的列,那么所有主键列和唯⼀索引列都必须包含进来
分区表⽆法使⽤外键约束
NULL值会使分区过滤⽆效
所有分区必须使⽤相同的存储引擎
分区的类型:
RANGE分区:基于属于⼀个给定连续区间的列值,把多⾏分配给分区
LIST分区:类似于按RANGE分区,区别在于LIST分区是基于列值匹配⼀个离散值集合中的某个值来进⾏选择
HASH分区:基于⽤户定义的表达式的返回值来进⾏选择的分区,该表达式使⽤将要插⼊到表中的这些⾏的列值进⾏计算。这个函数可以包含MySQL中有效的、产⽣⾮负整数值的任何表达式
KEY分区:类似于按HASH分区,区别在于KEY分区只⽀持计算⼀列或多列,且MySQL服务器提供其⾃⾝的哈希函数。必须有⼀列或多列包含整数值
具体关于mysql分区的概念请⾃⾏google或查询官⽅⽂档,我这⾥只是抛砖引⽟了。
我⾸先根据⽉份把上⽹记录表RANGE分区了12份,查询效率提⾼6倍左右,效果不明显,故:换id为HASH分区,分了64个分区,查询速度提升显著。问题解决!
结果如下:PARTITION BY HASH (id)PARTITIONS 64
select count() from readroom_website; --11901336⾏记录
/受影响⾏数: 0 已到记录: 1 警告: 0 持续时间 1 查询: 5.734 sec. /
select * from readroom_website where month(accesstime) =11 limit 10;
/受影响⾏数: 0 已到记录: 10 警告: 0 持续时间 1 查询: 0.719 sec. */
4.分表
分表就是把⼀张⼤表,按照如上过程都优化了,还是查询卡死,那就把这个表分成多张表,把⼀次查询
分成多次查询,然后把结果组合返回给⽤户。
分表分为垂直拆分和⽔平拆分,通常以某个字段做拆分项。⽐如以id字段拆分为100张表:表名为 tableName_id%100
但:分表需要修改源程序代码,会给开发带来⼤量⼯作,极⼤的增加了开发成本,故:只适合在开发初期就考虑到了⼤量数据存在,做好了分表处理,不适合应⽤上线了再做修改,成本太⾼!!!⽽且选择这个⽅案,都不如选择我提供的第⼆第三个⽅案的成本低!故不建议采⽤。
5.分库
把⼀个数据库分成多个,建议做个读写分离就⾏了,真正的做分库也会带来⼤量的开发成本,得不偿失!不推荐使⽤。
⽅案⼆详细说明:升级数据库,换⼀个100%兼容mysql的数据库mysql性能不⾏,那就换个。为保证源程序代码不修改,保证现有业务平稳迁移,故需要换⼀个100%兼容mysql的数据库。
1. 开源选择
tiDB github/pingcap/tidb
Cubrid /
开源数据库会带来⼤量的运维成本且其⼯业品质和MySQL尚有差距,有很多坑要踩,如果你公司要求必须⾃建数据库,那么选择该类型产品。
1. 云数据选择
阿⾥云POLARDB
www.aliyun/product/polardb?spm=a2c4g.11174283.cloudEssentials.47.7a984b5cS7h4wH
官⽅介绍语:POLARDB 是阿⾥云⾃研的下⼀代关系型分布式云原⽣数据库,100%兼容MySQL,存储容量最⾼可达 100T,性能最⾼提升⾄ MySQL 的 6 倍。POLARDB 既融合了商业数据库稳定、可靠、⾼性能的特征,⼜具有开源数据库简单、可扩展、持续迭代的优势,⽽成本只需商⽤数据库的 1/10。
我开通测试了⼀下,⽀持免费mysql的数据迁移,⽆操作成本,性能提升在10倍左右,价格跟rds相差不多,是个很好的备选解决⽅案!
阿⾥云OcenanBase
淘宝使⽤的,扛得住双⼗⼀,性能卓著,但是在公测中,我⽆法尝试,但值得期待
阿⾥云HybridDB for MySQL (原PetaData)
www.aliyun/product/petadata?spm=a2c4g.11174283.cloudEssentials.54.7a984b5cS7h4wH
官⽅介绍:云数据库HybridDB for MySQL (原名PetaData)是同时⽀持海量数据在线事务(OLTP)和在线分析(OLAP)的HTAP(Hybrid Transaction/Analytical Processing)关系型数据库。
我也测试了⼀下,是⼀个olap和oltp兼容的解决⽅案,但是价格太⾼,每⼩时⾼达10块钱,⽤来做存储太浪费了,适合存储和分析⼀起⽤的业务。
腾讯云DCDB
t/product/dcdb_for_tdsql
官⽅介绍:DCDB⼜名TDSQL,⼀种兼容MySQL协议和语法,⽀持⾃动⽔平拆分的⾼性能分布式数据库——即业务显⽰为完整的逻辑表,数据却均匀的拆分到多个分⽚中;每个分⽚默认采⽤主备架构,提供灾备、恢复、监控、不停机扩容等全套解决⽅案,适⽤于TB或PB级的海量数据场景。
腾讯的我不喜欢⽤,不多说。原因是出了问题不到⼈,线上问题⽆法解决头疼!但是他价格便宜,适合超⼩公司,玩玩。
⽅案三详细说明:去掉mysql,换⼤数据引擎处理数据
数据量过亿了,没得选了,只能上⼤数据了。
1. 开源解决⽅案
2. hadoop家族。hbase/hive怼上就是了。但是有很⾼的运维成本,⼀般公司是玩不起的,没⼗万投⼊是不会有很好的产出的!
3. 2.云解决⽅案
4. 这个就⽐较多了,也是⼀种未来趋势,⼤数据由专业的公司提供专业的服务,⼩公司或个⼈购买服务,⼤数据就像⽔/电等公共设施⼀
样,存在于社会的⽅⽅⾯⾯。
5. 国内做的最好的当属阿⾥云。
6. 我选择了阿⾥云的MaxCompute配合DataWorks,使⽤超级舒服,按量付费,成本极低。
7. MaxCompute可以理解为开源的Hive,提供sql/mapreduce/ai算法/python脚本/shell脚本等⽅式操作数
据,数据以表格的形式展现,以
分布式⽅式存储,采⽤定时任务和批处理的⽅式处理数据。DataWorks提供了⼀种⼯作流的⽅式管理你的数据处理任务和调度监控。
8. 当然你也可以选择阿⾥云hbase等其他产品,我这⾥主要是离线处理,故选择MaxCompute,基本都是图形界⾯操作,⼤概写了300⾏
sql,费⽤不超过100块钱就解决了数据处理问题。
原⽂链接:
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论