当前位置: 首页 > news >正文

Mysql 批量写入数据,对于这类性能问题,你是如何优化的

测试环境

配置直接影响执行速度,先上一下测试机配置:

  • cpu i7 5500U(低电压伤不起,以后再也不买低电压的U了)
  • 内存 8G ddr3 1600
  • php 7.1
  • mysql 5.5.40
  • 开发框架 CodeIgniter 3.1.2

影响写入效率的因素都有什么?

  • 数据库引擎

开发中常用的数据库引擎 MyISAM,InnoDB 这两种,其他的数据库引擎我在开发中还没用到,所以不在这里测试了。

先看一下库表结构 :

test库下有两张表:分别为上面提的两种引擎:

每张表结构如下(一个自增id,一个varchar类型待插入字段):

缺省状态下对两表插入20w数据看一下效率:

PHP代码如下:

/**
	 * 
	 * 测试插入效率
	 * 
	 * @return void
	 * 
	 */
	public function insertTest(){

		set_time_limit(0);   //防止超300s 500错误
	
		$t1 = microtime(true);
	
	
		//随机插入num条
		for ($i=1; $i<=200000; $i++){
			
			$result = $this->db->insert('myisam', ['value' => uniqid().$i]);
		}

		//程序运行时间
		$t2 = microtime(true);
		echo '耗时:'.round($t2-$t1,3).'秒<br>';
		echo '内存消耗:'.round(memory_get_usage()/1048576,2)." M<br/>";
	
	}复制代码

执行结果:

20w 数据 Myisam要 接近3分钟了。

看一下InnoDb缺省状态下执行时间:

插入1w条数据 用了 6分49秒,没办法等下去了, 按照这个数据量推测 6分49 * 20 = ???

后期由于数据量增多,也会影响插入性能,所以InnoDb默认状态插入20w单字段数据要2小时以上,无法 忍受。

  • 业务逻辑

显然上面的业务逻辑是有问题的,每条数据单次插入,增加了mysql的开销,每次插入数据都要重新连 接一下mysql,肯定是相当浪费资源了。所以CI提供了 insert_batch(),批量写入数据。Thinkphp3.2 也有addAll() 这样的方法来支持。其他框架应该都有!

原理很简单就是把二维数组,拼接为sql

将单条Sql如下:

$sql = "INSERT INTO TEST (value) VALUES ('helloworld1')";

$sql = "INSERT INTO TEST (value) VALUES ('helloworld2')";

拼接为:

$sql = "INSERT INTO TEST (value) VALUES ('helloworld1'), ('helloworld2')";

很明显批量插入速度要快很多。

还是20w数据,MyISAM 下批量查询速度多快?(已有数据会影响插入效率,已清空 myisam表)

还是上代码

/**
	 * 
	 * 测试批量插入效率
	 * 
	 * @return void
	 * @author lidalin.se@gmail.com
	 * 
	 */
	public function insertTest(){

		set_time_limit(0);   //防止超300s 500错误
	
		$t1 = microtime(true);
	
	
		//随机插入num条
		for ($i=1; $i<=200000; $i++){
			
			$data[$i] = ['value' => uniqid().$i];
		}
		//程序运行时间
		$t2 = microtime(true);
		echo '循环耗时:'.round($t2-$t1,3).'秒<br>';
		
		$this->db->insert_batch('myisam', $data);  //批量插入
		
		$t3 = microtime(true);
		echo '插入耗时:'.round($t3-$t2,3).'秒<br>';
		
		echo '内存消耗:'.round(memory_get_usage()/1048576,2)." M<br/>";
	
	}复制代码

执行结果:

比起之前的167秒的单条插入速度快了 5 倍。内存消耗增加1.5倍左右,内存换时间,可取~~~

InnoDB 会是什么速度呢?

执行结果:

这次终于执行完了,而且速度很快。和之前的两个小时比,效率也提升了N倍。

  • 修改配置参数提升性能:

InnoDB 引擎 真的这么慢?这么low?

答案显然是:NO

InnoDB写入之所以这么慢的一个原因是:

innodb_flush_log_at_trx_commit复制代码

参数配置的问题

如下图默认的值:

关于值请查阅mysql文档:

https://dev.mysql.com/doc/refman/5.7/en/innodb-parameters.html

innodb_flush_log_at_trx_commit=0时, log buffer将每秒一次地写入log file, 并且log file的flush(刷新 到disk)操作同时进行. 此时, 事务提交是不会主动触发写入磁盘的操作.

innodb_flush_log_at_trx_commit=1时(默认), 每次事务提交时, MySQL会把log buffer的数据写入log f ile, 并且将log file flush(刷新到disk)中去.

innodb_flush_log_at_trx_commit=2时, 每次事务提交时, MySQL会把log buffer的数据写入log file, 但 不会主动触发flush(刷新到disk)操作同时进行. 然而, MySQL会每秒执行一次flush(刷新到disk)操作.

把值设置为2,再试一下:

速度又提升了 3倍,和 MyISAM几乎相同。

所以以后说InnoDB写入速度慢,可能是配置问题

  • 还可以优化?

还有什么可以优化?

由于我们使用了 框架的insert_batch,看一下CI源码:

