MySQL中Update的执行效率测试及验证

1、引言
某日,在讨论解决生产环境的问题时,一同事问说增加条件的Update语句效率是否更高?虽然我当时就有了自己的判断,但本着严谨的态度,我还是编写了测试代码,对MySQL中Update的执行效率进行测试及验证。

2、解决思路
1、根据生产环境的情况,模拟不同数据量的测试数据,分别模拟以下数据量的数据进行比较
1000 2000 5000
10000 20000 50000
100000 200000 500000
1000000 2000000 5000000

2、测试对比增加条件和不加条件两种的Update语句的执行时间

3、解决过程
3.1 数据表名
不同数据量存放在不同的数据表中,表名规则体现数据量,代码如下
// 根据数据量生成表名
private String getTableName(int count) {
	return "call_charge_" + count;
}


3.2 删除数据表
// 删除数据表
private void dropTable(String tableName) {
	String dropTableSql = "DROP TABLE IF EXISTS `" + tableName + "` ";
	jdbcTemplate.execute(dropTableSql);
}


3.3 创建数据表
// 创建数据表
private void createTable(String tableName) {
	String createTableSql = "CREATE TABLE `" + tableName + "` (";
	createTableSql += "`caller_flag` int(11) NOT NULL,";
	createTableSql += "`call_type` int(11) NOT NULL,";
	createTableSql += "`e164no` char(64) NOT NULL,";
	createTableSql += "`conf_begin_time` int(11) NOT NULL,";
	createTableSql += "`begin_time` int(11) NOT NULL,";
	createTableSql += "`end_time` int(11) NOT NULL,";
	createTableSql += "`correct_begin_time` int(11) NOT NULL,";
	createTableSql += "`correct_end_time` int(11) NOT NULL,";
	createTableSql += "`correct_begin_time_string` varchar(32) NOT NULL,";
	createTableSql += "`correct_end_time_string` varchar(32) NOT NULL,";
	createTableSql += "`opposite_call_type` int(11) NOT NULL,";
	createTableSql += "`opposite_e164no` char(64) NOT NULL,";
	createTableSql += "`opposite_conf_begin_time` int(11) NOT NULL,";
	createTableSql += "`company_guid` varchar(32) NOT NULL,";
	createTableSql += "`band_width` int(11) NOT NULL,";
	createTableSql += "`roam_flag` int(11) NOT NULL,";
	createTableSql += "`out_flag` int(11) NOT NULL,";
	createTableSql += "`incoming_flag` int(11) NOT NULL,";
	createTableSql += "`csu_guid` varchar(32) NOT NULL,";
	createTableSql += "KEY `correct_begin_time` (`correct_begin_time`),";
	createTableSql += "KEY `company_guid` (`company_guid`)";
	createTableSql += ") ENGINE=InnoDB DEFAULT CHARSET=utf8";
	jdbcTemplate.execute(createTableSql);
}


3.4 初始化数据
// 初始化数据
private void initData(String tableName, int count) {
	String insertSql = "insert into `" + tableName + "` ";
	insertSql += "(`caller_flag`, `call_type`, `e164no`, `conf_begin_time`, `begin_time`, `end_time`, ";
	insertSql += "`correct_begin_time`, `correct_end_time`, `correct_begin_time_string`, `correct_end_time_string`, ";
	insertSql += "`opposite_call_type`, `opposite_e164no`, `opposite_conf_begin_time`, `company_guid`, ";
	insertSql += "`band_width`, `roam_flag`, `out_flag`, `incoming_flag`, `csu_guid`) ";
	insertSql += "values('1','2','051211#999548','1420075106','1420075107','1420075188','1420066067','1420066148',";
	insertSql += "'2015-01-01 06:47:47','2015-01-01 06:49:08','1','0512114880046','2147483647','','960','0',";
	insertSql += "'0','0','%s')";

	for (int i = 0; i < count; i++) {
		if (i % 3 == 0) {
			jdbcTemplate.execute(String.format(insertSql, CSU_GUID1));
		} else {
			jdbcTemplate.execute(String.format(insertSql, CSU_GUID2));
		}
	}
}


