译者注

该原文是Ayende Rahien大佬业余自己在使用C# 和 .NET构建一个简单、高性能兼容Redis协议的数据库的经历。

首先这个"Redis"是非常简单的实现,但是他在优化这个简单"Redis"路程很有趣,也能给我们在从事性能优化工作时带来一些启示。

原作者:Ayende Rahien

原链接:https://ayende.com/blog/197412-B/high-performance-net-building-a-redis-clone-naively

正文

我遇到了这个项目,它的目标是成为一个比Redis有着更好性能和更易用的克隆版。我发现它很有趣,因为它主要的卖点之一就是它是在多线程模式下运行(而不是像Redis那样是单线程)。他们使用memtier_benchmark(Redis项目的一部分)来测试性能。所以我很好奇,如果我使用C#来构建自己的Redis克隆版,会有怎么样的性能?

我构建的第一个版本非常简单。我的想法是使用高抽象的API来编写它,看看它的性能到底怎么样。为了使事情变得有趣,下面是它的测试方案:

  • 客户端:memtier_benchmark将在aws的c6g.2xlarge实例上运行,使用8核32G内存
  • 服务端:测试的实例将在aws的c6g.4xlarge上运行,使用16核64G内存

    客户端要运行的命令如下所示:
memtier_benchmark –s $SERVER_IP -t 8 -c 16 --test-time=30 --distinct-client-seed -d 256 --pipeline=30

上面的命令说明我们将使用8个线程(客户端实例上的CPU核心数),每个线程创建32个链接,20%的场景写入,80的场景读取,数据大小为256字节,将不断的把更多的数据推送到测试的实例中。

服务端使用以下命令运行:

dotnet run –c Release

以下是此测试在服务器的实例:



我选择30秒作为测试的持续时间,以收集更多的信息让我们感受正在发生的事情(比如GC周期),同时保持测试的持续时间足够短,这样我不会感觉到无聊。

以下是简单版本的测试结果:



因此,使用C#构建的简单版本,即使什么优化都不做,也有几乎100w/s的性能。从另外的角度来说,延时并不是那么的好。P99延时将近100ms。

现在我用数字和漂亮的图表引起了你的注意,让我向你展示我正在运行的实际代码。这是一个不到100行代码的“Redis克隆”。

using System.Collections.Concurrent;
using System.Net.Sockets; var listener = new TcpListener(System.Net.IPAddress.Any, 6379);
listener.Start();
var redisClone = new RedisClone(); while (true)
{
var client = listener.AcceptTcpClient();
var _ = redisClone.HandleConnection(client); // run async
} public class RedisClone
{
ConcurrentDictionary<string, string> _state = new(); public async Task HandleConnection(TcpClient client)
{
using var _ = client;
using var stream = client.GetStream();
using var reader = new StreamReader(stream);
using var writer = new StreamWriter(stream)
{
NewLine = "\r\n"
}; try
{
var args = new List<string>();
while (true)
{
args.Clear();
var line = await reader.ReadLineAsync();
if (line == null) break; if (line[0] != '*')
throw new InvalidDataException("Cannot understand arg batch: " + line); var argsv = int.Parse(line.Substring(1));
for (int i = 0; i < argsv; i++)
{
line = await reader.ReadLineAsync();
if (line == null || line[0] != '$')
throw new InvalidDataException("Cannot understand arg length: " + line);
var argLen = int.Parse(line.Substring(1));
line = await reader.ReadLineAsync();
if (line == null || line.Length != argLen)
throw new InvalidDataException("Wrong arg length expected " + argLen + " got: " + line); args.Add(line);
}
var reply = ExecuteCommand(args);
if(reply == null)
{
await writer.WriteLineAsync("$-1");
}
else
{
await writer.WriteLineAsync($"${reply.Length}\r\n{reply}");
}
await writer.FlushAsync();
}
}
catch (Exception e)
{
try
{
string? line;
var errReader = new StringReader(e.ToString());
while ((line = errReader.ReadLine()) != null)
{
await writer.WriteAsync("-");
await writer.WriteLineAsync(line);
}
await writer.FlushAsync();
}
catch (Exception)
{
// nothing we can do
}
}
} string? ExecuteCommand(List<string> args)
{
switch (args[0])
{
case "GET":
return _state.GetValueOrDefault(args[1]);
case "SET":
_state[args[1]] = args[2];
return null;
default:
throw new ArgumentOutOfRangeException("Unknown command: " + args[0]);
}
}
}

只是关于实现的几个注意事项。我实际上并没有做太多事情。大部分代码用于解析 Redis 协议。代码充满了内存分配。每个命令解析都是使用多个字符串拆分和连接来完成的。对客户端的回复需要更多的连接。系统的“存储”实际上只是一个简单的 ConcurrentDictionary,没有任何避免锁竞争或高成本的东西。

我们处理I/O的方式非常糟糕,而且......我想你明白我的想法,对吧?我的目标是看看如何使用这个(非常简单的)示例来获得更高的性能,而不必处理很多额外的细节。

鉴于我最初的尝试已经接近100万QPS,这是一个非常好的开始,即使我自己这么说。

我想采取的下一步是处理这里多余的内存分配。我们也许可以在内存分配这方面做得更好,虽然我的目标只是尝试。但我将在下一篇文章中这样做。