/**
	 * The "set_insert_batch" function.  Allows key/value pairs to be set for batch inserts
	 *
	 * @param	mixed
	 * @param	string
	 * @param	bool
	 * @return	CI_DB_query_builder
	 */
	public function set_insert_batch($key, $value = '', $escape = NULL)
	{
		$key = $this->_object_to_array_batch($key);

		if ( ! is_array($key))
		{
			$key = array($key => $value);
		}

		is_bool($escape) OR $escape = $this->_protect_identifiers;

		$keys = array_keys($this->_object_to_array(current($key)));
		sort($keys);

		foreach ($key as $row)
		{
			$row = $this->_object_to_array($row);
			if (count(array_diff($keys, array_keys($row))) > 0 OR count(array_diff(array_keys($row), $keys)) > 0)
			{
				// batch function above returns an error on an empty array
				$this->qb_set[] = array();
				return;
			}

			ksort($row); // puts $row in the same order as our keys

			if ($escape !== FALSE)
			{
				$clean = array();
				foreach ($row as $value)
				{
					$clean[] = $this->escape($value);
				}

				$row = $clean;
			}

			$this->qb_set[] = '('.implode(',', $row).')';
		}

		foreach ($keys as $k)
		{
			$this->qb_keys[] = $this->protect_identifiers($k, FALSE, $escape);
		}

		return $this;
	}复制代码

我们传入的数据,方法会再循环,判断。所以建议语句自己拼接

代码修改如下:

/**
	 * 
	 * 测试插入效率
	 * 
	 * @return void
	 * @author lidalin.se@gmail.com
	 * 
	 */
	public function insertTest(){

		set_time_limit(0);   //防止超300s 500错误
	
		$t1 = microtime(true);
	
	
		$sql = "insert into innodb (value) VALUES";
		//随机插入num条
		for ($i=1; $i<=200000; $i++){
			
			$val = uniqid().$i;
				
			$sql .= "('{$val}'),";
			
		}
		
		$sql = substr($sql,0,-1);
		
		//程序运行时间
		$t2 = microtime(true);
		echo '循环耗时:'.round($t2-$t1,3).'秒<br>';
		
		$this->db->query($sql);  //批量插入
		
		$t3 = microtime(true);
		echo '插入耗时:'.round($t3-$t2,3).'秒<br>';
		
		echo '内存消耗:'.round(memory_get_usage()/1048576,2)." M<br/>";
	
	}复制代码

执行结果:

20W条数据 InnoDB 循环1.6秒,插入1.2秒。速度是不是很爽了。。

拼接语句可能会报错
设置一下

max_allowed_packet = 500M

允许mysql 接受数据包大小。


转载于:https://juejin.im/post/5c21e751518825124e276186

相关文章:

  • spring mvc返回json字符串的方式
  • Linux 下的dd命令使用详解
  • ES 5 中 判断数组的方法
  • [HNOI2008]水平可见直线
  • 电商产品设计实战(二):电商整体产品架构
  • Integer类toString(int i,int radix)方法
  • 普通java工程的resources目录寻址
  • express.js的介绍及使用
  • Intel要在中国投35亿美金造这种闪存,3DxPoint技术牛在哪里?
  • MongoDB系统CentOS 7.1 crash的排障过程
  • MySQL建表语句转PostgreSQL建表语句全纪录
  • JAVA设计模式之观察者模式
  • 安装mongo,新建数据库,添加普通用户
  • 【EOS】Cleos基础
  • 视频课左右滑动后应该定位
  • 网络传输文件的问题
  • JavaScript 如何正确处理 Unicode 编码问题!
  • .pyc 想到的一些问题
  • 【技术性】Search知识
  • Android框架之Volley
  • axios请求、和返回数据拦截,统一请求报错提示_012
  • canvas绘制圆角头像
  • css选择器
  • Java 实战开发之spring、logback配置及chrome开发神器(六)
  • k8s如何管理Pod
  • Koa2 之文件上传下载
  • php ci框架整合银盛支付
  • quasar-framework cnodejs社区
  • rc-form之最单纯情况
  • spring + angular 实现导出excel
  • zookeeper系列(七)实战分布式命名服务
  • 二维平面内的碰撞检测【一】
  • 看完九篇字体系列的文章,你还觉得我是在说字体?
  • 前端技术周刊 2018-12-10:前端自动化测试
  • 正则表达式
  • ionic异常记录
  • Java性能优化之JVM GC(垃圾回收机制)
  • ​​​​​​​​​​​​​​Γ函数
  • ## 临床数据 两两比较 加显著性boxplot加显著性
  • #我与Java虚拟机的故事#连载03:面试过的百度,滴滴,快手都问了这些问题
  • $var=htmlencode(“‘);alert(‘2“); 的个人理解
  • (3)nginx 配置(nginx.conf)
  • (cljs/run-at (JSVM. :browser) 搭建刚好可用的开发环境!)
  • (ZT)出版业改革:该死的死,该生的生
  • (翻译)Entity Framework技巧系列之七 - Tip 26 – 28
  • (非本人原创)史记·柴静列传(r4笔记第65天)
  • (附源码)springboot金融新闻信息服务系统 毕业设计651450
  • (附源码)ssm本科教学合格评估管理系统 毕业设计 180916
  • (附源码)计算机毕业设计SSM疫情社区管理系统
  • (附源码)计算机毕业设计SSM智能化管理的仓库管理
  • (十一)图像的罗伯特梯度锐化
  • ../depcomp: line 571: exec: g++: not found
  • .bat批处理(六):替换字符串中匹配的子串
  • .NET Core WebAPI中使用swagger版本控制,添加注释
  • .NET Micro Framework 4.2 beta 源码探析