高并发
基准测试
环境
ThinkPad E14
CPU: AMD 4500U
内存: 16G
JDK: 1.8
测试工具:Jmeter
负载:1000线程 ramp-up 5秒 1000循环次数
执行语句
jmeter -n -t 测试计划.jmx -l results.log -e -o out
eg.1
@RequestMapping("buy/get")
public String buy_get() {
Jedis jedis = jedisPool.getResource();
String res = jedis.get("jedis:test-99");
jedis.close();
return res;
}
TPS: 13926.99 13899.51 18116.93
eg.2
@RequestMapping("buy/get2")
public String buy_get2() {
return RedisUtil.get("jedis:test-99");
}
TPS: 14203.74 18032.31 14112.73
eg.3
@RequestMapping("buy/get3")
public String buy_get3() {
ValueOperations operations = redisTemplate.opsForValue();
return (String) operations.get("RedisTemplate:test-99");
}
TPS: 11842.17 11517.69 11489.50
eg.4
@RequestMapping("buy/get4")
public String buy_get4() {
return RedisUtil2.strings().get("jedis:test-99");
}
TPS: 17779.36 12976.56 17614.63
eg.5
@RequestMapping("buy/get5")
public R buy_get5() {
return R("malu99");
}
TPS: 18562.52 21023.86 20304.16
eg.6
@RequestMapping("buy/get6")
public String buy_get6() {
return "malu99";
}
TPS: 21628.64 23030.86 22103.36
加入负载因子(限流计数器)
eg.1
加入负载因子对性能几乎没损耗,不过该方案线程不安全
private static int l = 0;
private static long my = 0;
@RequestMapping("buy/factor1")
public String buy_factor1() {
l++;
if ((l % 10) == 0) {
l = 0;
my++;
return "malu"+my;
} else {
return "error";
}
}
TPS: 22849.83 22617.27 21693.86
返回结果:
第一次 | 第二次 | 第三次 |
---|---|---|
malu99654 | malu99671 | malu99517 |
eg.2
接下来写入service来做更多的测试,首先测没有负载因子的情况:
buyController
@Autowired
buyService buyService;
@RequestMapping("buy/factor2")
public R buy_factor2() {
return buyService.factor2();
}
buyService
public interface buyService {
R factor2();
}
buyServiceImpl
@Service
public class buyServiceImpl implements buyService {
@Override
public R factor2() {
return R();
}
}
TPS: 19955.70 19022.98 20781.38
eg.3
使用私有静态属性,在多线程情况下不安全:
private static int l = 0;
private static long my = 0;
@Override
public R factor3(){
l++;
if ((l % 10) == 0) {
l = 0;
my++;
return R("malu"+my);
} else {
return R("error");
}
}
TPS: 22611.13 20362.87 19967.25
返回结果:
第一次 | 第二次 | 第三次 |
---|---|---|
malu99815 | malu99885 | malu99852 |
eg.4
为了线程安全,首先想到的是同步方法
/**
* 同步方法
*
* @return
*/
@Override
synchronized public R factor4() {
l++;
if ((l % 10) == 0) {
l = 0;
my++;
return R("malu" + my);
} else {
return R("error");
}
}
TPS: 18390.13 19664.91 18653.93
返回结果(线程安全):
第一次 | 第二次 | 第三次 |
---|---|---|
malu100001 | malu100001 | malu100001 |
eg.5
同步代码段一样可以
/**
* 同步代码段
*
* @return
*/
@Override
public R factor5() {
synchronized (buyServiceImpl.class) {
l++;
if ((l % 10) == 0) {
l = 0;
my++;
return R("malu" + my);
} else {
return R("error");
}
}
}
TPS: 19155.99 20210.59 20727.11
返回结果(线程安全):
第一次 | 第二次 | 第三次 |
---|---|---|
malu100001 | malu100001 | malu100001 |
eg.6
让我们试试原子类AtomicLong
//记录实际累加的数量
private static AtomicLong successNum = new AtomicLong(0);
/**
* 原子类 AtomicLong
*
* @return
*/
@Override
public R factor6() {
if ((successNum.incrementAndGet() % 10) == 0) {
my++;
return R("malu" + my);
} else {
return R("error");
}
}
TPS: 20164.14 19359.96 19009.24
返回结果(线程安全):
第一次 | 第二次 | 第三次 |
---|---|---|
malu100001 | malu100001 | malu100001 |
eg.7
来看JDK8里的LongAdder
private static LongAdder longAdder = new LongAdder();
/**
* LongAdder
*
* @return
*/
@Override
public R factor7() {
longAdder.increment();
if ((longAdder.sum() % 10) == 0) {
my++;
return R("malu" + my);
} else {
return R("error");
}
}
TPS: 18867.57 18704.17 20490.54
返回结果(没有锁,这样使用线程不安全):
第一次 | 第二次 | 第三次 |
---|---|---|
malu99766 | malu99868 | malu99664 |
zookeeper分布式锁
互斥锁(排他锁)
ZkConfig.java
/**
* 类说明: zookeeper配置
*
* @author : Malu
* @Since : 2020-07-30 15:15
**/
@Configuration
public class ZkConfig {
@Bean(initMethod = "start")
public CuratorFramework curatorFramework() {
return CuratorFrameworkFactory.newClient(
"192.168.3.27:2181",
5000,
5000,
new RetryNTimes(5, 5000));
}
}
ZkController.java
@Autowired
private CuratorFramework curatorFramework;
/**
* zookeeper互斥锁(排他锁),高并发测试
*
* @return R
*/
@GetMapping("/mutex2")
public R getMutex2() throws Exception {
// 获取一个分布式互斥锁
InterProcessMutex lock = new InterProcessMutex(curatorFramework, "/mutex_lock2");
try {
// 开启两个进程测试,会发现:如果一个分布式互斥锁获取了锁,那么直到锁释放为止数据都不会被侵扰
lock.acquire();
} catch (Exception e) {
e.printStackTrace();
} finally {
// 在finally中释放,防止死锁
lock.release();
}
return R();
}
环境:
CPU: i5 4590 @ 3.30 Ghz
内存: 32G
JDK: 1.8
测试工具:Jmeter
负载:200线程 ramp-up 5秒 10000循环次数
Windows10 本机zookeeper
TPS: 755.19
redisson分布式锁
pom.xml
<!-- https://mvnrepository.com/artifact/org.redisson/redisson-spring-boot-starter -->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.13.3</version>
</dependency>
application.yml
spring:
redis:
host: 192.168.1.47
port: 6381
database: 15
timeout: 10000
注意:Redis密码为空时不要加password参数,spring-boot自动装配会报错。
redissonController.java
@Autowired
private RedissonClient redissonClient;
@RequestMapping("lock2")
public R lock2() {
RLock lock = redissonClient.getLock("redisson_lock2");
lock.lock();
lock.unlock();
return R();
}
环境:
CPU: i5 4590 @ 3.30 Ghz
内存: 32G
JDK: 1.8
测试工具:Jmeter
负载:200线程 ramp-up 5秒 10000循环次数
独立Linux Redis
TPS: 1594.7 1594.08
可重入锁(tryLock)
/**
* RLock分布式可重入锁,tryLock
*
* @return
*/
@RequestMapping("lock5")
public R lock5() throws InterruptedException {
RLock lock = redissonClient.getLock("redisson_lock5");
boolean getLockOK = lock.tryLock();
if (getLockOK) {
try {
Thread.sleep(10 * 1000);
} catch (InterruptedException ie) {
ie.printStackTrace();
} finally {
lock.unlock();
}
return R("获取到锁");
} else {
return R("没有获取到锁");
}
}
环境:
CPU: i5 4590 @ 3.30 Ghz
内存: 32G
JDK: 1.8
测试工具:Jmeter
负载:200线程 ramp-up 5秒 10000循环次数
本机Windows Redis
TPS: 1913.48
环境:
CPU: i5 4590 @ 3.30 Ghz
内存: 32G
JDK: 1.8
测试工具:Jmeter
负载:200线程 ramp-up 5秒 10000循环次数
独立Linux Redis
TPS: 5751.29 5553.32 5686.2
参考
使用Redis搭建电商秒杀系统 https://tech.antfin.com/docs/2/63920
Redis高并发秒杀测试 https://github.com/14251104246/redis-demo