PHP MySQL 快速导入10万条数据
项目背景
数据来源:所有数据均为外部导入,最大数据量在10w+
输出数据:导出经过业务处理之后的数据
使用框架:fastadmin
涉及的问题:
1、数据读取
2、数据保存
使用数据:10w+
解决方案:
方案一:直接利用框架提供的功能导入Excel数据
结果:一分钟之后超时,最终执行完成时间在3分钟左右
分析:其中数据读取和数据保存(使用模型批量保存拆分为100,1000,10000)都十分耗时,而且在超时之后,系统其它功能无法响应。
可行性:不可行
方案二:将Excel数据换成csv格式
结果:一分钟之后还是超时,最终执行时间大约90s左右。
分析:读取csv格式的时候需要转码(这里很耗时,取消之后很快但是数据乱码了)。数据保存(使用模型批量保存拆分为100,1000,10000)部分依然很耗时。
可行性:不可行
方案三:取消转码,使用MySQL原生方式保存数据
结果:偶尔会成功(这里是单条数据方式插入),大约执行时间在60s左右。
分析:MySQL在执行sql语句的时候多次解析语句导致消耗很多时间,可以将语句合并为一条语句执行。
可行性:勉强可以
方案四:由于方案三可以知道多条语句执行比较耗时,修改为合并执行
结果:成功,大约耗时10-20s
分析:MySQL的语句是有大小限制,避免后续数据量大的时候出现错误。这里将数据拆分多次执行(10000,20000,50000 耗时差距在1s左右,基本可以看做没区别),最后将合并之后的sql语句在进行转码,最终耗时也在10-20s之间。
可行性:可以。

