mysql 计数并发_同步计数器的并发性能

一个常见的需求是统计网页的浏览量,我们将使用不同的技术方案来比较对并发性能的影响,常用的技术方案包括使用内存,redis,mysql等保存计数,这里为了突出个方案对并发性能造成的影响,我们将使用同步计数器,即计数完成之后才返回页面,我们将使用express.js框架来提供接口并使用autocannon工具进行压测。

首先我们看下一个简单的helloworld页面:

const express = require('express')

const app = express()

const port = 3000

app.get('/', (req, res) => {

res.send('Hello World!')

})

app.listen(port, () => console.log(`Example app listening at http://localhost:${port}`))

看一下这个页面的qps,平均2万1左右,我们将使用这个作为基准看各个方案对并发性能造成的影响:

Running 10s test @ http://localhost:3000

10 connections

┌─────────┬──────┬──────┬───────┬──────┬─────────┬─────────┬─────────┐

│ Stat │ 2.5% │ 50% │ 97.5% │ 99% │ Avg │ Stdev │ Max │

├─────────┼──────┼──────┼───────┼──────┼─────────┼─────────┼─────────┤

│ Latency │ 0 ms │ 0 ms │ 0 ms │ 1 ms │ 0.02 ms │ 0.14 ms │ 8.21 ms │

└─────────┴──────┴──────┴───────┴──────┴─────────┴─────────┴─────────┘

┌───────────┬─────────┬─────────┬─────────┬─────────┬──────────┬────────┬─────────┐

│ Stat │ 1% │ 2.5% │ 50% │ 97.5% │ Avg │ Stdev │ Min │

├───────────┼─────────┼─────────┼─────────┼─────────┼──────────┼────────┼─────────┤

│ Req/Sec │ 20543 │ 20543 │ 22159 │ 22287 │ 21785.46 │ 625.12 │ 20533 │

├───────────┼─────────┼─────────┼─────────┼─────────┼──────────┼────────┼─────────┤

│ Bytes/Sec │ 4.44 MB │ 4.44 MB │ 4.79 MB │ 4.81 MB │ 4.71 MB │ 135 kB │ 4.44 MB │

└───────────┴─────────┴─────────┴─────────┴─────────┴──────────┴────────┴─────────┘

Req/Bytes counts sampled once per second.

240k requests in 11.05s, 51.8 MB read

(1)首先,我们考虑使用内存计数器:

const express = require('express')

const app = express()

const port = 3000

let counter = 0;

app.get('/', (req, res) => {

counter++;

res.send('Hello World!')

})

app.get('/counter', (req, res) => {

res.json({ counter });

})

app.listen(port, () => console.log(`Example app listening at http://localhost:${port}`))

看一下并发性能,qps依旧保持在2万多,对性能几乎没有什么影响,但内存计数器的劣势也是很明显的,不支持集群,不能做持久化,node进程结束内存中的数据就丢了。

Running 10s test @ http://localhost:3000

10 connections

┌─────────┬──────┬──────┬───────┬──────┬─────────┬─────────┬──────────┐

│ Stat │ 2.5% │ 50% │ 97.5% │ 99% │ Avg │ Stdev │ Max │

├─────────┼──────┼──────┼───────┼──────┼─────────┼─────────┼──────────┤

│ Latency │ 0 ms │ 0 ms │ 1 ms │ 1 ms │ 0.04 ms │ 0.21 ms │ 16.01 ms │

└─────────┴──────┴──────┴───────┴──────┴─────────┴─────────┴──────────┘

┌───────────┬─────────┬─────────┬─────────┬─────────┬─────────┬─────────┬─────────┐

│ Stat │ 1% │ 2.5% │ 50% │ 97.5% │ Avg │ Stdev │ Min │

├───────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤

│ Req/Sec │ 13263 │ 13263 │ 21023 │ 21871 │ 20395.6 │ 2456.94 │ 13259 │

├───────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤

│ Bytes/Sec │ 2.87 MB │ 2.87 MB │ 4.54 MB │ 4.72 MB │ 4.41 MB │ 531 kB │ 2.86 MB │

└───────────┴─────────┴─────────┴─────────┴─────────┴─────────┴─────────┴─────────┘

Req/Bytes counts sampled once per second.

204k requests in 10.04s, 44.1 MB read

(2)要支持集群和持久化的话,我们首先想到的是使用redis,redis提供了INCR命令实现自增,很适合用作计数器:

const express = require('express')

const app = express()

const port = 3000

const redis = require('redis');

const client = redis.createClient();

app.get('/', (req, res) => {

client.incr('counter', function(err, reply) {

res.send('Hello World!')

});

})

app.get('/counter', (req, res) => {

client.get('counter', (err, counter) => {

res.json({ counter });

})

})

app.listen(port, () => console.log(`Example app listening at http://localhost:${port}`))

