应用场景
计数器在现在的网站是很经常出现的,如计算该网站的点击率、该文件的下载次数、网站的访问量等。
一般设计
在不考虑其他业务的情况下,只考虑实现一个计数器的思路。我们会创建一张独立的表存储计数值。
创建一个counter表来表示计数器:
CREATE TABLE counter ( count INT UNSIGNED NOT NULL );
初始化:
INSERT INTO counter VALUES(0);
计数器工作sql:
UPDATE counter SET count = count + 1;
测试100个线程并发工作执行语句10次:
mysqlslap -u root -p --concurrency=100 --iterations=100 --create-schema=‘test‘ --query=‘update counter set count = count + 1‘ --debug-info
结果:
Benchmark Average number of seconds to run all queries: 0.101 seconds Minimum number of seconds to run all queries: 0.026 seconds Maximum number of seconds to run all queries: 0.906 seconds Number of clients running queries: 100 Average number of queries per client: 1 User time 0.15, System time 0.84 Maximum resident set size 7024, Integral resident set size 0 Non-physical pagefaults 53513, Physical pagefaults 0, Swaps 0 Blocks in 0 out 0, Messages in 0 out 0, Signals 0 Voluntary context switches 59935, Involuntary context switches 6357
优化设计
对于在现在高并发的时代,影响着这个计数器的并发性的是一个mutex,为了保证事务的正确性,只能加锁,一加锁这些事务只能串行运行。为了获得高并发性能,我将计数的数据保存在多行中,每次执行都不一定会锁在同一行,导致等待。
创建表:
CREATE TABLE counter ( id TINYINT UNSIGNED NOT NULL PRIMARY KEY, count INT UNSIGNED NOT NULL );
初始化:
CREATE PROCEDURE loadCounter(number INT UNSIGNED) BEGIN DECLARE i INT UNSIGNED DEFAULT 0; WHILE i < number DO INSERT INTO counter SELECT i, 0; COMMIT; SET i = i + 1; END WHILE; END; CALL loadCounter(100);
更新计数器:
UPDATE counter SET count = count + 1 WHERE id = FLOOR(RAND()*100);
获取计数值:
SELECT SUM(count) FROM counter;
测试100个线程并发工作执行语句10次:
mysqlslap -u root -p --concurrency=100 --iterations=100 --create-schema=‘test‘ --query=‘UPDATE counter SET count = count + 1 WHERE id = FLOOR(RAND()*100);‘ --debug-info
结果:
Benchmark Average number of seconds to run all queries: 0.091 seconds Minimum number of seconds to run all queries: 0.022 seconds Maximum number of seconds to run all queries: 0.347 seconds Number of clients running queries: 100 Average number of queries per client: 1 User time 0.20, System time 0.83 Maximum resident set size 5260, Integral resident set size 0 Non-physical pagefaults 53218, Physical pagefaults 1, Swaps 0 Blocks in 8 out 0, Messages in 0 out 0, Signals 0 Voluntary context switches 64740, Involuntary context switches 1588
优化思路
我们对比两者的测试结果,发现优化后的设计在执行上是有了提高,很执行sql的最长时间中,优化后时间降下了2倍,是因为少了等待锁的时间。
一般设计的并发性差的原因是因为我们的执行目标都在一条记录上,多个线程同时执行的话,很容易产生互斥。优化设计将目标扩散到了多个记录中。事务的等待时间久大大减少了,效率就高了很多。
时间: 2024-11-08 23:53:45