MySQL 版本:5.7.9
表结构:
CREATE TABLE `award` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`award` varchar(255) DEFAULT NULL,
`count` int(11) NOT NULL COMMENT '数量',
`created_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8
原理可参看:CountDownLatch 模拟高并发
package com.wlm.test.concurrent;
import java.util.concurrent.CountDownLatch;
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.wlm.test.award.AwardDao;
/**
* @author wengliemiao
*/
@Service
public class ConcurrentTest {
@Autowired
private AwardDao awardDao;
/**
* 线程数量
*/
public static final int THREAD_NUM = 50;
/**
* 开始时间
*/
private static long startTime = 0L;
@PostConstruct
public void init() {
try {
startTime = System.currentTimeMillis();
System.out.println("CountDownLatch started at: " + startTime);
// 初始化计数器为1
CountDownLatch countDownLatch = new CountDownLatch(1);
for (int i = 0; i < THREAD_NUM; i ++) {
new Thread(new Run(countDownLatch)).start();
}
// 启动多个线程
countDownLatch.countDown();
} catch (Exception e) {
System.out.println("Exception: " + e);
}
}
/**
* 线程类
*/
private class Run implements Runnable {
private CountDownLatch startLatch;
public Run(CountDownLatch startLatch) {
this.startLatch = startLatch;
}
@Override
public void run() {
try {
// 线程等待
startLatch.await();
// 执行操作
awardDao.update(4);
long endTime = System.currentTimeMillis();
System.out.println(Thread.currentThread().getName() + " ended at: " + endTime + ", cost: " + (endTime - startTime) + " ms.");
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
注:线程并发执行的时间取决于当时执行时的系统状态。
此处可以看出,并发对同一条数据进行update操作时,随着并发数的增高,执行时间增长。这是由于 MySQL 的 InnoDB 存储引擎对 CUD 操作默认加排他锁,因此一次只能有一条数据执行update操作。
可参看:MySQL行级锁
对以下几种情况进行测试:
1、数据量少时,执行5次,并发2000更新同一行数据性能测试结果为:
执行语句:
update `award` set `count` = `count` + 1 where `award` = '4'
有索引数据表结构为:
CREATE TABLE `award` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`award` varchar(255) DEFAULT NULL,
`count` int(11) NOT NULL COMMENT '数量',
`created_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `Index` (`award`,`count`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8
执行次数 | 未加索引 | 加索引 |
---|---|---|
1 | 2830ms | 3414ms |
2 | 3439ms | 2870ms |
3 | 2950ms | 2689ms |
4 | 2673ms | 3325ms |
5 | 3245ms | 2916ms |
从以上结果可以看出,数据量少时,有无索引情况下,并发更新同一行数据的性能大致相同。
2、数据量少时,执行5次,各并发1000更新不同行数据性能测试结果为:
执行次数 | 未加索引 | 加索引 |
---|---|---|
1 | 2884ms | 3002ms |
2 | 2988ms | 3007ms |
3 | 2827ms | 3184ms |
4 | 3147ms | 3763ms |
5 | 2941ms | 2675ms |
从以上结果可以看出,数据量少时,有无索引情况下,并发更新不同行数据的性能大致相同。
3、数据量大时,执行5次,并发10更新同一行数据性能测试结果为:
执行语句:
update `category2` set `updated_time` = CURRENT_TIMESTAMP() where `name` = 'cate_1000'
加索引数据表结构为:
CREATE TABLE `category2` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(220) NOT NULL,
`created_time` datetime NOT NULL,
`updated_time` datetime NOT NULL,
PRIMARY KEY (`id`),
KEY `Index` (`name`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=10277277 DEFAULT CHARSET=utf8
执行次数 | 未加索引 | 加索引 |
---|---|---|
1 | 55773ms | 427ms |
2 | 61479ms | 311ms |
3 | 55599ms | 355ms |
4 | 55280ms | 357ms |
5 | 53652ms | 350ms |
从以上数据可以看出,数据量大时,有索引并发更新同一行数据性能远远超过无索引。
4、数据量大时,执行5次,各并发5更新不同行数据性能测试结果为:
执行次数 | 未加索引 | 加索引 |
---|---|---|
1 | 57077ms | 318ms |
2 | 56290ms | 352ms |
3 | 55726ms | 361ms |
4 | 55465ms | 324ms |
5 | 55483ms | 365ms |
从以上数据可以看出,数据量大时,有索引并发更新不同行数据性能远远超过无索引。
结论:
1、数据量少时,有无索引对并发更新性能影响不大;
2、数据量大时,有索引并发更新性能远远超过无索引;
3、从以上数据看出,单纯执行update操作时,有无索引情况下并发更新不同行性能大致相同(可能是由于无索引情况下 update 占据的表锁在执行完当前 update 操作之后,自动释放,参看MySQL行级锁)。