TL;DR

前言

这是一篇去年8月写的文章,本来是希望达到10k, 不过实际测试并没有成功,最终达到了1000+的事务,完成C10K的一个K,还有9个 :(

DB选择

DB 类型 业务 io
Mysql sql,支持事务,有缓存 强sql,强类型,类似java B & B+
Postgres sql,支持事务
Mongo nosql,key-value DB,读很快 弱sql,非强制,类似js B+
Redis nosql just kv,代码复杂
sqlite3 sql 强sql,支持事务 Btree

初步结论,理论上是随便用,先用sqlite3把,如果性能达不到,再来排查原因和更改DB

业务场景

我们设计一个sticker网站,可以售卖sticker,用户可以购买sticker,sticker是有库存的,如果用户的余额足够,则可以售卖,另外售卖的过程中,如果库存没了的话,需要回滚订单。

主要对象

  • 用户
  • 商品,库存字段也在商品身上
  • 订单

主要流程

  • 商品的详情页面,get v1/api/sticker/:id
  • 商品的点赞+1接口,post v1/api/sticker/likes
  • 下单接口,post v1/api/order
  • 订单查看接口,get v1/api/order/:id

表现

get 接口 读

load

PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 
10154 ubuntu 20 0 1757000 36888 10020 R 362.7 0.1 2:48.41 main 
9046 ubuntu 20 0 2125668 31976 9876 S 346.3 0.1 2:51.78 server

QPS表现

耗时 并发数 成功数 失败数 qps 最长耗时 最短耗时 平均耗时 下载字节 字节每秒 状态码
1s 100 14296 0 15228.54 39.92 0.28 6.57 2,201,584 2,201,538 200:14296
2s 100 29744 0 15753.80 39.92 0.28 6.35 4,580,576 2,290,266 200:29744
3s 100 45190 0 15899.55 39.92 0.28 6.29 6,959,260 2,315,325 200:45228

下单接口 事务写

PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 
9046 ubuntu 20 0 5731688 145260 10004 S 11.0 0.4 8:56.24 server 
10310 ubuntu 20 0 1535804 21276 9868 S 2.3 0.1 0:45.78 main
耗时 并发数 成功数 失败数 qps 最长耗时 最短耗时 平均耗时 下载字节 字节每秒 状态码
1s 78 234 0 462.62 921.42 0.88 216.16 4,854 4,853 200:234
2s 96 468 0 339.03 1723.88 0.88 294.95 9,632 4,813 200:468
3s 97 671 0 300.53 2843.52 0.88 332.75 13,697 4,564 200:671
4s 99 891 0 267.94 3300.58 0.85 373.22 18,217 4,553 200:891
5s 100 1127 0 265.51 4121.60 0.85 376.64 22,887 4,577 200:1127
6s 100 1352 0 254.29 4121.60 0.84 393.26 27,634 4,605 200:1352
7s 100 1556 0 243.91 5965.82 0.84 409.99 31,668 4,523 200:1556
8s 100 1750 0 244.42 5965.82 0.84 409.13 35,364 4,419 200:1750
9s 100 2014 0 242.56 5965.82 0.84 412.27 40,688 4,520 200:2014
10s 100 2266 0 242.84 5965.82 0.79 411.79 45,980 4,597 200:2266
11s 100 2505 0 243.15 6417.73 0.79 411.28 50,668 4,606 200:2505
12s 100 2769 0 243.57 6417.73 0.79 410.56 56,198 4,683 200:2769
13s 100 2954 0 240.99 6417.73 0.72 414.96 59,611 4,585 200:2954
14s 100 3201 0 240.45 6417.73 0.72 415.89 64,578 4,612 200:3201
15s 100 3437 0 237.23 6417.73 0.72 421.53 69,516 4,634 200:3437
16s 100 3622 0 235.57 6417.73 0.72 424.51 73,425 4,588 200:3622
17s 100 3800 0 235.41 6417.73 0.72 424.79 76,927 4,525 200:3800
18s 100 3990 0 234.07 6417.73 0.72 427.21 80,603 4,477 200:3990
19s 100 4176 0 233.61 6417.73 0.72 428.07 84,177 4,430 200:4176
20s 100 4328 0 229.46 6417.73 0.72 435.81 87,093 4,354 200:4328
21s 100 4506 0 229.19 6417.73 0.72 436.32 90,583 4,313 200:4506
22s 100 4701 0 222.47 6417.73 0.72 449.51 94,600 4,299 200:4701
23s 100 4862 0 219.28 6417.73 0.72 456.03 97,841 4,253 200:4862
24s 100 5031 0 216.87 6417.73 0.72 461.10 101,150 4,214 200:503
25s 100 5199 0 216.08 6417.73 0.72 462.78 104,284 4,171 200:519
26s 100 5374 0 213.84 6417.73 0.72 467.64 107,871 4,148 200:537
27s 100 5546 0 212.53 6417.73 0.72 470.52 111,251 4,120 200:554
28s 100 5718 0 211.58 6417.73 0.72 472.63 114,633 4,093 200:571
29s 100 5893 0 208.82 6417.73 0.72 478.87 118,122 4,073 200:58
iostat -d -m 1 10

Device tps MB_read/s MB_wrtn/s MB_dscd/s MB_read MB_wrtn MB_dscd
xvda 701.00 0.00 7.82 0.00 0 7 0 
xvda 867.00 0.00 9.66 0.00 0 9 0 
xvda 990.00 0.00 11.00 0.00 0 11 0 
xvda 906.00 0.00 10.75 0.00 0 10 0 
xvda 787.00 0.00 8.62 0.00 0 8 0 
xvda 240.00 0.00 2.64 0.00 0 2 0 
xvda 387.00 0.00 4.25 0.00 0 4 0

问题出现了

tps 只有200,而且是在8核32G内存的机器上,cpu没有瓶颈,内存也没有,问题出在哪里?

1 慢sql

下单的时候,会读取用户和产品表,此时慢sql

2022/08/04 09:55:45 /home/ubuntu/go/src/c10k-go/cmd/server/server.go:71 SLOW SQL >= 200ms [1210.759ms] [rows:1] SELECT * FROM `users` WHERE `name` = "demo-9475" AND `users`.`deleted_at` IS NULL ORDER BY `users`.`id` LIMIT 1

2022/08/04 09:55:45 /home/ubuntu/go/src/c10k-go/cmd/server/server.go:81 SLOW SQL >= 200ms [929.940ms] [rows:1] SELECT * FROM `products` WHERE `code` = "Sticker" AND `products`.`deleted_at` IS NULL ORDER BY `products`.`id` LIMIT 1

原因

查询的时候,没有索引,慢sql

解决

索引: 加索引可以解决部分问题

in-memory: 如果我们使用sqlite的in-memory 模式,这样避免从磁盘读取,速度更快

预热: 预先放到缓存,读缓存的数据,从一个树的查找 O(log n), 放缓存的话,时间复杂度是 O(1)

三种方案,应该有一个数量级的差异,晚些看下数据对比

2 锁表导致的

2022/08/04 09:55:45 /home/ubuntu/go/src/c10k-go/cmd/server/db.go:59 database is locked [0.061ms] [rows:0] UPDATE `products` SET `created_at`="2022-08-04 09:40:45.165",`updated_at`="2022-08-0 09:55:45.644",`deleted_at`=NULL,`code`="Sticker",`price`=100,`quantity`=99971357 WHERE `products`.`deleted_at` IS NULL AND `id` = 1 create order failed: 2

原因-为啥锁表报错

业务场景:这里其实是扣减库存,扣减的是商品的库存,来更新的库存表,update的时候,更新库存-1

报错:这里有锁表导致的事务失败,怎么解决?

| operationalerror-database-is-locked

搜索推荐是切换DB,更换掉sqlite,是一种解决方案,

锁表报错解决

业务:先看库存这个业务,他其实是一个有限的资源,一般来讲,是没办法超卖的,比如说你有100件手机,你不能卖出去101件,第101件你是发不出货的,卖出去后,就是超卖了,发不出货就会有投诉,退款等麻烦的事情。

实物和虚拟的区别:不过我们这里测试的时候,库存其实是无限的,不太担心超卖的,数量极大,这就是卖虚拟的东西和实物的区别,虚拟的可以随便卖,履约成本无限低,不像实物,所以虚拟商品,一般来讲,库存超卖相对还好

事务: 首先锁表商品表,减库存,创建订单,创单成功后,提交事务。 如果床单失败,则回滚事务,加上库存。

系统:可以看到这里的压力有两点,一个是减库存,对商品表的压力,一个是创建订单,对订单表的压力。

锁资源:我们知道一条record 更新的时候,一般是record 锁和索引锁,新增的时候,会有db的table 级别的锁.

| 锁级别 innodb-locks-set

减库存流程:

  • 如果商品和库存在一个表里面的话,库存频繁更新的话,那么商品读取也会受到影响,解法是拆分表,相当于垂直拆分,缓解压力
  • 另外对于单个热点商品来讲,减库存的流程,其实相当于秒杀场景,大量的流量流入单个商品,单个库存,这里其实有数据库热点的,有热点就好办,最直观的解法,肯定是把热点与非热点拆分开来,特殊对待,为啥特殊对待? 毕竟资源是有限的呀
  • 解法: 我们现在压测,而且是单个产品,相当于一个秒杀场景,所以把库存单独拿出来,放到redis缓存,利用redis的Decr 来减库存。

订单创建流程:

  • 这里的压力主要是表锁,新增记录。
  • 解法很简单,水平扩容即可,分库分表来缓解新增的压力,暂时不加,看下效果

限流:

  • 限流是为了保护集群,比如一个餐厅,最多提供100人同时就餐,如果来了1000人,餐厅会挤得水泄不通,资源可能会到非就餐人员身上,正常就餐受到影响,反而提供不了100人同时就餐服务了。
  • 我们压测,可以动态控制流量,所以暂时不加限流

解决1-读加缓存

读的地方,都加到缓存里面

load

PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 
16869 root 20 0 2570232 63384 9648 S 57.3 3.1 1:31.96 server 
16941 root 20 0 1600468 22360 9172 S 23.7 1.1 0:37.09 main

并发表现:TPS 700 左右,latency最长13s,平均0.1 s,可接受的范围

耗时 并发数 成功数 失败数 qps 最长耗时 最短耗时 平均耗时 下载字节 字节每秒 状态码
235s 100 173411 0 740.00 13486.30 0.25 135.14 3,790,647 16,130 200:173411
236s 100 174178 0 740.61 13486.30 0.25 135.02 3,807,690 16,134 200:174178
237s 100 174925 0 740.56 13486.30 0.25 135.03 3,823,497 16,132 200:174925
238s 100 175642 0 740.56 13486.30 0.25 135.03 3,838,482 16,128 200:175642
239s 100 176435 0 740.85 13486.30 0.25 134.98 3,855,447 16,131 200:176435
240s 100 177571 0 741.99 13486.30 0.25 134.77 3,882,983 16,178 200:177571
241s 100 178609 0 743.09 13486.30 0.25 134.57 3,907,901 16,215 200:178609
242s 100 179479 0 744.23 13486.30 0.25 134.37 3,927,131 16,227 200:179479

慢SQL 相关报错日志

2022/08/06 12:44:55 /root/go/src/c10k-go/cmd/server/db.go:59 database is locked [0.047ms] [rows:0] UPDATE `products` SET `created_at`="2022-08-06 12:11:54.183",`updated_at`="2022-08-06 12:44:55.893",`deleted_at`=NULL,`code`="Sticker",`price`=100,`quantity`=99989897 WHERE `products`.`deleted_at` IS NULL AND `id` = 1 create order failed: 2

2022/08/06 12:44:55 /root/go/src/c10k-go/cmd/server/db.go:49 SLOW SQL >= 200ms [329.423ms] [rows:1] SELECT * FROM `products` WHERE `products`.`deleted_at` IS NULL AND `products`.`id` = 1 ORDER BY `products`.`id` LIMIT 1

一个是product 锁表导致的,一个是下单报错,可以看到,压力全在product库,原因就是库存在这里

怎么办? 继续拆,把库存拆出去,但需要考虑一致性。 把减库存的压力放到redis,这样的话,product没压力了,事务就是减redis成功后,再增加order即可

解决2-库存拆分

PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 
18969 root 20 0 2570512 34096 11160 S 28.3 1.7 1:00.64 server 
19344 root 20 0 1305540 17000 9080 S 13.3 0.8 0:12.72 main

TPS表现:结果更慢了,TPS 400+, why?

耗时 并发数 成功数 失败数 qps 最长耗时 最短耗时 平均耗时 下载字节 字节每秒 状态码
105s 50 44999 0 431.17 5017.35 1.33 115.96 585,291 5,574 200:44999
106s 50 45339 0 430.80 5017.35 1.33 116.06 589,711 5,563 200:45339
107s 50 45680 0 430.20 5017.35 1.33 116.23 594,144 5,552 200:45680
108s 50 45840 0 429.09 5017.35 1.33 116.53 596,224 5,520 200:45840
109s 50 46161 0 427.25 5017.35 1.33 117.03 600,397 5,508 200:46161
110s 50 46461 0 426.08 5017.35 1.33 117.35 604,313 5,493 200:46461

慢sql,现在压力全部在order库了,新增的时候,锁表导致的order 大于200ms, 本来均摊到两个表的压力,现在全部到一个表了,结果更慢了,当然也可能是order表数据变大了,先不细究

2022/08/06 13:06:05 /root/go/src/c10k-go/cmd/server/db.go:60 SLOW SQL >= 200ms [2029.641ms] [rows:1] INSERT INTO `orders` (`created_at`,`updated_at`,`deleted_at`,`user_id`,`product_id`,`status`) VALUES ("2022-08-06 13:06:03.588","2022-08-06 13:06:03.588",NULL,1,1,"pending") RETURNING `id`,`status`

2022/08/06 13:06:05 /root/go/src/c10k-go/cmd/server/db.go:60 SLOW SQL >= 200ms [429.022ms] [rows:1] INSERT INTO `orders` (`created_at`,`updated_at`,`deleted_at`,`user_id`,`product_id`,`status`) VALUES ("2022-08-06 13:06:05.324","2022-08-06 13:06:05.324",NULL,1,1,"pending") RETURNING `id`,`status`

解决3-订单单表新增

就剩下一个问题了,怎么提高单表的事务插入速度?

sqlite 切换成inmemory 试下看看,结果不贴了,反正是不行,可能是当时的机器不行 :(

| inmemory

切换成mysql

load ,两核的小鸡,不堪重负,mysql 就吃完了

PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 
22971 lxd 20 0 1856120 511704 30824 S 69.4 25.3 5:59.03 mysqld 
27494 root 20 0 1453036 18376 9892 S 47.2 0.9 0:54.78 server 
22928 root 20 0 1517876 8208 2476 S 34.6 0.4 1:23.44 docker-proxy 
27559 root 20 0 1379016 18584 9304 S 29.9 0.9 0:33.23 main

TPS表现:效果是显著的,并发数 1200+了

耗时 并发数 成功数 失败数 qps 最长耗时 最短耗时 平均耗时 下载字节 字节每秒 状态码
91s 50 109297 0 1203.37 456.32 2.19 41.55 1,420,861 15,613 200:109297
92s 50 110507 0 1203.60 456.32 2.19 41.54 1,436,591 15,615 200:110507
93s 50 111691 0 1203.39 456.32 2.19 41.55 1,451,983 15,612 200:111691
94s 50 112890 0 1203.36 456.32 2.19 41.55 1,467,570 15,612 200:112890
95s 50 114023 0 1202.61 456.32 2.19 41.58 1,482,299 15,603 200:114023

慢sql

2022/08/06 13:50:02 /root/go/src/c10k-go/cmd/server/db.go:60 SLOW SQL >= 200ms [205.602ms] [rows:1] INSERT INTO `orders` (`created_at`,`updated_at`,`deleted_at`,`user_id`,`product_id`,`status`) VALUES ('2022-08-06 13:50:02.278','2022-08-06 13:50:02.278',NULL,1,1,'pending')

2022/08/06 13:50:25 /root/go/src/c10k-go/cmd/server/db.go:60 SLOW SQL >= 200ms [238.287ms] [rows:1] INSERT INTO `orders` (`created_at`,`updated_at`,`deleted_at`,`user_id`,`product_id`,`status`) VALUES ('2022-08-06 13:50:25.573','2022-08-06 13:50:25.573',NULL,1,1,'pending')

2022/08/06 13:50:25 /root/go/src/c10k-go/cmd/server/db.go:60 SLOW SQL >= 200ms [200.938ms] [rows:1] INSERT INTO `orders` (`created_at`,`updated_at`,`deleted_at`,`user_id`,`product_id`,`status`) VALUES ('2022-08-06 13:50:25.699','2022-08-06 13:50:25.699',NULL,1,1,'pending')

2022/08/06 13:50:48 /root/go/src/c10k-go/cmd/server/db.go:60 SLOW SQL >= 200ms [244.081ms] [rows:1] INSERT INTO `orders` (`created_at`,`updated_at`,`deleted_at`,`user_id`,`product_id`,`status`) VALUES ('2022-08-06 13:50:48.284','2022-08-06 13:50:48.284',NULL,1,1,'pending')

慢sql怎么解决?

走批量插入,还是继续分片? 要求是2k可以不?

还是先加机器吧,继续加cpu,看下瓶颈在哪里,换成8核16G再来战

加完机器 tps 650+??? what happend?

耗时 并发数 成功数 失败数 qps 最长耗时 最短耗时 平均耗时 下载字节 字节每秒 状态码
47s 100 31555 0 676.78 872.18 22.54 147.76 410,215 8,727 200:31555
48s 100 32018 0 671.71 872.18 22.54 148.87 416,234 8,671 200:32018
49s 100 32476 0 667.29 872.18 22.54 149.86 422,188 8,615 200:32476
50s 100 33035 0 664.77 872.18 22.54 150.43 429,455 8,586 200:33035
51s 100 33613 0 663.74 872.18 22.54 150.66 436,969 8,567 200:33613
52s 100 34161 0 661.17 872.18 22.54 151.25 444,093 8,540 200:34161

解决4-db最大连接数

2022/08/07 12:17:20 /root/go/src/c10k-go/cmd/server/db.go:60 SLOW SQL >= 200ms [268.386ms] [rows:1] INSERT INTO `orders` (`created_at`,`updated_at`,`deleted_at`,`user_id`,`product_id`,`status`) VALUES ('2022-08-07 12:17:20.273','2022-08-07 12:17:20.273',NULL,1,1,'pending')

2022/08/07 12:17:20 /root/go/src/c10k-go/cmd/server/db.go:60 SLOW SQL >= 200ms [276.732ms] [rows:1] INSERT INTO `orders` (`created_at`,`updated_at`,`deleted_at`,`user_id`,`product_id`,`status`) VALUES ('2022-08-07 12:17:20.265','2022-08-07 12:17:20.265',NULL,1,1,'pending')

create order failed: 3 [mysql] 2022/08/07 12:18:19 packets.go:37: unexpected EOF [mysql] 2022/08/07 12:18:19 packets.go:37: unexpected EOF [mysql] 2022/08/07 12:18:19 packets.go:37: unexpected EOF [mysql] 2022/08/07 12:18:19 packets.go:37: unexpected EOF [mysql] 2022/08/07 12:18:19 packets.go:37: unexpected EOF

一看就是最大链接数问题,看下gorm的最大连接数

| Golang MySQL error - packets.go:33: unexpected EOF

sqlDB, _ := gormDB.DB()
sqlDB.SetMaxIdleConns(10)
sqlDB.SetMaxOpenConns(100)

解决5-docker-proxy load 高

PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 
2046 root 20 0 5730236 13968 2832 S 467.7 0.1 10:17.72 docker-proxy 
2088 lxd 20 0 5014264 499052 33452 R 128.0 3.0 9:57.75 mysqld 
2706 root 20 0 1526768 22588 10020 S 52.0 0.1 3:07.06 server 
20389 root 20 0 1748444 21524 9208 S 33.7 0.1 0:40.35 main 
778 root 20 0 57832 9152 6724 S 5.7 0.1 0:19.09 redis-server

mysql 使用的是docker,看到网络肯定是挂了 mysql的load 基本在2左右 server 0.6 压测client 0.4 redis 0.05 左右

解决:不使用docker了,直接裸机mysql,docker-proxy 就不会是问题了

TPS 恢复 1000+

耗时 并发数 成功数 失败数 qps 最长耗时 最短耗时 平均耗时 下载字节 字节每秒 状态码
61s 100 61452 0 1019.02 612.01 18.68 98.13 798,876 13,096 200:61452
62s 100 62260 0 1015.74 612.01 18.68 98.45 809,380 13,054 200:62260
63s 100 63122 0 1014.10 612.01 18.68 98.61 820,586 13,025 200:63122
64s 100 63996 0 1011.60 612.01 18.68 98.85 831,948 12,998 200:63996
65s 100 64749 0 1007.47 612.01 18.68 99.26 841,737 12,949 200:64749
66s 100 65603 0 1004.88 612.01 18.68 99.51 852,839 12,921 200:65603
67s 100 66556 0 1005.23 612.01 18.68 99.48 865,228 12,913 200:66556
68s 100 67401 0 1003.13 612.01 18.68 99.69 876,213 12,885 200:67401
69s 100 68347 0 1002.32 612.01 18.68 99.77 888,511 12,876 200:68347
70s 100 69240 0 1001.25 612.01 18.68 99.88 900,120 12,858 200:69240

去掉docker db后,再看下load,恢复正常了

PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 
45178 mysql 20 0 5046820 500292 37164 S 240.2 3.1 7:22.01 mysqld 
45625 root 20 0 1600500 23016 9852 S 122.3 0.1 2:25.70 server 
45779 root 20 0 1822432 22776 9268 S 81.4 0.1 1:04.24 main 
778 root 20 0 57832 9212 6724 S 11.0 0.1 0:31.49 redis-server

| redis 真是厉害啊,才0.1,最占资源的还是DB,所以DB需要分拆出来,有专业的DBA团队来维护是一个必然的事情 mysql 2.5 server 1.2 client 0.8 redis 0.1

磁盘io

Device tps MB_read/s MB_wrtn/s MB_dscd/s MB_read MB_wrtn MB_dscd
vda 352.00 0.00 4.38 0.00 0 4 0

慢sql: 现在基本都是订单插入的慢sql了

2022/08/07 12:45:51 /root/go/src/c10k-go/cmd/server/db.go:60 SLOW SQL >= 200ms [282.300ms] [rows:1] INSERT INTO `orders` (`created_at`,`updated_at`,`deleted_at`,`user_id`,`product_id`,`status`) VALUES ('2022-08-07 12:45:51.009','2022-08-07 12:45:51.009',NULL,1,1,'pending')

2022/08/07 12:45:51 /root/go/src/c10k-go/cmd/server/db.go:60 SLOW SQL >= 200ms [300.287ms] [rows:1] INSERT INTO `orders` (`created_at`,`updated_at`,`deleted_at`,`user_id`,`product_id`,`status`) VALUES ('2022-08-07 12:45:50.991','2022-08-07 12:45:50.991',NULL,1,1,'pending')

2022/08/07 12:45:51 /root/go/src/c10k-go/cmd/server/db.go:60 SLOW SQL >= 200ms [299.624ms] [rows:1] INSERT INTO `orders` (`created_at`,`updated_at`,`deleted_at`,`user_id`,`product_id`,`status`) VALUES ('2022-08-07 12:45:50.992','2022-08-07 12:45:50.992',NULL,1,1,'pending')

2022/08/07 12:45:51 /root/go/src/c10k-go/cmd/server/db.go:60 SLOW SQL >= 200ms [291.614ms] [rows:1] INSERT INTO `orders` (`created_at`,`updated_at`,`deleted_at`,`user_id`,`product_id`,`status`) VALUES ('2022-08-07 12:45:51','2022-08-07 12:45:51',NULL,1,1,'pending')

2022/08/07 12:45:51 /root/go/src/c10k-go/cmd/server/db.go:60 SLOW SQL >= 200ms [299.556ms] [rows:1] INSERT INTO `orders` (`created_at`,`updated_at`,`deleted_at`,`user_id`,`product_id`,`status`) VALUES ('2022-08-07 12:45:50.992','2022-08-07 12:45:50.992',NULL,1,1,'pending')

解决6-换DB,加配置

到此没办法解决了,只能提高配置了,看下mysql 最大能力了, 换postgres or 换其他的mysql

尝试了第三方解决方案,估计给的实例资源比较差,pg digitalOcean自带的,配置低的话,不行, digitalOcean 自带的mysql 900+一样,2c4G, 还不如自建的mysql

digitalOcean pg的TPS

耗时 并发数 成功数 失败数 qps 最长耗时 最短耗时 平均耗时 下载字节 字节每秒 状态码
1s 32 298 0 1044.82 934.47 3.91 95.71 3,874 3,856 200:298
2s 67 955 0 1013.23 1979.03 3.91 98.69 12,415 6,206 200:955
3s 99 1523 0 817.50 2939.58 3.91 122.32 19,799 6,597 200:1523
4s 100 1994 0 677.12 3060.34 3.91 147.69 25,922 6,480 200:1994
5s 100 2566 0 646.20 3060.34 3.91 154.75 33,358 6,671 200:2566
6s 100 3197 0 648.93 3060.34 3.91 154.10 41,561 6,924 200:3197
7s 100 3837 0 652.61 3060.34 3.91 153.23 49,881 7,125 200:3837
8s 100 4442 0 647.12 3060.34 3.91 154.53 57,746 7,213 200:4442
9s 100 5132 0 649.41 3060.34 3.91 153.98 66,716 7,412 200:5132
10s 100 5794 0 647.98 3060.34 3.91 154.33 75,322 7,530 200:5794
11s 100 6444 0 651.26 3060.34 3.91 153.55 83,772 7,614 200:6444
12s 100 7064 0 646.37 3060.34 3.91 154.71 91,832 7,651 200:7064
13s 100 7712 0 642.93 3060.34 3.83 155.54 100,256 7,711 200:7712
14s 100 8371 0 645.52 3060.34 3.83 154.91 108,823 7,772 200:8371
15s 100 9010 0 645.66 3060.34 3.83 154.88 117,130 7,808 200:9010
16s 100 9658 0 644.54 3060.34 3.83 155.15 125,554 7,846 200:9658

digitalOcean mysql server 5 的TPS

耗时 并发数 成功数 失败数 qps 最长耗时 最短耗时 平均耗时 下载字节 字节每秒 状态码
159s 100 134747 0 855.20 3156.15 5.27 116.93 1,751,711 11,016 200:134747
160s 100 135554 0 853.64 3156.15 5.27 117.14 1,762,202 11,013 200:135558
161s 100 136682 0 855.51 3156.15 5.27 116.89 1,776,866 11,036 200:136682
162s 100 137457 0 855.16 3156.15 5.27 116.94 1,786,941 11,030 200:137457
163s 100 138103 0 854.17 3156.15 5.27 117.07 1,795,339 11,014 200:138103
164s 100 138830 0 853.16 3156.15 5.27 117.21 1,804,790 11,003 200:138838
165s 100 139687 0 853.15 3156.15 5.27 117.21 1,815,931 11,005 200:139687
166s 100 140265 0 851.49 3156.15 5.27 117.44 1,823,445 10,984 200:140265
167s 100 140952 0 850.71 3156.15 5.27 117.55 1,832,376 10,972 200:140952
168s 100 141609 0 849.58 3156.15 5.27 117.71 1,840,917 10,957 200:141609
169s 100 142296 0 848.47 3156.15 5.27 117.86 1,849,848 10,945 200:142296

第三方DB的应用LOAD

PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 
73601 root 20 0 5142000 23352 11496 R 527.8 0.1 8:34.38 server 
73893 root 20 0 1896164 24076 9380 S 77.8 0.1 2:53.16 main

第三方DB的慢sql

2022/08/07 15:03:40 /root/go/src/c10k-go/cmd/server/db.go:60 SLOW SQL >= 200ms [253.865ms] [rows:1] INSERT INTO `orders` (`created_at`,`updated_at`,`deleted_at`,`user_id`,`product_id`,`status`) VALUES ('2022-08-07 15:03:40.232','2022-08-07 15:03:40.232',NULL,1,1,'pending')

2022/08/07 15:04:25 /root/go/src/c10k-go/cmd/server/db.go:60 SLOW SQL >= 200ms [248.147ms] [rows:1] INSERT INTO `orders` (`created_at`,`updated_at`,`deleted_at`,`user_id`,`product_id`,`status`) VALUES ('2022-08-07 15:04:24.973','2022-08-07 15:04:24.973',NULL,1,1,'pending')

2022/08/07 15:04:44 /root/go/src/c10k-go/cmd/server/db.go:60 SLOW SQL >= 200ms [258.707ms] [rows:1] INSERT INTO `orders` (`created_at`,`updated_at`,`deleted_at`,`user_id`,`product_id`,`status`) VALUES ('2022-08-07 15:04:43.935','2022-08-07 15:04:43.935',NULL,1,1,'pending')

pgsql 换成本地

load

PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 
127490 root 20 0 1897792 31676 12208 R 244.4 0.2 2:58.06 server 
127754 root 20 0 1601236 22120 9156 S 53.0 0.1 0:37.00 main 
73264 postgres 20 0 218692 30360 27604 R 32.8 0.2 1:09.48 postgres 
778 root 20 0 57832 9232 6724 S 6.3 0.1 1:35.19 redis-server 
73270 postgres 20 0 73424 7256 4368 R 2.6 0.0 0:05.10 postgres

TPS 1100+

耗时 并发数 成功数 失败数 qps 最长耗时 最短耗时 平均耗时 下载字节 字节每秒 状态码
97s 100 111084 0 1150.23 524.39 2.81 86.94 1,446,540 14,912 200:111084
98s 100 112243 0 1150.17 524.39 2.81 86.94 1,461,607 14,914 200:112243
99s 100 113367 0 1150.38 524.39 2.81 86.93 1,476,219 14,911 200:113369
100s 100 114549 0 1150.57 524.39 2.81 86.91 1,491,585 14,915 200:114549
101s 100 115704 0 1151.13 524.39 2.81 86.87 1,506,600 14,916 200:115704
102s 100 117044 0 1152.56 524.39 2.81 86.76 1,524,020 14,941 200:117044
103s 100 118235 0 1152.99 524.39 2.81 86.73 1,539,503 14,946 200:118235
104s 100 119582 0 1154.97 524.39 2.81 86.58 1,557,014 14,971 200:119582
105s 100 120818 0 1155.32 524.39 2.81 86.56 1,573,082 14,981 200:120818
106s 100 122110 0 1156.76 524.39 2.81 86.45 1,589,878 14,998 200:122110

日志 sql 报错,没有慢sql,但事务失败的很多,原因未知,日志没有打印出来DB的,没有继续查

create order failed: 3 
create order failed: 3 
create order failed: 3 
create order failed: 3 
create order failed: 3 
create order failed: 3 
create order failed: 3 
create order failed: 3 
create order failed: 3 
create order failed: 3

结论

1 读的地方加缓存,注意缓存失效

2 如果是秒杀场景,库存可以使用Redis扣减,基本不占资源

3 订单表的事务插入,尽量异步,不能异步的,加资源,使用Mysql或者Postgres 都可以

在8c16g的资源下,单机做到1100+的TPS,还可以吧,下次继续 :)