Redis 几乎每小时都出现大量超时,求助

70 天前
 drymonfidelia

报错信息是 Timeout awaiting response (outbound=1KiB, inbound=0KiB, 5728ms elapsed, timeout is 5000ms), command=SET, next: EVAL, inst: 0, qu: 0, qs: 15, aw: False, bw: SpinningDown, rs: ReadAsync, ws: Idle, in: 76, in-pipe: 0, out-pipe: 0, serverEndpoint: 127.0.0.1:6379, mc: 1/1/0, mgr: 10 of 10 available, clientName: AppProductionEnvServer1(SE.Redis- v2.5.43.42402), IOCP: (Busy=0,Free=10000,Min=9000,Max=10000), WORKER: (Busy=236,Free=32531,Min=10000,Max=32767), POOL: (Threads=236,Queueditems=50,Completeditems=8751117254), v: 2.5.43.42402 (Please take a look at this article for some common client-side issues that can cause timeouts: https://stackexchange.github.io/StackExchange.Redis/Timeouts)

出错的时候看了下也就三百多并发,比较怀疑是下面这段代码引起的:

   while (!await redis.GetDatabase().LockTakeAsync($"PlaceOrder:{user.UserId}", "1", TimeSpan.FromSeconds(180)))
        {
            await Task.Delay(1);
        }

作用是确保同一用户只有一个订单未写入数据库(系统下单逻辑涉及几十个函数,全是一些莫名奇妙的判断逻辑,混淆后可读性大幅提升的那种(当然是开玩笑的)),屎山作者已经跑路了,没人能看懂他代码,一个用户下多个订单数据会混乱。更牛逼的这套系统除了性能极差,运行 3 年没出错一次。一次调用 API 只能下一单,客户端随硬件交付,已经写死了,不能更新,然后客户端一次多少个订单就多少并发调用 API 提交,没有队列功能。目前要求 500 订单 10 秒内全部下单完成返回订单号(单独提交的话每个订单 0.01 秒左右能写入完数据拿到订单号)。

预分配订单号行不通,不运行一遍这部分屎山代码不能确定这个订单能不能提交,返回订单号就代表这个订单提交成功了,不能取消。目前打算改造成 Sub/Pub ,不知道能不能提升性能,或者 V 友有没有更好的改造方案?只要能让这屎山跑起来就行,代码多脏都没关系,改动需要尽可能小,不能把系统改炸。目前加硬件到 256GB 内存都没解决。

5863 次点击
所在节点    Redis
67 条回复
antli
70 天前
antli
70 天前
考虑到此信息,我们强烈建议客户将 IOCP 和辅助角色线程的最小配置值设置为大于默认值。 我们无法提供有关此值应是多少的通用指导,因为一个应用程序的合适值对于另一个应用程序可能会太高或太低。 此设置还可能会影响复杂应用程序其他部分的性能,因此每个客户需要按照其特定需求来微调此设置。 开始时设置为 200 或 300 会比较好,随后可进行测试并根据需要进行调整。
如何配置此设置:
建议使用 global.asax.cs 中的 ThreadPool.SetMinThreads (...) 方法,以编程方式更改此设置。 例如:
C#复制
private readonly int minThreads = 200;
void Application_Start(object sender, EventArgs e)
{
// Code that runs on application startup
AreaRegistration.RegisterAllAreas();
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
ThreadPool.SetMinThreads(minThreads, minThreads);
}
备注
此 方法指定的值是全局设置,将影响整个 AppDomain 。 例如,如果已有 4 核计算机,并想要在运行时将 minWorkerThreads 和 minIoThreads 设置为 50 (每个 CPU ),请使用 ThreadPool.SetMinThreads(200, 200)。
还可以使用 Machine.config 中 <processModel> 配置元素下的 minIoThreads 或 minWorkerThreads 配置设置来指定最小线程设置。Machine.config 通常位于 %SystemRoot%\Microsoft.NET\Framework\[versionNumber]\CONFIG\。 不建议以这种方式设置最小线程数,因为这是系统范围设置。
备注
此配置元素中指定的值是按核心设置。 例如,如果使用 4 核计算机,并且希望 minIoThreads 设置在运行时为 200 ,则使用 <processModel minIoThreads="50"/>。
wccc
70 天前
还是修改锁的实现, 可重入
rnv
70 天前
是不是惊群了,每次锁空闲会唤起一大批在等待的,但只有一个拿到了锁
zhuisui
70 天前
setex 作为一个原子操作,兼顾读写,消耗较大。
300 个线程 1ms 一次,那就是 30w qps ,超时也正常。
基于这个思路改善肯定没问题。
abccccabc
70 天前
```作用是确保同一用户只有一个订单未写入数据库```

这句话怎么怪怪的,用户的订单不是都应该写入数据库吗?
EmbraceQWQ
70 天前
如果确定是卡死了 redis ,业务要求就是如此的话,感觉要么增大等待时间,锁的粒度是不是也可以变小一点例如上面提到了 hash ,不知道上集群会不会有改善,要么就上队列来搞
drymonfidelia
70 天前
@abccccabc 就是确保同一用户只有一个订单正在写入数据库的意思
@8355 我看到他的代码后一个订单有引用前一个订单的数据,同时下多个订单确实会出问题。逻辑实在太混乱了,有几处我都看不懂,实在不敢改
timy007
70 天前
有使用 StackExchange.Redis.Extensions 这个包吗? 有个话把 poolSize 改成 1 试试。
https://www.cnblogs.com/cmt/p/16405164.html
keakon
70 天前
看上去是并发拿锁的太多了,都在轮询。你考虑下常规的锁实现:先获取自旋锁,不成功就进入内核等待。

比如先 LockTakeAsync ,不成功就 brpop 一个 key ,拿到这个 key 或超时再尝试下次 LockTakeAsync 。
完成订单的线程除了释放 LockTakeAsync 的锁,还需要 rpush 这个 key ,用来唤醒一个客户端。
chenqh
70 天前
你要不 sleep 个随机数,sleep 肯定有问题的。
popvlovs
70 天前
还有个不是办法的办法,如果你们能在 load-balancer 里自定义一个按 user-id hash 的策略,那可以考虑把分布式锁干掉
xinzhanghello
70 天前
看下 jstack ,看下主、副、pub/sub 线程卡在哪里? refer: https://mp.weixin.qq.com/s/t040fhPDPzQ3EeZo1_yp8A
sighforever
70 天前
@drymonfidelia delay 50 你觉得长,可以先试试 10 ,5, 3 哪怕是 2 都减少了一半的并发啊
abccccabc
70 天前
@drymonfidelia

redis 是什么版本?据说最新的 redis 版本可以利用多 cpu 特性。
drymonfidelia
70 天前
@abccccabc 6.0.16 ,不敢更新
EscYezi
70 天前
是不是连接池耗尽了,循环等待过程中一直持有连接没归还?
drymonfidelia
70 天前
@antli 以前就考虑过 minThreads 的问题,把 minThreads 调大了,没解决,后面调到非常大,像上面的错误消息里的那样,还是不行
drymonfidelia
70 天前
@EscYezi 看错误消息,连接池应该还是有很多空闲的
testcgd
69 天前
感觉是锁的实现问题,你把 delay 改下,等待一次时间翻倍,最多等 50ms

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/1034586

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX