记录使用JAVA程序处理千万级的数据表
要求:原表有4000w+数据,需要对其中message字段进行数据处理,并将处理的结果写入result字段中
优化:分表,sql的优化
过程:
最开始是啥都不懂,打算一次性将4000w条数据获取出来 select * from d1 limit 0,40000000;
但结果,java程序在获取500w不到的数据程序就报错提出内存泄漏,而且到数据越到后面越慢,更不要说一次性获取4000w条数据了
所以我去网上搜索如何处理千万级的数据,其中提到了进行分页 select * from d1 limit 0,10000;
一次读取1W条数据,循环个4000次不就可以了,好像IDE也没有提示内存泄漏的异常。
以为这样能解决问题,那么就太天真了。且不说内存会不会泄漏的问题,光这运行的速度就可以让你绝望了。前1000W的数据光读取大概1个多小时了(还没处理数据呢),4000W数据不是简单乘以4就可以了,mysql中的limit 分页方法有一个问题就是它处理数据量大,到后面查询的效率就会越低。
limit的工作原理是遍历到你的标记点,取后面1W数据,前面遍历的数据就抛弃了,那你想想假如这标记点在1000W,相当于花了遍历了1001W数据的时间,取出了这最后1W条数据。
我后来又将分页的sql进行优化,提高了查询的速度
select * from d1 where id>=(select id from d1 order by id limit #{start},1) order by id limit 10000
这种查询方法是分页的子查询方法,在id是数字自增情况下使用,可以提高查询速度,尤其是数据量大的情况,效果更佳。
但这终究无法解决limit分页对于千万级数据的性能局限
因此就将表分成了4个小表,拿其中一个小表代码作为展示:
CREATE TABLE `d1_1` (
`result` VARCHAR(30) DEFAULT NULL,
`date` date DEFAULT NULL,
`time` time DEFAULT NULL,
`message` text,
`id` int(10) unsigned NOT NULL,
PRIMARY KEY (`id`),
KEY `date` (`date`) USING BTREE
) ENGINE=MyISAM DEFAULT CHARSET=utf8
INSERT INTO d1_1 (date, time, id, message) SELECT
date,
time,
id,
message
FROM
d1
LIMIT 0,10000000
一次性处理4000W条数据太慢,所以就一次性处理1000W条,这样分页limit的性能局限就不会太成为困扰(主要也是因为标记点越到后面limit查询就越费力)
上面的方法主要针对的是数据的读出,下面介绍的数据的写入
我们将数据处理了后又要写入数据库,处理完一条数据就写入数据库的方法想一想就知道这样的效率不会高的,相当于在不断地跟数据库建立连接,我需要做的是批量更新数据
首先在数据库url链接上加入:&allowMultiQueries=true(允许一次执行多条sql,注意这里是&而不是&)
mybatis批量更新数据的数据代码如下:
<update id="update" parameterType="java.util.List">
<foreach collection="list" separator=";" item="i" index="index" >
update d1_1
<set>
result=#{i.result}
</set>
where id=#{i.id}
</foreach>
</update>
以上就是我处理千万级数据的过程了,包括截取数据的过程,处理一张1000W的小表平均时间大概是20+分钟(不同电脑配置不同所执行的时间也有所差异)