.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等. ...
随机推荐
- [Tool] SourceTree操作中遇到错误(Filename too long)的解决方案
[Tool] SourceTree操作中遇到错误(Filename too long)的解决方案 问题情景 使用SourceTree,可以方便开发人员使用图形化接口的Git指令来管理原始码.但是在Wi ...
- java入门笔记(1)
上图表达的是我们写的java程序是怎么在电脑上运行并算出结果的.编译器判断语法是否正确,如果错误,不能生成.class文件. JVM(Java Virtual Machine)是java虚拟机. JV ...
- jQuery网站顶部定时折叠广告
效果体验:http://hovertree.com/texiao/jquery/4.htm HTML文件代码: <!DOCTYPE html> <html xmlns="h ...
- 【单页应用巨坑之History】细数History带给单页应用的噩梦
前言 在我们日常的网页浏览中,我们非常喜欢做一个操作:点击浏览器的前进后退在Ajax技术出现后,有些时候前进后退就会给开发者带来困扰,甚至一些开发者试图去干掉History随着Html5的发展,移动端 ...
- 纯前端JSON文件编辑器[0]
准备工作 参考资料: FileReader(用来获取上传文件的数据) <download>(用来设置下载文件的名称) Blob(用来存储数据的一个容器) createObjectURL(用 ...
- iOS 点击TextField不弹出软键盘的解决方案
开发中遇到: 在模拟器里面,textfield可以通过电脑键盘输入,可是怎么也不会自动弹出模拟器软键盘 解决方案: 切换一下键盘,command+shift+k,Xcode6.3 中只能是一种输入源
- SharePoint 快捷获取列表栏内部名称
在列表设置页面点击浏览器书签获取列表字段内部名称,使用效果如下图: 如何使用: 修改浏览器上任意书签的url地址为以下代码,注意:代码中不能有换行符 javascript:(function(){va ...
- Linux vi/vim(转载)
Linux vi/vim 所有的 Unix Like 系统都会内建 vi 文书编辑器,其他的文书编辑器则不一定会存在. 但是目前我们使用比较多的是 vim 编辑器. vim 具有程序编辑的能力,可以主 ...
- SharePoint 2013 使用JavaScript对象模型配置智能提示
前言 默认在VS2012/2013中编写SharePoint JavaScript 客户端对象模型,都没有智能感知的功能,用起来非常麻烦:其实,我们可以手动配置一下,让JavaScript可以进行智能 ...
- Android中的AlertDialog使用示例二(普通选项对话框)
在Android开发中,我们经常会需要在Android界面上弹出一些对话框,比如询问用户或者让用户选择.这些功能我们叫它Android Dialog对话框,AlertDialog实现方法为建造者模式. ...