使用.NET简单实现一个Redis的高性能克隆版(一)的更多相关文章

  1. 使用.NET简单实现一个Redis的高性能克隆版(二)

    译者注 该原文是Ayende Rahien大佬业余自己在使用C# 和 .NET构建一个简单.高性能兼容Redis协议的数据库的经历. 首先这个"Redis"是非常简单的实现,但是他 ...

  2. 使用.NET简单实现一个Redis的高性能克隆版(三)

    译者注 该原文是Ayende Rahien大佬业余自己在使用C# 和 .NET构建一个简单.高性能兼容Redis协议的数据库的经历. 首先这个"Redis"是非常简单的实现,但是他 ...

  3. 使用.NET简单实现一个Redis的高性能克隆版(四、五)

    译者注 该原文是Ayende Rahien大佬业余自己在使用C# 和 .NET构建一个简单.高性能兼容Redis协议的数据库的经历. 首先这个"Redis"是非常简单的实现,但是他 ...

  4. 使用.NET简单实现一个Redis的高性能克隆版(六)

    译者注 该原文是Ayende Rahien大佬业余自己在使用C# 和 .NET构建一个简单.高性能兼容Redis协议的数据库的经历. 首先这个"Redis"是非常简单的实现,但是他 ...

  5. 使用.NET简单实现一个Redis的高性能克隆版(七-完结)

    译者注 该原文是Ayende Rahien大佬业余自己在使用C# 和 .NET构建一个简单.高性能兼容Redis协议的数据库的经历. 首先这个"Redis"是非常简单的实现,但是他 ...

  6. 发布一个参考ssdb,用go实现的类似redis的高性能nosql:ledisdb

    起因 ledisdb是一个参考ssdb,采用go实现,底层基于leveldb,类似redis的高性能nosql数据库,提供了kv,list,hash以及zset数据结构的支持. 我们现在的应用极大的依 ...

  7. Nginx+Lua+MySQL/Redis实现高性能动态网页展现

    Nginx结合Lua脚本,直接绕过Tomcat应用服务器,连接MySQL/Redis直接获取数据,再结合Lua中Template组件,直接写入动态数据,渲染成页面,响应前端,一次请求响应过程结束.最终 ...

  8. java架构之路-(Redis专题)Redis的高性能和持久化

    上次我们简单的说了一下我们的redis的安装和使用,这次我们来说说redis为什么那么快和持久化数据 在我们现有的redis中(5.0.*之前的版本),Redis都是单线程的,那么单线程的Redis为 ...

  9. [开源] gnet: 一个轻量级且高性能的 Golang 网络库

    Github 主页 https://github.com/panjf2000/gnet 欢迎大家围观~~,目前还在持续更新,感兴趣的话可以 star 一下暗中观察哦. 简介 gnet 是一个基于 Ev ...

随机推荐

  1. 分享一个 SpringCloud Feign 中所埋藏的坑

    背景 前段时间同事碰到一个问题,需要在 SpringCloud 的 Feign 调用中使用自定义的 URL:通常情况下是没有这个需求的:毕竟都用了 SpringCloud 的了,那服务之间的调用都是走 ...

  2. django三板斧与request对象方法与ORM

    目录 django三板斧 HttpResponse() render() redirect() 网页获取静态文件样式 request对象方法 post请求问题 针对get请求和post请求执行不同代码 ...

  3. 『忘了再学』Shell基础 — 17、预定义变量

    目录 1.预定义变量$? 2.预定义变量$$和$! 我们之前说过,Shell中的变量不是按照变量值的类型来进行分类的,而是按照Linux系统中定义的变量类别来分类的. 预定义变量就是,事先把变量的名称 ...

  4. MongoDB 体系结构与数据模型

    每日一句 If no one else guards the world, then I will come forward. 如果没有别人保卫这个世界,那么我将挺身而出. 概述 MongoDB主要是 ...

  5. CF1588F Jumping Through the Array

    在讲正解之前,先播一个小故事: xay 复杂度错误过题.将操作按照时间分块,块内他令所有置换环都必须有至少一个"黑点". 可以通过没有修改 \(p\) 操作,同时 \(p_i=i\ ...

  6. java和.net 双语言开发框架,开源的PaaS平台

    当下,我国国内的PaaS平台正在蓬勃发展,各式各样的PaaS平台层出不穷,但万变不离其宗,一个优秀的PaaS平台总有自己独树一帜或与众不同的地方.那么,首先我们要了解下什么是PaaS平台?PaaS是( ...

  7. 我的 Java 学习&面试网站又又又升级了!

    晚上好,我是 Guide. 距离上次介绍 JavaGuide 新版在线阅读网站已经过去 7 个多月了(相关阅读:官宣!我升级了!!!),这 7 个多月里不论是 JavaGuide 的内容,还是 Jav ...

  8. redis入门,linux安装

    1.下载 https://redis.io/download 2.上传到linux服务器tools文件夹下 3.解压到安装目录 tar -zxf /app/redis/redis-5.0.4.tar. ...

  9. C语言学习之我见-strncmp()字符串比较函数(控制范围)

    strncmp()函数,用于范围内,两个字符串的比较,n表示最大比较范围. (1)函数原型 int strncmp(const char *_Str1,const char *_Str2,size_t ...

  10. 在公网服务器搭建CobaltStrike4.0

    因为工作需要使用cs,正好之前腾讯云薅了一把羊毛,就把VPS装起来cs. 选的环境是centos7.6 cs运行需要java环境 先使用yum -y list java* 查看yum存在的java库 ...