项目背景

数据来源:所有数据均为外部导入,最大数据量在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万条数据的更多相关文章

  1. mysql快速导入5000万条数据过程记录(LOAD DATA INFILE方式)

    mysql快速导入5000万条数据过程记录(LOAD DATA INFILE方式) 首先将要导入的数据文件top5000W.txt放入到数据库数据目录/var/local/mysql/data/${d ...

  2. 复杂业务下向Mysql导入30万条数据代码优化的踩坑记录

    从毕业到现在第一次接触到超过30万条数据导入MySQL的场景(有点low),就是在顺丰公司接入我司EMM产品时需要将AD中的员工数据导入MySQL中,因此楼主负责的模块connector就派上了用场. ...

  3. Mvc+Dapper+存储过程分页10万条数据

    10万条数据采用存储过程分页实现(Mvc+Dapper+存储过程) 有时候大数据量进行查询操作的时候,查询速度很大强度上可以影响用户体验,因此自己简单写了一个demo,简单总结记录一下: 技术:Mvc ...

  4. 【转】Oracle中如何用一条SQL快速生成10万条测试数据

    转自http://blog.csdn.net/welken/article/details/4971887   做数据库开发或管理的人经常要创建大量的测试数据,动不动就需要上万条,如果一条一条的录入, ...

  5. 性能优化:虚拟列表,如何渲染10万条数据的dom,页面同时不卡顿

    列表大概有2万条数据,又不让做成分页,如果页面直接渲染2万条数据,在一些低配电脑上可能会照成页面卡死,基于这个需求,我们来手写一个虚拟列表 思路 列表中固定只显示少量的数据,比如60条 在列表滚动的时 ...

  6. MariaDB(MySql)使用储存过程和随机函数插入10万条数据

    ))default charset =utf8; #定义一个随机切割字符串的函数 delimiter // create function randStr() ) begin ) default 'A ...

  7. 【原创】10万条数据采用存储过程分页实现(Mvc+Dapper+存储过程)

    有时候大数据量进行查询操作的时候,查询速度很大强度上可以影响用户体验,因此自己简单写了一个demo,简单总结记录一下: 技术:Mvc4+Dapper+Dapper扩展+Sqlserver 目前主要实现 ...

  8. DataTable 快速导入数据库——百万条数据只需几秒

    public void InsertTable(DataTable dt, string TabelName, DataColumnCollection dtColum) { string str = ...

  9. 最短时间(几秒内)利用C#往SQLserver数据库一次性插入10万条数据

    用途说明: 公司要求做一个数据导入程序,要求将Excel数据,大批量的导入到数据库中,尽量少的访问数据库,高性能的对数据库进行存储.于是在网上进行查找,发现了一个比较好的解决方案,就是采用SqlBul ...

随机推荐

  1. FL Studio中有关减少CPU占用率的一些技巧

    在使用FL Studio20进行音乐制作时经常容易碰到的工程卡顿,声音延迟现象绝大部分是由于电脑CPU超负荷运行而导致的.除了提升电脑本身的性能以外,在FL Studio20中我们也可以运用一些方法来 ...

  2. FL studio系列教程(十三):如何在FL Studio步进音序器中制作节奏

    了解了FL Studio一些操作功能后,我们就要去用这些操作功能完成我们想要的作品.所以今天小编就来带领大家在FL Studio的步进音序器中制作出简单的节奏,与此同时大家也会了解到通道的几个基础功能 ...

  3. jQuery 第七章 实例方法 位置图形

    位置坐标图形大小相关方法: .offset() .position() .scrollTop() .scrollLeft() .width() .height() .innerWidth() inne ...

  4. 【PYTEST】第二章编写测试函数

    知识点: assert 测试函数标记 跳过测试 标记预期失败的测试用例 1. asseet 返回的都是布尔值,等于False(F) 就是失败, assert 有很多 assert something ...

  5. IDEA集成Docker插件后出现日志乱码的解决办法

    修改IDEA的vmoptions文件 找到IDEA安装目录的bin目录,在idea.exe.vmoptions和idea64.exe.vmoptions文件中追加以下内容: -Dfile.encodi ...

  6. volatile使用

    mq的故障延迟机制:

  7. LaTeX中的数学公式的初步使用

    数学公式初步使用代码及注释: 显示效果:

  8. Java lambda 分组后多列求和

    主要思路是reducing,可以像sql一样分组后多列求和处理成新对象等: select code,max(name)as name,sum(chengJi)as chengJi,sum(age)as ...

  9. PyQt(Python+Qt)学习随笔:Designer中的QDialogButtonBox的accepted、rejected和helpRequested信号

    QDialogButtonBox中可以包含多个pushButton,但QDialogButtonBox本身只提供4种信号,分别是accepted.rejected.clicked和helpReques ...

  10. 影评网站Alpha版本-测试与发布

    影评网站Alpha版本-测试与发布 项目发布地址: http://120.78.161.21:8080/zhiying/ (建议使用Chrome或火狐浏览器打开,其他浏览器可能加载失败 一.Alpha ...