您的当前位置:首页正文

MySQL批量SQL插入性能优化

2023-11-10 来源:帮我找美食网
    VALUES (‘0‘, ‘userid_0‘, ‘content_0‘, 0);INSERT INTO `insert_table` (`datetime`, `uid`, `content`, `type`)    VALUES (‘1‘, ‘userid_1‘, ‘content_1‘, 1);

 

修改成:

 

 
12INSERT INTO `insert_table` (`datetime`, `uid`, `content`, `type`)    VALUES (‘0‘, ‘userid_0‘, ‘content_0‘, 0), (‘1‘, ‘userid_1‘, ‘content_1‘, 1);

 

修改后的插入操作能够提高程序的插入效率。这里第二种SQL执行效率高的主要原因是合并后日志量(MySQL的binlog和innodb的事务让日志)减少了,降低日志刷盘的数据量和频率,从而提高效率。通过合并SQL语句,同时也能减少SQL语句解析的次数,减少网络传输的IO。这里提供一些测试对比数据,分别是进行单条数据的导入与转化成一条SQL语句进行导入,分别测试1百、1千、1万条数据记录。技术分享

 

 

2. 在事务中进行插入处理。把插入修改成:

 

 
1234567START TRANSACTION;INSERT INTO `insert_table` (`datetime`, `uid`, `content`, `type`)    VALUES (‘0‘, ‘userid_0‘, ‘content_0‘, 0);INSERT INTO `insert_table` (`datetime`, `uid`, `content`, `type`)    VALUES (‘1‘, ‘userid_1‘, ‘content_1‘, 1);...COMMIT;

 

使用事务可以提高数据的插入效率,这是因为进行一个INSERT操作时,MySQL内部会建立一个事务,在事务内才进行真正插入处理操作。通过使用事务可以减少创建事务的消耗,所有插入都在执行后才进行提交操作。这里也提供了测试对比,分别是不使用事务与使用事务在记录数为1百、1千、1万的情况。技术分享

 

3. 数据有序插入。数据有序的插入是指插入记录在主键上是有序排列,例如datetime是记录的主键:

 

 
123456INSERT INTO `insert_table` (`datetime`, `uid`, `content`, `type`)    VALUES (‘1‘, ‘userid_1‘, ‘content_1‘, 1);INSERT INTO `insert_table` (`datetime`, `uid`, `content`, `type`)    VALUES (‘0‘, ‘userid_0‘, ‘content_0‘, 0);INSERT INTO `insert_table` (`datetime`, `uid`, `content`, `type`)    VALUES (‘2‘, ‘userid_2‘, ‘content_2‘,2);

 

修改成:

 

 
123456INSERT INTO `insert_table` (`datetime`, `uid`, `content`, `type`)    VALUES (‘0‘, ‘userid_0‘, ‘content_0‘, 0);INSERT INTO `insert_table` (`datetime`, `uid`, `content`, `type`)    VALUES (‘1‘, ‘userid_1‘, ‘content_1‘, 1);INSERT INTO `insert_table` (`datetime`, `uid`, `content`, `type`)    VALUES (‘2‘, ‘userid_2‘, ‘content_2‘,2);

 

由于数据库插入时,需要维护索引数据,无序的记录会增大维护索引的成本。我们可以参照innodb使用的B+tree索引,如果每次插入记录都在索引的最后面,索引的定位效率很高,并且对索引调整较小;如果插入的记录在索引中间,需要B+tree进行分裂合并等处理,会消耗比较多计算资源,并且插入记录的索引定位效率会下降,数据量较大时会有频繁的磁盘操作。下面提供随机数据与顺序数据的性能对比,分别是记录为1百、1千、1万、10万、100万。技术分享

从测试结果来看,该优化方法的性能有所提高,但是提高并不是很明显。

 

性能综合测试:这里提供了同时使用上面三种方法进行INSERT效率优化的测试。技术分享

从测试结果可以看到,合并数据+事务的方法在较小数据量时,性能提高是很明显的,数据量较大时(1千万以上),性能会急剧下降,这是由于此时数据量超过了innodb_buffer的容量,每次定位索引涉及较多的磁盘读写操作,性能下降较快。而使用合并数据+事务+有序数据的方式在数据量达到千万级以上表现依旧是良好,在数据量较大时,有序数据索引定位较为方便,不需要频繁对磁盘进行读写操作,所以可以维持较高的性能。

 