3.5 复制数据
// 复制数据
private void copyData(String templateTableName, int count, int step) {
	String createSql = "CREATE TABLE `" + getTableName(count) + "` SELECT * FROM " + templateTableName;
	jdbcTemplate.execute(createSql);

	String copySql = "INSERT INTO `" + getTableName(count) + "` SELECT * FROM " + templateTableName;
	for (int i = step; i < count; i += step) {
		jdbcTemplate.update(copySql);
	}
}


3.6 公用测试代码
private String CSU_GUID1 = "10103000000006000000000000000001";
private String CSU_GUID2 = "10103000000006000000000000000002";
private String CSU_GUID3 = "10103000000006000000000000000003";

// 公用单元测试方法
private void doTest(String templateTableName, int count, int step) {
	dropTable(getTableName(count));
	copyData(templateTableName, count, step);
	doTest(count);
}

// 测试加条件和不加条件的update
private void doTest(int count) {
	long startTime = System.currentTimeMillis();
	String updateSql = "update " + getTableName(count) + " set csu_guid = '" + CSU_GUID3 + "' where csu_guid != '"
			+ CSU_GUID1 + "'";
	jdbcTemplate.update(updateSql);
	long endTime = System.currentTimeMillis();
	System.out.println(String.format("测试数据[%d]过滤Update=%d", count, endTime - startTime));

	startTime = System.currentTimeMillis();
	updateSql = "update " + getTableName(count) + " set csu_guid = '" + CSU_GUID2 + "'";
	jdbcTemplate.update(updateSql);
	endTime = System.currentTimeMillis();
	System.out.println(String.format("测试数据[%d]直接Update=%d", count, endTime - startTime));
}


3.7 Junit测试
@Test
public void prepareData() {
	dropTable("call_charge_data");
	createTable("call_charge_data");
	initData("call_charge_data", 100);
}

@Test
public void create1000() {
	doTest("call_charge_data", 1000, 100);
}

@Test
public void create2000() {
	doTest(getTableName(1000), 2000, 1000);
}

@Test
public void create5000() {
	doTest(getTableName(1000), 5000, 1000);
}

@Test
public void create10000() {
	doTest(getTableName(1000), 10000, 1000);
}

@Test
public void create20000() {
	doTest(getTableName(10000), 20000, 10000);
}

@Test
public void create50000() {
	doTest(getTableName(10000), 50000, 10000);
}

@Test
public void create100000() {
	doTest(getTableName(10000), 100000, 10000);
}

@Test
public void create200000() {
	doTest(getTableName(100000), 200000, 100000);
}

@Test
public void create500000() {
	doTest(getTableName(100000), 500000, 100000);
}

@Test
public void create1000000() {
	doTest(getTableName(100000), 1000000, 100000);
}

@Test
public void create2000000() {
	doTest(getTableName(1000000), 2000000, 1000000);
}

@Test
public void create5000000() {
	doTest(getTableName(1000000), 5000000, 1000000);
}


