go解决c10k problem(二)–db版下单场景
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,还可以吧,下次继续 :)