看一下并发性能,qps保持在1万9左右,比内存计数器要稍微少一点,但并没有损失多少,是一个很理想的替代方案。

Running 10s test @ http://localhost:3000

10 connections

┌─────────┬──────┬──────┬───────┬──────┬─────────┬─────────┬──────────┐

│ Stat │ 2.5% │ 50% │ 97.5% │ 99% │ Avg │ Stdev │ Max │

├─────────┼──────┼──────┼───────┼──────┼─────────┼─────────┼──────────┤

│ Latency │ 0 ms │ 0 ms │ 0 ms │ 1 ms │ 0.03 ms │ 0.22 ms │ 17.19 ms │

└─────────┴──────┴──────┴───────┴──────┴─────────┴─────────┴──────────┘

┌───────────┬─────────┬─────────┬─────────┬─────────┬──────────┬─────────┬─────────┐

│ Stat │ 1% │ 2.5% │ 50% │ 97.5% │ Avg │ Stdev │ Min │

├───────────┼─────────┼─────────┼─────────┼─────────┼──────────┼─────────┼─────────┤

│ Req/Sec │ 11895 │ 11895 │ 20431 │ 20767 │ 19630.91 │ 2464.33 │ 11889 │

├───────────┼─────────┼─────────┼─────────┼─────────┼──────────┼─────────┼─────────┤

│ Bytes/Sec │ 2.57 MB │ 2.57 MB │ 4.41 MB │ 4.49 MB │ 4.24 MB │ 533 kB │ 2.57 MB │

└───────────┴─────────┴─────────┴─────────┴─────────┴──────────┴─────────┴─────────┘

Req/Bytes counts sampled once per second.

216k requests in 11.05s, 46.6 MB read

(3)我们在来看使用mysql的话,性能会影响多少,首先我们来设计一张表来保存计数:

CREATE TABLE `hit_counter` (

`id` int NOT NULL,

`cnt` int unsigned NOT NULL,

PRIMARY KEY (`id`)

) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT INTO `hit_counter`(`id`, `cnt`) VALUES (1, 0);

代码如下:

const express = require('express')

const app = express()

const port = 3000

const mysql = require('mysql');

const connection = mysql.createConnection({

host: 'localhost',

user: 'root',

password: '******',

database: 'webapp'

});

connection.connect();

app.get('/', (req, res) => {

connection.query('update hit_counter set cnt = cnt + 1 where id = 1', function(err, results) {

res.send('Hello World!')

});

})

app.get('/counter', (req, res, next) => {

connection.query('select * from hit_counter where id = 1', function(err, results) {

if (err) next(err);

const counter = results[0].cnt;

res.json({ counter });

})

})

app.listen(port, () => console.log(`Example app listening at http://localhost:${port}`))

我们看下并发性能,发现qps降到了2千多,差不多只有原接口的1/10,响应时间也由原来的0.02ms增加到4.14ms,对性能的影响还是很大的。

Running 10s test @ http://localhost:3000

10 connections

┌─────────┬──────┬──────┬───────┬──────┬─────────┬─────────┬──────────┐

│ Stat │ 2.5% │ 50% │ 97.5% │ 99% │ Avg │ Stdev │ Max │

├─────────┼──────┼──────┼───────┼──────┼─────────┼─────────┼──────────┤

│ Latency │ 4 ms │ 4 ms │ 5 ms │ 7 ms │ 4.14 ms │ 0.59 ms │ 14.35 ms │

└─────────┴──────┴──────┴───────┴──────┴─────────┴─────────┴──────────┘

┌───────────┬────────┬────────┬────────┬────────┬─────────┬─────────┬────────┐

│ Stat │ 1% │ 2.5% │ 50% │ 97.5% │ Avg │ Stdev │ Min │

├───────────┼────────┼────────┼────────┼────────┼─────────┼─────────┼────────┤

│ Req/Sec │ 1925 │ 1925 │ 2157 │ 2193 │ 2135.73 │ 71.85 │ 1925 │

├───────────┼────────┼────────┼────────┼────────┼─────────┼─────────┼────────┤

│ Bytes/Sec │ 416 kB │ 416 kB │ 466 kB │ 474 kB │ 461 kB │ 15.5 kB │ 416 kB │

└───────────┴────────┴────────┴────────┴────────┴─────────┴─────────┴────────┘

Req/Bytes counts sampled once per second.

23k requests in 11.03s, 5.07 MB read

总结

在上面的例子中,我们使用了同步计数器来比较内存、redis和mysql对并发性能的影响,内存和redis对性能的影响较小,mysql影响较大,而且redis还可以使用在集群中并且支持持久化,因此是最佳选择。在真实的使用场景中,对于计数器,我们一般使用异步的方式,因此对性能影响不会有较大差异,但使用redis依旧是很好的选择。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值