数据处理基础代码如下(网上代码修改,如有侵权,联系必删)
<?php namespace app\common\library;
use think\Db; class CsvReader {
private $csv_file;
private $spl_object = null;
private $error; public function __construct($csv_file = '') {
if($csv_file && file_exists($csv_file)) {
$this->csv_file = $csv_file;
}
} public function set_csv_file($csv_file) {
if(!$csv_file || !file_exists($csv_file)) {
$this->error = 'File invalid';
return false;
}
$this->csv_file = $csv_file;
$this->spl_object = null;
} public function get_csv_file() {
return $this->csv_file;
} private function _file_valid($file = '') {
$file = $file ? $file : $this->csv_file;
if(!$file || !file_exists($file)) {
return false;
}
if(!is_readable($file)) {
return false;
}
return true;
} private function _open_file() {
if(!$this->_file_valid()) {
$this->error = 'File invalid';
return false;
}
if($this->spl_object == null) {
$this->spl_object = new \SplFileObject($this->csv_file, 'rb');
}
return true;
} /**
* 读取时候直接转码
* @* @param integrity $start 数据长度
* @* @param integrity $length 数据长度
* @* @param Callback $call 需要处理业务回调方法
* @* @param Array $para 表头名称与数据库表字段对应关系
*/
public function get_data($start = 0, $length = 0, $call=null, $para=null) {
if(!$this->_open_file()) {
return false;
}
$length = $length ? $length : $this->get_lines();
$start = $start - 1;
$start = ($start < 0) ? 0 : $start; $data = [];
$this->spl_object->seek($start);
$rowindex=[];
$tpara = [];
if (!empty($para)) {
$thead = (array)$this->spl_object->fgetcsv();
$index = 0;
foreach ($thead as $key => $val) {
$encoding = mb_detect_encoding($val, ['utf-8', 'gbk', 'latin1', 'big5']);
if ($encoding != 'utf-8') {
$val = mb_convert_encoding($val, 'utf-8', $encoding);
}
if (isset($para[$val])) {
$rowindex[] = $index;
$tpara[$index] = $para[$val];
} $index++;
}
$this->spl_object->next();
} while ($length-- && !$this->spl_object->eof()) {
$tdata =(array)$this->spl_object->fgetcsv();
$db = []; foreach ($rowindex as $index) {
if(!isset($tdata[$index]))continue; $value = $tdata[$index];
$value = is_null($value) ? '' : trim($value);
$encoding = mb_detect_encoding($value, ['utf-8', 'gbk', 'latin1', 'big5']);
if ($encoding != 'utf-8') {
$db[$tpara[$index]] = mb_convert_encoding($value, 'utf-8', $encoding);
}
else{
$db[$tpara[$index]] = $value;
}
} if ($call) {
$t=call_user_func_array( $call ,[$db]); if ($t) {
$data[] = $t;
}
}else {
$data[]=$db;
}
$this->spl_object->next();
}
return $data;
} /**
* 读取时候不转码,并且保存到数据库
* @* @param Int $start 数据长度
* @* @param Int $length 数据长度
* @* @param Callback $call 需要处理业务回调方法
* @* @param Array $para 表头名称与数据库表字段对应关系
* @* @param String $table 需要出入的表名
*/
public function get_data1($length = 0, $start = 0,$call=null,$para=null,$table=null) {
if(!$this->_open_file()) {
return false;
}
$length = $length ? $length : $this->get_lines();
$start = $start - 1;
$start = ($start < 0) ? 0 : $start; $data = [];
$this->spl_object->seek($start);
$rowindex=[]; $tpara = []; //表头索引与数据库字段对应关系
if (!empty($para)) {
$thead = (array)$this->spl_object->fgetcsv();
// var_dump($thead);
$index = 0;
foreach ($thead as $key => $val) {
$encoding = mb_detect_encoding($val, ['utf-8', 'gbk', 'latin1', 'big5']);
if ($encoding != 'utf-8') {
$val = mb_convert_encoding($val, 'utf-8', $encoding);
}
if (isset($para[$val])) {
$rowindex[] = $index;
$tpara[$index] = $para[$val];
} $index++;
}
$this->spl_object->next();
} while ($length-- && !$this->spl_object->eof()) {
$tdata =(array)$this->spl_object->fgetcsv();
$db = []; foreach ($rowindex as $idx) {
if(!isset($tdata[$idx]))continue; $value = $tdata[$idx];
$value = is_null($value) ? '' : trim($value);
$db[$tpara[$idx]] = $value;
} // 如需要处理业务,数字可以,其他文字不行(未转码)
if ($call) {
$t=call_user_func_array( $call ,[$db]); if ($t) {
$data[] = $t;
}
}else {
$data[]=$db;
}
$this->spl_object->next(); if (count($data)==10000) {
$this->insertsdb($table,$para,$data);
$data=[];
}
} if (count($data)>0) {
$this->insertsdb($table,$para,$data);
}
}
/**
* @* @param String $table 表名
* @* @param Array $para 要插入表的字段
* @* @param Array $data 要插入表的数据
*/
public function insertsdb($table,$para,$data)
{
$sql = 'insert into '.$table.' (';
$p = implode(',',$para);
$sql .= $p.') values'; $tmpval='';
foreach ($data as $key => $value) {
$v = implode('","',array_values($value)); $tmpval .='("'.$v.'"),';
} $sql .= rtrim($tmpval,','); // 利用事务也可以减少执行时间
Db::startTrans();
try{
$encoding = mb_detect_encoding($sql, ['utf-8', 'gbk', 'latin1', 'big5']);
$sql = mb_convert_encoding($sql, 'utf-8', $encoding);
$sql = str_replace("\\","",$sql);
Db::execute($sql);
// 提交事务
Db::commit();
} catch (\Exception $e) {
// 回滚事务
Db::rollback(); } }
public function get_lines() {
if(!$this->_open_file()) {
return false;
}
$this->spl_object->seek(filesize($this->csv_file));
return $this->spl_object->key();
} public function get_error() {
return $this->error;
}
}
调用代码如下
public function import()
{
$file = $this->request->request('file');
if (!$file) {
$this->error(__('Parameter %s can not be empty', 'file'));
}
$filePath = ROOT_PATH . DS . 'public' . DS . $file;
if (!is_file($filePath)) {
$this->error(__('No results were found'));
}
$csv = new CsvReader($filePath);
// 此处公司业务不便公布(大约有20个参数),根据自己业务自行调整
$para =[
'单位详细名称'=>'company_name',
'省份'=>'province',
'地市'=>'city',
'区县'=>'country',
'是否关闭'=>'is_shutdown',
'公司Id'=>'company_id'
];
$data = $csv->get_data1(0,0,[$this,'handdbCallBack'],$para,'fa_bs_pollutant_discharge_info');
if($data)
{
$this->error('失败');
}
$this->success();
} /**
* 业务处理回调
*/
public function handdbCallBack($row)
{ if ($row) { // 当前编码是CP936
// 这里可以使用 $encoding = mb_detect_encoding($str, ['utf-8', 'gbk', 'latin1', 'big5'])
// 获取当前数据编码
$encoding='CP936';
$test = mb_convert_encoding('是', $encoding, 'utf-8');
if (isset($row['is_shutdown'])) {
$row['is_shutdown']=$test==$row['is_shutdown']?1:0;
}
// 此处为雪花ID生成,自行百度
$row['company_id'] = $this->snow->generateID(); return $row;
}
return null;
}
关于导出部分,可以直接导出csv格式,速度会很快。这里就不上代码了,自行百度。
以上是所有内容,以此记录一下,便于以后查阅。如有问题或者更好的解决方案欢迎各位指出。谢谢!!!
PHP MySQL 快速导入10万条数据的更多相关文章
- mysql快速导入5000万条数据过程记录(LOAD DATA INFILE方式)
mysql快速导入5000万条数据过程记录(LOAD DATA INFILE方式) 首先将要导入的数据文件top5000W.txt放入到数据库数据目录/var/local/mysql/data/${d ...
- 复杂业务下向Mysql导入30万条数据代码优化的踩坑记录
从毕业到现在第一次接触到超过30万条数据导入MySQL的场景(有点low),就是在顺丰公司接入我司EMM产品时需要将AD中的员工数据导入MySQL中,因此楼主负责的模块connector就派上了用场. ...
- Mvc+Dapper+存储过程分页10万条数据
10万条数据采用存储过程分页实现(Mvc+Dapper+存储过程) 有时候大数据量进行查询操作的时候,查询速度很大强度上可以影响用户体验,因此自己简单写了一个demo,简单总结记录一下: 技术:Mvc ...
- 【转】Oracle中如何用一条SQL快速生成10万条测试数据
转自http://blog.csdn.net/welken/article/details/4971887 做数据库开发或管理的人经常要创建大量的测试数据,动不动就需要上万条,如果一条一条的录入, ...
- 性能优化:虚拟列表,如何渲染10万条数据的dom,页面同时不卡顿
列表大概有2万条数据,又不让做成分页,如果页面直接渲染2万条数据,在一些低配电脑上可能会照成页面卡死,基于这个需求,我们来手写一个虚拟列表 思路 列表中固定只显示少量的数据,比如60条 在列表滚动的时 ...
- MariaDB(MySql)使用储存过程和随机函数插入10万条数据
))default charset =utf8; #定义一个随机切割字符串的函数 delimiter // create function randStr() ) begin ) default 'A ...
- 【原创】10万条数据采用存储过程分页实现(Mvc+Dapper+存储过程)
有时候大数据量进行查询操作的时候,查询速度很大强度上可以影响用户体验,因此自己简单写了一个demo,简单总结记录一下: 技术:Mvc4+Dapper+Dapper扩展+Sqlserver 目前主要实现 ...
- DataTable 快速导入数据库——百万条数据只需几秒
public void InsertTable(DataTable dt, string TabelName, DataColumnCollection dtColum) { string str = ...
- 最短时间(几秒内)利用C#往SQLserver数据库一次性插入10万条数据
用途说明: 公司要求做一个数据导入程序,要求将Excel数据,大批量的导入到数据库中,尽量少的访问数据库,高性能的对数据库进行存储.于是在网上进行查找,发现了一个比较好的解决方案,就是采用SqlBul ...
随机推荐
- 企业级工作流解决方案(十五)--集成Abp和ng-alain--Abp其他改造
配置功能增强 Abp定义了各种配置接口,但是没有定义这些配置数据从哪里来,但是管理配置数据对于一个应用程序来说,是必不可少的一件事情. .net的配置数据管理,一般放在Web.config文件或者Ap ...
- MindManager思维导图应用到办公中需要注意什么
MindManager思维导图是一个易于使用的项目管理软件,能很好地提高项目组的工作效率和小组成员之间的协作性.接下来,小编就为大家介绍三个能利用好该思维导图软件办公的技巧. 一.审阅会议--合作办公 ...
- Linux 设置静态路由表
一般来说多半不会使用到这个操作,但是最近有几台服务器需要多网卡,一个内网一个外网.导致网络访问内网有时候不通,这就需要我们手动写一下路由表了.操作如: 创建配置文件 网卡配置路径:/etc/sysco ...
- 【2020.12.02提高组模拟】球员(player)
题目 题目描述 老师们已经知道学生喜欢睡觉,Soaring是这项记录保持者.他只会在吃饭或玩FIFA20时才会醒来.因此,他经常做关于足球的梦,在他最近的一次梦中,他发现自己成了皇家马德里足球俱乐部的 ...
- JZOJ 2020.10.6 【NOIP2017提高A组模拟9.7】简单无向图
简单无向图 题目 Description Input Output Sample Input 输入1: 4 2 1 1 2 输入2: 10 2 2 2 2 1 1 2 1 1 2 Sample Out ...
- R语言无网络安装R包,彻底解决依赖问题!
R version: 3.5.3, 3.6.3 更新日期: 2020-9-10 大家测试后多提建议哈, 有问题我会持续更新的 在工作中,我们使用的服务器通常是不能联外网的,这在安装R包的时候产生了巨大 ...
- PyQt学习随笔:通过自定义类重写QApplication的notify方法捕获应用的所有消息
PyQt程序通过调用QApplication类的exec_()(sys.exit(app.exec_()) 进入程序主循环,开始处理事件,它从事件队列中获取本地窗口系统事件,将它们转化为 QEvent ...
- Leetcode学习笔记(1)
scrapy爬虫的学习告一段落,又因为现在在学习数据结构,做题平台是lettcode:https://leetcode-cn.com/ 每周都要交一次做题的笔记,所以把相关代码和思路同时放在博客上记录 ...
- Android10_原理机制系列_Window介绍及WMS的启动过程
简介 Window简介 Android中,Window是一个重要部分,用户看到的界面.触摸显示界面进行一系列操作都涉及到Window.但实际上,Window本身并不具备绘制功能. 该篇简单介绍下Win ...
- 使用 typescript 快速开发一个 cli
cli 的全称 command-line interface(命令行界面),也就是前端同学常用的脚手架,比如 yo.vue cli.react cli 等. cli 可以方便我们快速创建项目,下图是引 ...