今天做个功能遇到数据批量插入的问题,测试了下几种情况下的简单对比,虽然测试方式不全面但也能得出基本结果了,就不浪费时间了。
批量插入测试的几个要点:
- 引擎:MyISAM 和 InnoDB
- SQL 语法:
a: INSERT INTO TABLE filed1, filed2 … VALUES (val1 , val2 , …)
b: INSERT INTO TABLE filed1, filed2 … VALUES (val1 , val2 , …) , (val1 , val2 , …) , (val1 , val2 , …) …
- InnoDB 驱动下启用事务和不启用事务
- 驱动方式:PDO 使用 预编译占位符方式
具体过程没什么好写的,直接总结下好了:
1. 速度最快的方式。
引擎:选择引擎的话,当然是 MyISAM 了;
语法:上面 SQL 语法 b 方式。一个 INSERT 语句后面 VALUES 部分跟上 N 个要插入的每行数据;
我用的是PDO占位符方式,所有 VALUES 后面全是问号形式占位符。
对于占位符,PDO 里面貌似最大数量为 65535 个,所以根据 插入语句的字段数量不通,每次插入的行数也就不一样了。
我测试的例子中,每行有 17 个字段有占位符,所以一次最多录入 3855 行数据,如果不用占位符的话好像没什么限制的吧(我没试过)
2. 容易失误的地方
使用 InnoDB 引擎,却用普通方式批量插入,没有启动事务进行提交
特别是使用上面 SQL 语法 a 方式,每一行都带上字段名,插入4000行左右用了144秒,慢的要死。
加上事务或者使用 SQL语法 b 方式两者速度相差不多,则速度缩减到 0.5 秒左右,相差 288 倍。
3. 下面贴上代码
源码:不能直接用的 看看意思明白就行
/**
* 生成插入数据的字段以及对应占位符并绑定数据
* @param null|array $data
* @param int $rows
* @return string
*/
protected function getInsertFieldValues(array $data, $rows) {
$data && $this->data($data);
$data = $this->getData();
$fields = $holders = $bind = array();
foreach ($data as $k => $v) {
$fields[] = $k;
$holders[] = ‘?‘;
}
$vals = ‘ , ( ‘ . implode(‘ , ‘, $holders) . ‘ )‘;
return ‘( ‘ . implode(‘ , ‘, $fields) . ‘ ) VALUES ‘. ltrim(str_repeat($vals, $rows), ‘ ,‘);
}
/**
* 将多行数据共用一个 INSERT 语句进行批量插入
* 速度最快, 但插入失败时只能大范围的元素块, 而不能精确到某一行
* 其次, 每次插入占位符总数不能超过 65535 个, 程序将自动判断占位符数量并对列表进行分页插入
* 该方法对 innoDB 引擎和 MyISAM 引擎速度相差不大
* @param array $list
* @return int|array 成功则返回行数, 失败返回包含错误部分的所有元素(但不能精确到具体某一行)
* @throws Exception
*/
protected function addAllMoreInRow(array $list) {
$rowsNum = count($list);
if (isset($list[0])) $row = $list[0];
else {
$row = array_shift($list);
array_unshift($list, $row);
}
// 每一行的占位符数量
$holdersNum = count($row);
// 计算对数组分割每块最大元素数量
$chunkSize = min(array(50000, floor(65535/$holdersNum)));
// 获得批量插入语句模版并进行预编译
$query[0] = ‘INSERT INTO‘;
$query[1] = $this->getTable();
$query[2] = $this->getInsertFieldValues($row, $chunkSize);
$this->prepare($query);
// 保存之前已设置的绑定数据
$defaultBind = $this->bind ? $this->bind : array();
// 对数组按页分组
$list = array_chunk($list, $chunkSize, true);
// 最后一页索引
$last = count($list) - 1;
// 将数组分成多块单独绑定数据再进行按页插入
foreach ($list as $p=>$block) {
// 最后一页因数量不一致需重置预编译模版
if ($p == $last) {
$this->reset();
$query[2] = $this->getInsertFieldValues($row, count($block));
$this->prepare($query);
}
$this->bind = $defaultBind;
foreach ($block as $r=>$data) {
foreach ($data as $k=>$v) {
$this->bind[] = $v;
}
}
$result = $this->execute($this->bind);
if (! $result) return $block;
}
$this->reset();
return $rowsNum;
}
版权声明:本文为博主原创文章,未经博主允许不得转载。http://blog.csdn.net/zhouzme
时间: 2024-10-03 23:22:57