.net ServiceStack.Redis 性能调优
最近在debug生产环境的问题时,发现了ServiceStack 4.0.60版本RedisClient存在一个非常严重的性能问题。在高并发下,PooledRedisClientManager.GetClient和Redis.DisposeClient会导致High CPU,并且持续非常长的时间才能自动修复。下面是Demo程序压测还原问题后,工具的分析结果。
通过分析源代码发现:原来获取RedisClient的逻辑中通过锁方式实现,并且当连接被占满后再获取连接时,需要循环遍历数组中所有的连接对象判断是否有可用连接,会非常消耗CPU。Dispose方法也存在循环遍历的问题。尝试了很多种修改方案后,都不尽人意,果断把这两段逻辑重写,下面是相关代码,已经经过压测。
PooledRedisClientManager.cs: private ConcurrentQueue<RedisClient> deactiveClientQueue = new ConcurrentQueue<RedisClient>();
private static object lckObj = new object();
private static object waitObj = new object();
private int redisClientSize = 0;
private int maxRedisClient = 500; //PooledRedisClientManager的构造函数中初始化此值:maxRedisClient = this.Config.MaxWritePoolSize;
//GetReadOnlyClient方法也可按此方式修改
public IRedisClient GetClient()
{
RedisClient client = null;
var poolTimedOut = false;
DateTime startTime = DateTime.Now; while (true)
{
bool getResult = deactiveClientQueue.TryDequeue(out client); if (getResult == false)
{
if (redisClientSize >= maxRedisClient)
{
Thread.Sleep(3);
if (PoolTimeout.HasValue)
{
// wait for a connection, cry out if made to wait too long
if ((DateTime.Now - startTime).TotalMilliseconds >= PoolTimeout.Value)
{
poolTimedOut = true;
break;
}
} }
else
{
client = CreateRedisClient();
if (client != null)
return client;
}
}
else
{
if (client != null)
{
InitClient(client);
return client;
}
else
{
client = CreateRedisClient();
if (client != null)
return client;
}
}
} if (poolTimedOut == true)
{
throw new TimeoutException(PoolTimeoutError);
} return client;
} private RedisClient CreateRedisClient()
{
if (redisClientSize >= maxRedisClient)
return null;
lock (lckObj)
{
if (redisClientSize >= maxRedisClient)
return null;
Random dom = new Random((int)DateTime.Now.Ticks);
var newClient = InitNewClient(RedisResolver.CreateMasterClient(dom.Next(100)));
newClient.OnDispose += (isRecycle) =>
{
if (isRecycle == true)
{
try
{
deactiveClientQueue.Enqueue(newClient);
}
catch
{
lock (lckObj)
{
redisClientSize--;
}
}
}
else
{
lock (lckObj)
{
redisClientSize--;
}
}
};
redisClientSize++; return newClient;
}
}
RedisClient.cs:
public event RedisClientDisposeEventHandler OnDispose;
public override void Dispose()
{
if (OnDispose != null)
OnDispose(this.HadExceptions == false);
base.Dispose();
}
RedisClient.cs:
public delegate void RedisClientDisposeEventHandler(bool isRecycle);
下面是修改前后的结果对比:
1.100个线程,每个线程完成2000次Redis调用,每次调用GetClient。 改造前12s,改造后8.5s,提升近50%。老版本CPU消耗稍高,并具有持续性。
2.200个线程,每个线程完成2000次Redis调用,每次调用GetClient。 改造前378s,改造后19s,提升提升近20倍。老版本CPU消耗非常高(解决100%),并具有持续性。新版本CPU占用了仅有原来的一半。
3.300个线程,每个线程完成2000次Redis调用,每次调用GetClient。 改造前1580s(26分钟),改造后29s,提升提升近55倍。老版本CPU消耗非常高(解决100%),并具有持续性。新版本CPU占用了仅有原来的一半。
通过上述三个场景的测试可以看出,当RedisClient访问压力持续增加时,原版本的响应时间呈现指数性增长,当达到一定压力时,RedisClient访问几乎阻塞,需要非常长时间才能缓解。重构后的RedisClient在性能上有大幅度提升,特别是在高并发下的性能表现,直接秒杀原版本!
.net ServiceStack.Redis 性能调优的更多相关文章
- redis性能调优笔记(can not get Resource from jedis pool和jedis connect time out)
对这段时间redis性能调优做一个记录. 1.单进程单线程 redis是单进程单线程实现的,如果你没有特殊的配置,redis内部默认是FIFO排队,即你对redis的访问都是要在redis进行排队,先 ...
- Redis性能调优
Redis性能调优 尽管Redis是一个非常快速的内存数据存储媒介,也并不代表Redis不会产生性能问题.前文中提到过,Redis采用单线程模型,所有的命令都是由一个线程串行执行的,所以当某个命令执行 ...
- Redis性能调优建议
一. Redis部署结构优化建议 1. Master不做AOF或RDB持久化,Slave做AOF持久化,建议同时做RDB持久化 2. 所有Master全部增加Slave 3. Master挂载Slav ...
- Redis性能调优:保存SNAPSHOT对性能的影响
前一段时间.开发环境反馈,Redisserver訪问很慢,每一个请求要数秒时间,重新启动之后2~3天又会这样. 我查看了一下Linux的性能,没有什么问题. 通过 # redis-cli --late ...
- StackExchange.Redis性能调优
大家经常出现同步调用Redis超时的问题,但改成异步之后发现错误非常少了,但却可能通过前后记日志之类的发现Redis命令非常慢. PS: 以后代码都在Windows bash中运行,StackExch ...
- Redis基础、高级特性与性能调优
本文将从Redis的基本特性入手,通过讲述Redis的数据结构和主要命令对Redis的基本能力进行直观介绍.之后概览Redis提供的高级能力,并在部署.维护.性能调优等多个方面进行更深入的介绍和指导. ...
- Redis 基础、高级特性与性能调优
本文将从Redis的基本特性入手,通过讲述Redis的数据结构和主要命令对Redis的基本能力进行直观介绍.之后概览Redis提供的高级能力,并在部署.维护.性能调优等多个方面进行更深入的介绍和指导. ...
- Redis 宝典 | 基础、高级特性与性能调优
转载:Redis 宝典 | 基础.高级特性与性能调优 本文由 DevOpsDays 本文由简书作者kelgon供稿,高效运维社区致力于陪伴您的职业生涯,与您一起愉快的成长. 作者:kelgon ...
- Redis基础与性能调优
Redis是一个开源的,基于内存的结构化数据存储媒介,可以作为数据库.缓存服务或消息服务使用. Redis支持多种数据结构,包括字符串.哈希表.链表.集合.有序集合.位图.Hyperloglogs等. ...
随机推荐
- 深入理解和应用Float属性
一.Float的特性 1. 应用于文字围绕图片 2. 创建一个块级框 3. 多列浮动布局 4. 浮动元素的宽度.高度自适应,但可以设置其值. 二.核心解决的问题 文字围绕图片:img标签与多个文本标签 ...
- c++对象池使用
// // ObjectPool.h // DragonBall // // Created by user on 13-8-22. // // #include <iostream> # ...
- css(一)
CSS CSS是Cascading Style Sheets的简称,中文称为层叠样式表,用来控制网页数据的表现,可以使网页的表现与数据内容分离. 一 css的四种引入方式 1.行内式 ...
- Gradle's dependency cache may be corrupt解决方法
问题描述: Error:Unable to find method 'com.google.common.cache.CacheBuilder.build(Lcom/google/common/cac ...
- Xcode8开发iOS10推送通知过程
iOS10发布后,简书优先开发增加了iOS10的新通知.本文分享整个feature的开发过程遇到的问题. 1.工程配置 Xcode8发生了很大的变化,直接打开原来的工程编译运行,这个时候是获取不到Pu ...
- OC 类别与扩展(匿名类别)
OC 类别与扩展(匿名类别) 类别(Categroy): 又称为扩展类,在类的原基础上扩展方法,且不可添加变量,如果扩展的方法与原始类中的方法相同,则会隐藏原始方法,且不可在扩展方法中通过super调 ...
- UITableViewController和XML解析还有地图的简单结合
然后我的代码就按照上面的这个顺序输出. #import <Foundation/Foundation.h> #import <MapKit/MapKit.h> @interfa ...
- java入门知识点结构
第一部分 计算机程序和面向对象编程 编程语言种类: 机器语言:2进制(0和1) 汇编语言:英文字符缩写和助记符 高级语言: 面向过程:面向过程是从微观上/细节上处理具体事务. C语言 面向对象: ...
- MySQL学习笔记之视图
视图是对磁盘上保存的表数据的抽象,即抽取一个表或多个表的部分行或列的数据,展示给使用者. 首先列举下MySQL中最简单的对视图操作的语法: 1.创建视图: create view 视图名 as sel ...
- [AlwaysOn Availability Groups]AlwaysOn等待类型
AlwaysOn等待类型 当排查AlwaysOn延迟,等待统计信息可以在DMV中查看累计的AlwaysOn等待类型. 查看AlwaysOn等待类型 SELECT * FROM sys.dm_os_wa ...