3.8 测试结果
--------------------------
Test[com.hero.test.update.TestMysqlUpdate.create1000] start...
测试数据[1000]过滤Update=30
测试数据[1000]直接Update=135
Test[com.hero.test.update.TestMysqlUpdate.create1000] end...
--------------------------
Test[com.hero.test.update.TestMysqlUpdate.create10000] start...
测试数据[10000]过滤Update=225
测试数据[10000]直接Update=852
Test[com.hero.test.update.TestMysqlUpdate.create10000] end...
--------------------------
Test[com.hero.test.update.TestMysqlUpdate.create100000] start...
测试数据[100000]过滤Update=7883
测试数据[100000]直接Update=43920
Test[com.hero.test.update.TestMysqlUpdate.create100000] end...
--------------------------
Test[com.hero.test.update.TestMysqlUpdate.create1000000] start...
测试数据[1000000]过滤Update=26170
测试数据[1000000]直接Update=460927
Test[com.hero.test.update.TestMysqlUpdate.create1000000] end...
--------------------------
Test[com.hero.test.update.TestMysqlUpdate.create2000] start...
测试数据[2000]过滤Update=53
测试数据[2000]直接Update=657
Test[com.hero.test.update.TestMysqlUpdate.create2000] end...
--------------------------
Test[com.hero.test.update.TestMysqlUpdate.create20000] start...
测试数据[20000]过滤Update=329
测试数据[20000]直接Update=2025
Test[com.hero.test.update.TestMysqlUpdate.create20000] end...
--------------------------
Test[com.hero.test.update.TestMysqlUpdate.create200000] start...
测试数据[200000]过滤Update=10867
测试数据[200000]直接Update=95978
Test[com.hero.test.update.TestMysqlUpdate.create200000] end...
--------------------------
Test[com.hero.test.update.TestMysqlUpdate.create2000000] start...
测试数据[2000000]过滤Update=42915
测试数据[2000000]直接Update=944865
Test[com.hero.test.update.TestMysqlUpdate.create2000000] end...
--------------------------
Test[com.hero.test.update.TestMysqlUpdate.create5000] start...
测试数据[5000]过滤Update=122
测试数据[5000]直接Update=1449
Test[com.hero.test.update.TestMysqlUpdate.create5000] end...
--------------------------
Test[com.hero.test.update.TestMysqlUpdate.create50000] start...
测试数据[50000]过滤Update=11848
测试数据[50000]直接Update=14608
Test[com.hero.test.update.TestMysqlUpdate.create50000] end...
--------------------------
Test[com.hero.test.update.TestMysqlUpdate.create500000] start...
测试数据[500000]过滤Update=14463
测试数据[500000]直接Update=222890
Test[com.hero.test.update.TestMysqlUpdate.create500000] end...
--------------------------
Test[com.hero.test.update.TestMysqlUpdate.create5000000] start...
测试数据[5000000]过滤Update=90726
测试数据[5000000]直接Update=2322410
Test[com.hero.test.update.TestMysqlUpdate.create5000000] end...
--------------------------
Test[com.hero.test.update.TestMysqlUpdate.prepareData] start...
Test[com.hero.test.update.TestMysqlUpdate.prepareData] end...

整理后的测试结果如下所示
数据量 加条件的执行时间(ms) 不加条件的执行时间(ms) 单元测试执行时间(s)
1000 30 135 5.103
2000 53 657 7.096
5000 122 1449 13.244
10000 225 852 9.859
20000 329 2025 26.353
50000 11848 14608 68.743
100000 7883 43920 102.544
200000 10867 95978 232.576
500000 14463 222890 560.074
1000000 26170 460927 1018.023
2000000 42915 944865 2052.403
5000000 90726 2322410 5145.207

特别说明:
测试结果,因机器及机器的工作状态而不同

4、总结
从测试结果的对比来看,增加条件的Update的执行效率明显比不加条件的Update的执行更短,效率也就更好

5、附录
1、测试基类BaseTest
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.rules.TestName;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

/**
 * Junit测试基类
 * @author [email protected]
 * @date 2014-7-28 下午5:27:34
 * @version 1.0.0
 */
// 使用@RunWith(SpringJUnit4ClassRunner.class),才能使测试运行于Spring测试环境
@RunWith(SpringJUnit4ClassRunner.class)
// @ContextConfiguration 注解有以下两个常用的属性:
// locations:可以通过该属性手工指定 Spring 配置文件所在的位置,可以指定一个或多个 Spring 配置文件
// inheritLocations:是否要继承父测试类的 Spring 配置文件,默认为 true
// 如果只有一个配置文件就直接写locations=“配置文件路径+名”
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class BaseTest {

	@Rule
	public TestName name = new TestName();

	@Before
	public void before() {
		System.out.println("--------------------------");
		System.out.println("Test[" + getClassName() + "." + getMethodName() + "] start...");
	}

	@After
	public void after() {
		System.out.println("Test[" + getClassName() + "." + getMethodName() + "] end...");
	}

	private String getClassName() {
		return getClass().getName();
	}

	private String getMethodName() {
		return name.getMethodName();
	}
}


2、Spring配置文件applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
	xsi:schemaLocation="http://www.springframework.org/schema/beans 
                     http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

	<!-- 配置数据源 -->
	<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
		<property name="driverClassName" value="com.mysql.jdbc.Driver" />
		<property name="url" value="jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8" />
		<property name="username" value="root" />
		<property name="password" value="" />
	</bean>

	<!-- 配置Spring  JdbcTemplate -->
	<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
		<property name="dataSource">
			<ref bean="dataSource" />
		</property>
	</bean>
</beans>

猜你喜欢

转载自cgs1999.iteye.com/blog/2343171
今日推荐