C# 与StackExchange.Redis并行执行?

C# 与StackExchange.Redis并行执行?,c#,performance,redis,stackexchange.redis,C#,Performance,Redis,Stackexchange.redis,我在列表中有一个1M项目存储,我正在序列化它以便插入到Redis中。(2.8) 我将工作划分为10任务,每个任务都有自己的部分(List是线程安全的只读() 简化: 例如: 对于ITEMS=100,THREADS=10,每个任务将捕获自己的页面并处理相关范围 例如: void Main() { var ITEMS=100; var THREADS=10; var PAGE=4; List<int> lst = Enumerable.Range(0,I

我在
列表中有一个1M项目存储,我正在序列化它以便插入到Redis中。(2.8)

我将工作划分为
10
任务
,每个任务都有自己的部分(
List
是线程安全的只读()

简化:

例如:

对于
ITEMS=100
THREADS=10
,每个
任务将捕获自己的页面并处理相关范围

例如:

void Main()
{
    var ITEMS=100;
    var THREADS=10;
    var PAGE=4;

    List<int> lst = Enumerable.Range(0,ITEMS).ToList();

    for (int i=0;i< ITEMS/THREADS ;i++)
    {
      lst[PAGE*(ITEMS/THREADS)+i].Dump();
    }
}
void Main()
{
var项目=100;
var=10;
var-PAGE=4;
List lst=Enumerable.Range(0,ITEMS.ToList();
对于(int i=0;i
  • PAGE=0
    将处理:
    0,1,2,3,4,5,6,7,8,9
  • PAGE=4
    将处理:
    40,41,42,43,44,45,46,47,48,49
好的

现在回到东南redis

我想实现这个模式,所以:(使用
ITEMS=1000000

我的测试:

(这里是
dbsize
每秒检查一次):

如您所见,通过10个线程添加了1M条记录

现在,我不知道它是否快,但当我将项目从
1M
更改为
10M
——事情变得非常慢,我得到了异常:

异常出现在
for
循环中

未处理的异常:System.AggregateException:一个或多个错误 发生了---

System.TimeoutException:超时执行设置urn:user>288257,inst:1,queu e:11,qu=0,qs=11,qc=0,wr=0/0,in=0/0 at StackExchange.Redis.ConnectionMultiplexer.ExecuteSyncImpl[T](消息 消息,ResultProcessor
1处理器,ServerEndPoint服务器)位于
c:\TeamCity\buildAgen
t\work\58bc9a6df18a3782\StackExchange.Redis\StackExchange\Redis\ConnectionMultip
lexer.cs:第1722行
StackExchange.Redis.RedisBase.ExecuteSync[T](消息消息,
结果进程sor
1处理器,服务器端点服务器) c:\TeamCity\buildAgent\work\58bc9a6df 18a3782\StackExchange.Redis\StackExchange\Redis\RedisBase.cs:第79行 …按任意键继续

问题:

  • 我分配工作的方式是否正确(最快)
  • 如何更快地完成任务(请提供示例代码)
  • 如何解决此异常
相关信息:


出现在App.config中(否则我将退出MemoryException),另外-build for x64bit,我有16GB,ssd驱动器,i7CPU)。

当前,您的代码正在使用同步API(
StringSet
),并由10个线程同时加载。这对SE.Redis来说并没有明显的挑战-它在这里工作得很好。我怀疑这确实是一个超时,服务器处理某些数据的时间比您希望的要长,很可能也与服务器的分配器有关。那么,一个选择就是简单地增加一点超时时间。不是很多。。。尝试5秒,而不是默认的1秒。很可能,无论如何,大多数操作都运行得非常快

关于加速:这里的一个选择是不要等待,即保持数据的管道化。如果您满足于不检查每条消息的错误状态,那么一种简单的方法是在
StringSet
调用的末尾添加
,标志:CommandFlags.FireAndForget
。在我的本地测试中,这将1M示例的速度提高了25%(我怀疑其余的很多时间实际上都花在了字符串序列化上)

我在10M示例中遇到的最大问题只是处理10M示例的开销——特别是因为这会为
redis服务器
和应用程序占用大量内存,而应用程序(为了模拟您的设置)位于同一台机器上。这会产生竞争性内存压力,托管代码中会出现GC暂停等。但也许更重要的是:开始做任何事情都需要很长时间。因此,我对代码进行了重构,使用并行
yield-return
生成器,而不是单个列表。例如:

    static IEnumerable<Person> InventPeople(int seed, int count)
    {
        for(int i = 0; i < count; i++)
        {
            int f = 1 + seed + i;
            var item = new Person
            {
                Id = f,
                Name = Path.GetRandomFileName().Replace(".", "").Substring(0, appRandom.Value.Next(3, 6)) + " " + Path.GetRandomFileName().Replace(".", "").Substring(0, new Random(Guid.NewGuid().GetHashCode()).Next(3, 6)),
                Age = f % 90,
                Friends = ParallelEnumerable.Range(0, 100).Select(n => appRandom.Value.Next(1, f)).ToArray()
            };
            yield return item;
        }
    }

    static IEnumerable<T> Batchify<T>(this IEnumerable<T> source, int count)
    {
        var list = new List<T>(count);
        foreach(var item in source)
        {
            list.Add(item);
            if(list.Count == count)
            {
                foreach (var x in list) yield return x;
                list.Clear();
            }
        }
        foreach (var item in list) yield return item;
    }
在这里,
Batchify
的目的是确保我们不会在每次操作之间花费相当长的时间来帮助服务器——数据是以1000个数据为一批创建的,并且每批数据都可以很快获得

我还担心JSON的性能,所以我转向了JIL:

    public static string ToJSON<T>(this T obj)
    {
        return Jil.JSON.Serialize<T>(obj);
    }
这使时间下降了一点,因此我可以在3分57秒内加载10米,速度为42194 rops。大部分时间实际上是应用程序内部的本地处理。如果我更改它,使每个线程加载相同的项目
ITEMS/THREADS
次,那么这将更改为1分48秒-速率为92592 rops


我不确定我是否真的回答了什么,但是简短的版本可能只是“尝试更长的超时时间,考虑使用火灾和忘记”。

当它失败时它做了什么?顶部的位(<代码>并行枚举< /代码>)?或
fore
循环中的位?如果顶部位失败,我不会感到惊讶,但这与SE.Redis无关。@spender是的,SE.Redis
IDatabase
实例是完全线程安全的,并且用于高度并发的access@MarcGravell很抱歉没有提到这一点,ParallelEnumerable工作正常。它可以启动在
for
循环中崩溃(一段时间后)你有黄色的还是类似的吗?基本上,我可以运行吗?@spender这纯粹是猜测;我看不出有任何理由怀疑与此相关的是,非常感谢你,我现在肯定要尝试它-还有-增加超时的设置在哪里?在*.conf文件中?@RoyiNamir这一版本-包括我想做的许多事情,并演示如何更改超时(您也可以通过更改strin
    public static string ToJSON<T>(this T obj)
    {
        return Jil.JSON.Serialize<T>(obj);
    }
 foreach (var element in InventPeople(PER_THREAD * counter1, PER_THREAD)
     .Select(x => new { x.Id, Json = x.ToJSON() }).Batchify(1000))