注意事项:1. SQL语句是有长度限制,在进行数据合并在同一SQL中务必不能超过SQL长度限制,通过max_allowed_packet配置可以修改,默认是1M,测试时修改为8M。2. 事务需要控制大小,事务太大可能会影响执行的效率。MySQL有innodb_log_buffer_size配置项,超过这个值会把innodb的数据刷到磁盘中,这时,效率会有所下降。所以比较好的做法是,在数据达到这个这个值前进行事务提交。

 

转自:http://tech.uc.cn/?p=634

MySQL批量SQL插入性能优化

标签:

小编还为您整理了以下内容,可能对您也有帮助:

mysql 一次插入几万条数据应该怎么做优化

关于mysql处理百万级以上的数据时如何提高其查询速度的方法

最近一段时间由于工作需要,开始关注针对Mysql数据库的select查询语句的相关优化方法。

由于在参与的实际项目中发现当mysql表的数据量达到百万级时,普通SQL查询效率呈直线下降,而且如果where中的查询条件较多时,其查询速度简直无法容忍。曾经测试对一个包含400多万条记录(有索引)的表执行一条条件查询,其查询时间竟然高达40几秒,相信这么高的查询延时,任何用户都会抓狂。因此如何提高sql语句查询效率,显得十分重要。以下是网上流传比较广泛的30种SQL查询语句优化方法:

1、应尽量避免在 where 子句中使用!=或<>操作符,否则将引擎放弃使用索引而进行全表扫描。

2、对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引。

3、应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描,如:

select id from t where num is null

可以在num上设置默认值0,确保表中num列没有null值,然后这样查询:

select id from t where num=0

4、尽量避免在 where 子句中使用 or 来连接条件,否则将导致引擎放弃使用索引而进行全表扫描,如:

select id from t where num=10 or num=20

可以这样查询:

select id from t where num=10

union all

select id from t where num=20

5、下面的查询也将导致全表扫描:(不能前置百分号)

select id from t where name like ‘%c%’

若要提高效率,可以考虑全文检索。

6、in 和 not in 也要慎用,否则会导致全表扫描,如:

select id from t where num in(1,2,3)

对于连续的数值,能用 between 就不要用 in 了:

select id from t where num between 1 and 3

7、如果在 where 子句中使用参数,也会导致全表扫描。因为SQL只有在运行时才会解析局部变量,但优化程序不能将访问计划的选择推迟到运行时;它必须在编译时进行选择。然 而,如果在编译时建立访问计划,变量的值还是未知的,因而无法作为索引选择的输入项。如下面语句将进行全表扫描:

select id from t where num=@num

可以改为强制查询使用索引:

select id from t with(index(索引名)) where num=@num

8、应尽量避免在 where 子句中对字段进行表达式操作,这将导致引擎放弃使用索引而进行全表扫描。如:

select id from t where num/2=100

应改为:

select id from t where num=100*2

9、应尽量避免在where子句中对字段进行函数操作,这将导致引擎放弃使用索引而进行全表扫描。如:

select id from t where substring(name,1,3)=’abc’–name以abc开头的id

select id from t where datediff(day,createdate,’2005-11-30′)=0–’2005-11-30′生成的id

应改为:

select id from t where name like ‘abc%’

select id from t where createdate>=’2005-11-30′ and createdate<’2005-12-1′

10、不要在 where 子句中的“=”左边进行函数、算术运算或其他表达式运算,否则系统将可能无法正确使用索引。

11、在使用索引字段作为条件时,如果该索引是复合索引,那么必须使用到该索引中的第一个字段作为条件时才能保证系统使用该索引,否则该索引将不会被使 用,并且应尽可能的让字段顺序与索引顺序相一致。

12、不要写一些没有意义的查询,如需要生成一个空表结构:

select col1,col2 into #t from t where 1=0

这类代码不会返回任何结果集,但是会消耗系统资源的,应改成这样:

create table #t(…)

13、很多时候用 exists 代替 in 是一个好的选择:

select num from a where num in(select num from b)

用下面的语句替换:

select num from a where exists(select 1 from b where num=a.num)

14、并不是所有索引对查询都有效,SQL是根据表中数据来进行查询优化的,当索引列有大量数据重复时,SQL查询可能不会去利用索引,如一表中有字段 sex,male、female几乎各一半,那么即使在sex上建了索引也对查询效率起不了作用。

15、索引并不是越多越好,索引固然可以提高相应的 select 的效率,但同时也降低了 insert 及 update 的效率,因为 insert 或 update 时有可能会重建索引,所以怎样建索引需要慎重考虑,视具体情况而定。一个表的索引数最好不要超过6个,若太多则应考虑一些不常使用到的列上建的索引是否有 必要。

16.应尽可能的避免更新 clustered 索引数据列,因为 clustered 索引数据列的顺序就是表记录的物理存储顺序,一旦该列值改变将导致整个表记录的顺序的调整,会耗费相当大的资源。若应用系统需要频繁更新 clustered 索引数据列,那么需要考虑是否应将该索引建为 clustered 索引。

17、尽量使用数字型字段,若只含数值信息的字段尽量不要设计为字符型,这会降低查询和连接的性能,并会增加存储开销。这是因为引擎在处理查询和连接时会 逐个比较字符串中每一个字符,而对于数字型而言只需要比较一次就够了。

18、尽可能的使用 varchar/nvarchar 代替 char/nchar ,因为首先变长字段存储空间小,可以节省存储空间,其次对于查询来说,在一个相对较小的字段内搜索效率显然要高些。

19、任何地方都不要使用 select * from t ,用具体的字段列表代替“*”,不要返回用不到的任何字段。

20、尽量使用表变量来代替临时表。如果表变量包含大量数据,请注意索引非常有限(只有主键索引)。

21、避免频繁创建和删除临时表,以减少系统表资源的消耗。

22、临时表并不是不可使用,适当地使用它们可以使某些例程更有效,例如,当需要重复引用大型表或常用表中的某个数据集时。但是,对于一次性事件,最好使 用导出表。

23、在新建临时表时,如果一次性插入数据量很大,那么可以使用 select into 代替 create table,避免造成大量 log ,以提高速度;如果数据量不大,为了缓和系统表的资源,应先create table,然后insert。

24、如果使用到了临时表,在存储过程的最后务必将所有的临时表显式删除,先 truncate table ,然后 drop table ,这样可以避免系统表的较长时间锁定。

25、尽量避免使用游标,因为游标的效率较差,如果游标操作的数据超过1万行,那么就应该考虑改写。

26、使用基于游标的方法或临时表方法之前,应先寻找基于集的解决方案来解决问题,基于集的方法通常更有效。

27、与临时表一样,游标并不是不可使用。对小型数据集使用 FAST_FORWARD 游标通常要优于其他逐行处理方法,尤其是在必须引用几个表才能获得所需的数据时。在结果集中包括“合计”的例程通常要比使用游标执行的速度快。如果开发时 间允许,基于游标的方法和基于集的方法都可以尝试一下,看哪一种方法的效果更好。

28、在所有的存储过程和触发器的开始处设置 SET NOCOUNT ON ,在结束时设置 SET NOCOUNT OFF 。无需在执行存储过程和触发器的每个语句后向客户端发送 DONE_IN_PROC 消息。

29、尽量避免向客户端返回大数据量,若数据量过大,应该考虑相应需求是否合理。

30、尽量避免大事务操作,提高系统并发能力。

mysql如何优化插入记录速度



插入记录时,影响插入速度的主要是索引、唯一性校验、一次插入记录条数等。根据这些情况,可以分别进行优化,本节将介绍优化插入记录速度的几种方法。

一. 对于MyISAM引擎表常见的优化方法如下:

1. 禁用索引。对于非空表插入记录时,MySQL会根据表的索引对插入记录建立索引。如果插入大量数据,建立索引会降低插入记录的速度。为了解决这种情况可以在插入记录之前禁用索引,数据插入完毕后在开启索引。禁用索引的语句为: ALTER TABLE tb_name DISABLE KEYS; 重新开启索引的语句为: ALTER TABLE table_name ENABLE KEYS; 对于空表批量导入数据,则不需要进行此操作,因为MyISAM引擎的表是在导入数据之后才建立索引的。

2. 禁用唯一性检查:数据插入时,MySQL会对插入的记录进行唯一性校验。这种唯一性校验也会降低插入记录的速度。为了降低这种情况对查询速度的影响,可以在插入记录之前禁用唯一性检查,等到记录插入完毕之后再开启。禁用唯一性检查的语句为: SET UNIQUE_CHECKS=0; 开启唯一性检查的语句为: SET UNIQUE_CHECKS=1;

3. 使用批量插入。使用一条INSERT语句插入多条记录。如 INSERT INTO table_name VALUES(....),(....),(....)

4. 使用LOAD DATA INFILE批量导入当需要批量导入数据时,使用LOAD DATA INFILE语句导入数据的速度比INSERT语句快。

二.对于InnoDB引擎的表,常见的优化方法如下:

1. 禁用唯一性检查。同MyISAM引擎相同,通过 SET UNIQUE_CHECKS=0; 导入数据之后将该值置1。

2. 禁用外键检查。插入数据之前执行禁止对外键的查询,数据插入完成之后再恢复对外键的检查。禁用外键检查语句为: SET FOREIGN_KEY_CHECKS=0; 恢复对外键的检查语句为: SET FOREIGN_KEY_CHECKS=1;

3. 禁止自动提交。插入数据之前禁止事务的自动提交,数据导入完成之后,执行恢复自动提交操作。禁止自动提交语句为: SET AUTOCOMMIT=0; 恢复自动提交只需将该值置1。

mysql如何优化插入记录速度



插入记录时,影响插入速度的主要是索引、唯一性校验、一次插入记录条数等。根据这些情况,可以分别进行优化,本节将介绍优化插入记录速度的几种方法。

一. 对于MyISAM引擎表常见的优化方法如下:

1. 禁用索引。对于非空表插入记录时,MySQL会根据表的索引对插入记录建立索引。如果插入大量数据,建立索引会降低插入记录的速度。为了解决这种情况可以在插入记录之前禁用索引,数据插入完毕后在开启索引。禁用索引的语句为: ALTER TABLE tb_name DISABLE KEYS; 重新开启索引的语句为: ALTER TABLE table_name ENABLE KEYS; 对于空表批量导入数据,则不需要进行此操作,因为MyISAM引擎的表是在导入数据之后才建立索引的。

2. 禁用唯一性检查:数据插入时,MySQL会对插入的记录进行唯一性校验。这种唯一性校验也会降低插入记录的速度。为了降低这种情况对查询速度的影响,可以在插入记录之前禁用唯一性检查,等到记录插入完毕之后再开启。禁用唯一性检查的语句为: SET UNIQUE_CHECKS=0; 开启唯一性检查的语句为: SET UNIQUE_CHECKS=1;

3. 使用批量插入。使用一条INSERT语句插入多条记录。如 INSERT INTO table_name VALUES(....),(....),(....)

4. 使用LOAD DATA INFILE批量导入当需要批量导入数据时,使用LOAD DATA INFILE语句导入数据的速度比INSERT语句快。

二.对于InnoDB引擎的表,常见的优化方法如下:

1. 禁用唯一性检查。同MyISAM引擎相同,通过 SET UNIQUE_CHECKS=0; 导入数据之后将该值置1。

2. 禁用外键检查。插入数据之前执行禁止对外键的查询,数据插入完成之后再恢复对外键的检查。禁用外键检查语句为: SET FOREIGN_KEY_CHECKS=0; 恢复对外键的检查语句为: SET FOREIGN_KEY_CHECKS=1;

3. 禁止自动提交。插入数据之前禁止事务的自动提交,数据导入完成之后,执行恢复自动提交操作。禁止自动提交语句为: SET AUTOCOMMIT=0; 恢复自动提交只需将该值置1。

如何优化MySQL insert性能

1. 一条SQL语句插入多条数据。

常用的插入语句如:

INSERTINTO`insert_table` (`datetime`, `uid`, `content`, `type`) VALUES('0', 'userid_0', 'content_0', 0);

INSERTINTO`insert_table` (`datetime`, `uid`, `content`, `type`) VALUES('1', 'userid_1', 'content_1', 1);

修改成:

INSERTINTO`insert_table` (`datetime`, `uid`, `content`, `type`) VALUES('0', 'userid_0', 'content_0', 0), ('1', 'userid_1', 'content_1', 1);

修改后的插入操作能够提高程序的插入效率。这里第二种SQL执行效率高的主要原因有两个,一是减少SQL语句解析的操作, 只需要解析一次就能进行数据的插入操作,二是SQL语句较短,可以减少网络传输的IO。

这里提供一些测试对比数据,分别是进行单条数据的导入与转化成一条SQL语句进行导入,分别测试1百、1千、1万条数据记录。

性能测试:这里提供了同时使用上面两种方法进行INSERT效率优化的测试。即多条数据合并为同一个SQL,并且在事务中进行插入。

2. 在事务中进行插入处理。把插入修改成:

START TRANSACTION;

INSERTINTO`insert_table` (`datetime`, `uid`, `content`, `type`) VALUES('0', 'userid_0', 'content_0', 0);

INSERTINTO`insert_table` (`datetime`, `uid`, `content`, `type`) VALUES('1', 'userid_1', 'content_1', 1);

...

COMMIT;

使用事务可以提高数据的插入效率,这是因为进行一个INSERT操作时,MySQL内部会建立一个事务,在事务内进行真正插入处理。通过使用事务可以减少创建事务的消耗,所有插入都在执行后才进行提交操作。

这里也提供了测试对比,分别是不使用事务与使用事务在记录数为1百、1千、1万的情况。

从测试结果可以看到,insert的效率大概有50倍的提高,这个一个很客观的数字。

如何提升MySQL批量插入的效率

需要将大量数据(大概5W条)插入MySQL数
据库,用普通的SQL
Statement执行,时间大概是几分钟。于是想到用PreparedStatement,但是改了之后发现效率并没有很大的提升。不成,想到了
load data local
infile...命令,以前一直认为这条命令仅限MySQL终端上使用而不是标准的SQL语句,今天看了几篇文章之后有了很大的收获。

1. 使用PreparedStatement batch operation

以前使用PreparedStatement性能没有很大提升的原因在于:

没有使用批处理方法
在语句执行之前应关闭事务自动提交,语句执行完之后再提交

public
void batchLoad(Connection connection)

{

try
{

connection.setAutoCommit(false);

BufferedReader reader =
new BufferedReader(new
FileReader("tfacts_result"));

String sqlString =
"insert into test(node1, node2, weight) values(?, ?, ?)";

PreparedStatement pstmt = connection.prepareStatement(sqlString);

String line =
null;

while(true)

{

line = reader.readLine();

if(line == null)

{

break;

}

String[] columns = line.split("\t");

for(int
i = 1; i <= columns.length; i++)

{

pstmt.setString(i, columns[i-1]);

}

pstmt.addBatch();

}

pstmt.executeBatch();

connection.commit();

pstmt.close();

reader.close();

}
catch (FileNotFoundException e) {

e.printStackTrace();

}catch
(SQLException e){

e.printStackTrace();

}catch
(IOException e){

e.printStackTrace();

}

2.使用load data local infile into tabel XXX(注意在文件中用\t将每列数据隔开)

public
void loadData(Connection connection)

{

long
starTime = System.currentTimeMillis();

String sqlString =
"load data local infile ? into table test";

PreparedStatement pstmt;

try
{

pstmt = connection.prepareStatement(sqlString);

pstmt.setString(1,
"tfacts_result");

pstmt.executeUpdate();

pstmt.close();

}
catch (SQLException e) {

e.printStackTrace();

}

long
endTime = System.currentTimeMillis();

System.out.println("program runs "
+ (endTime - starTime) + "ms");

}

测试了5W条数据,PreparedStatement耗时10s,而load data infile耗时3s。

如何提升MySQL批量插入的效率

需要将大量数据(大概5W条)插入MySQL数
据库,用普通的SQL
Statement执行,时间大概是几分钟。于是想到用PreparedStatement,但是改了之后发现效率并没有很大的提升。不成,想到了
load data local
infile...命令,以前一直认为这条命令仅限MySQL终端上使用而不是标准的SQL语句,今天看了几篇文章之后有了很大的收获。

1. 使用PreparedStatement batch operation

以前使用PreparedStatement性能没有很大提升的原因在于:

没有使用批处理方法
在语句执行之前应关闭事务自动提交,语句执行完之后再提交

public
void batchLoad(Connection connection)

{

try
{

connection.setAutoCommit(false);

BufferedReader reader =
new BufferedReader(new
FileReader("tfacts_result"));

String sqlString =
"insert into test(node1, node2, weight) values(?, ?, ?)";

PreparedStatement pstmt = connection.prepareStatement(sqlString);

String line =
null;

while(true)

{

line = reader.readLine();

if(line == null)

{

break;

}

String[] columns = line.split("\t");

for(int
i = 1; i <= columns.length; i++)

{

pstmt.setString(i, columns[i-1]);

}

pstmt.addBatch();

}

pstmt.executeBatch();

connection.commit();

pstmt.close();

reader.close();

}
catch (FileNotFoundException e) {

e.printStackTrace();

}catch
(SQLException e){

e.printStackTrace();

}catch
(IOException e){

e.printStackTrace();

}

2.使用load data local infile into tabel XXX(注意在文件中用\t将每列数据隔开)

public
void loadData(Connection connection)

{

long
starTime = System.currentTimeMillis();

String sqlString =
"load data local infile ? into table test";

PreparedStatement pstmt;

try
{

pstmt = connection.prepareStatement(sqlString);

pstmt.setString(1,
"tfacts_result");

pstmt.executeUpdate();

pstmt.close();

}
catch (SQLException e) {

e.printStackTrace();

}

long
endTime = System.currentTimeMillis();

System.out.println("program runs "
+ (endTime - starTime) + "ms");

}

测试了5W条数据,PreparedStatement耗时10s,而load data infile耗时3s。

怎么提高Mysql执行sql导入的速度

1、如果mysql的data数据很少,内存足够大,可以把data防止到内存盘中。

linux如下设置内存盘:

mount -t ramfs none /ram

默认使用内存一半

如果内存不够大,系统有多个硬盘,则把mysql应用程序和data目录分开到不同硬盘上。

2、mysql的表设置为myiasm,比同等条件下的innodb能快20倍以上

3、导入完成以后才创建数据库索引

4、导入完成以后根据需要转换为其他engine,比如innodb

5、多条数据插入一个表,可以使用多记录方式:

insert into tablename values(’xxx’,'xxx’),(’yyy’,'yyy’)…;

6、如果多个mysql执行导入,可以使用delayed

insert delayed into tablename values(’sss’,’ssss’);

7、大文件sql文件可以用split分成多份再导

8、同等条件下,redhat比ubuntu强很多(几乎肯定)

mysql插入1000条数据到数据表中如何能加快速度

常用的插入语句如:

INSERT INTO `insert_table` (`datetime`, `uid`, `content`, `type`)

VALUES ('0', 'userid_0', 'content_0', 0);

INSERT INTO `insert_table` (`datetime`, `uid`, `content`, `type`)

VALUES ('1', 'userid_1', 'content_1', 1);

修改成:

INSERT INTO `insert_table` (`datetime`, `uid`, `content`, `type`)

VALUES ('0', 'userid_0', 'content_0', 0), ('1', 'userid_1', 'content_1', 1);

修改后的插入操作能够提高程序的插入效率。这里第二种SQL执行效率高的主要原因是合并后日志量(MySQL的binlog和innodb的事务让日志)减少了,降低日志刷盘的数据量和频率,从而提高效率。通过合并SQL语句,同时也能减少SQL语句解析的次数,减少网络传输的IO。

SQL语句是有长度,在进行数据合并在同一SQL中务必不能超过SQL长度,通过max_allowed_packet配置可以修改,默认是1M,测试时修改为8M。

Top