redisb并发访问慢出现的问题
最近项目一上线,就问题颇多,本地测试,ok,上线后,大用户量的时候,顶不住。用了一个礼拜的时间发现的问题,总结下来。
项目是netty4.0,reids2.8,nginx等框架。目前是4台proxy服务器,一台核心服务器,reids只部署在核心服务器上,各代理服务器共享redis数据。
当大量用户访问自己距离较近的proxy服务器时,proxy同时请求核心服务器,并发量到1w时,经常请求卡死,网页请求不回来,开始从netty的http处理并发下手,各种netty的官网,netty的优化,和配置,都修改了,还是没有起作用,后来屏蔽redis后,发现netty处理20w并发一点问题没有,问题确定在redis上。
然后,着手redis的优化,redis的池的优化,配置,没有作用,后台发现,本地访问redis并发1w,很快,但是,访问其他服务器的redis特别卡,发现原因,就在于,跨服务器访问redis,可能由于网络,跨服务器,导致的并发请求redis,回不来的问题,那么,最后,舍弃了proxy服务器远程调用核心服务器reids的方案,nginx改为所有心跳请求,跨过proxy服务器,直接走核心服务器,这样相当于本地访问redis,最后担心核心服务器并发能力,暂时,开启了2个服务,处理所有并发,reids问题得到解决。
总结:就是reids本身性能没有问题,处理并发能力ok,就是跨服务器远程访问其他服务器reids时,并发大了,网络延迟等,会出现取reids卡死。
Redis是一个非常高效的基于内存的NOSQL数据库,它提供非常高效的数据读写效能.在实际应用中往往是带宽和CLIENT库读写损耗过高导致无法更好地发挥出Redis更出色的能力.下面结合一些redis本身的特性和一些client操作上的改变来提高整个redis操作的效能.
上图是反映平常操作redis的情况,每个线程都独立的发起相应连接对redis的网络读写.虽然我们可以通过批操作的方式来把当前多个操作合并成一个,但这种方式只能针对当单线程,而多线程相互合并则设计上很少关注.从redis的协议来说其实并没有限制,只是在client库的设计一般没有考虑进去.
如果在多线程操作REDIS的同时如果能够合并网络操作,那意味着可以减少操作网络读写的情况把处理能力提升到最大化.这样Client总体的性能都会有所提升,而REDIS也因表层的网络读取减少而达到更好的利用率.
以上是设计图,原理并不复杂,其实就是把每个请求的操作放到一个队列中,后面开启一个线程来把前面的指令进行一个合并操操作.一个线程在高并发下可以无法更快速地合并起来,可以根据需要进行合理的操作线程应用.
这种设计的效果是否真的比较理想呢,以下是一个简单的测试
public void AnycSet() { CodeTimer.Time("beetle.redis asynset", () => { Parallel.For(0, Count, x => { ProtobufKey key = x.ToString(); key.AsynSet(new User() { UserId = x, NickName = "sdffffffffffffffffffffffffffffffffffffffffsdffffffffffffffffffffffffffffffffffffffffbeetlesdffffffffffffffffffffffffffffffffffffffffsdffffffffffffffffffffffffffffffffffffffff" + x }); }); }); } public void Set() { CodeTimer.Time("beetle.redis set", () => { Parallel.For(0, Count, x => { ProtobufKey key = x.ToString(); key.Set(new User() { UserId = x, NickName = "sdffffffffffffffffffffffffffffffffffffffffsdffffffffffffffffffffffffffffffffffffffffbeetlesdffffffffffffffffffffffffffffffffffffffffsdffffffffffffffffffffffffffffffffffffffff" + x }); }); }); }
测试结果如下
以上是10W次的操作测试结果,由于redis在本机所以交互非常可观.
虽然在多线程高并发下这样的设计可以把吞吐能力和效能有一个非常不错的效果,但其也存在缺陷因为每次操作都经过不同线程的调处理,如果并发线程不多操作密集度不高.那效果并不理想;因为网络操作密集度不高,可得到并合的数量不多,这方面的损耗有可能低于操作跨线程调度所带来的损耗.