我需要创建一个C#应用程序(Windows服务),该应用程序每5秒运行一次(间隔),并生成大约2000万个值。
我需要在5秒内将这2000万个值插入Redis(1个键/值),并确保在下一个间隔开始之前完成插入。
注意:我只需要在Redis中保留7个周期 => 2000万* 7 => 1.4亿个Redis键
我正在使用C#的Threading.Tasks调用一个函数(2000万次),以便并行(异步)处理它们。
我什至为我的进程创建了一个Redis客户端池,以便能够并行执行Redis查询。
这里是C#调用函数2000万次的部分:
List<Task> tasksList = new List<Task>();
foreach (object k in ListOf20MillionData)
{
tasksList.Add(
Task.Factory.StartNew(() =>
{
GenerateValue(k);
//Inside 'GenerateValue' data is generated and pushed to redis
})
);
}
这是'GenerateValue'中的一段代码,它从客户端池中获取redis客户端对象,执行插入操作并将redis客户端释放回该池中。
RedisClient redisClientObj = RedisPool.GetNextAvailableClient();
redisClientObj.Add("SomeKey", "SomeValue");
RedisPool.ReleaseRedisClient(redisClientObj );
我的关注和挑战:
答案 0 :(得分:2)
不是。池不会为您提供更多的吞吐量。它们通过顺序命令分解不同的逻辑连接范围,并允许简单的并发...但是redis核心是单线程的,您应该希望饱和网络,而不是线程。
吨数,但是添加更多的吨数对您没有帮助-实际上,建立大量的连接会增加开销。
仅在具有庞大网络的非常盒子上出现;您可能使用“集群”增加吞吐量,但这也会增加数据包碎片
批次。疯狂地批处理,以最大程度地减少往返次数。快速响应的胖批处理可以非常有效地利用网络,并且不需要复杂的代码。 redis mset
命令已针对以下方面进行了优化:胖批处理响应很小。
在本地,同一台计算机在单个线程上将数据发明为Redis服务器,但是对我来说,它仍然需要34秒,
static void Main()
{
using (var conn = ConnectionMultiplexer.Connect("127.0.0.1:6379"))
{
var db = conn.GetDatabase();
var watch = Stopwatch.StartNew();
foreach(var batch in InventData(20000000).Batchify(5000))
{
db.StringSet(batch);
}
watch.Stop();
Console.WriteLine(watch.ElapsedMilliseconds);
}
}
或者如果我使用Parallel
,即
var watch = Stopwatch.StartNew();
Parallel.ForEach(InventData(20000000).Batchify(5000),
batch => db.StringSet(batch));
watch.Stop();
需要16秒。
和(请参阅注释),如果我将Parallel
与async结合使用:
var watch = Stopwatch.StartNew();
Parallel.ForEach(InventData(20000000).Batchify(5000),
batch => db.StringSetAsync(batch));
watch.Stop();
然后只需不到14秒的时间。
与
static IEnumerable<KeyValuePair<RedisKey, RedisValue>> InventData(int count)
{
if (count < 0) throw new ArgumentOutOfRangeException(nameof(count));
string dictionary = "abcdefghijklmnopqrstuvwxyz _@:0123456789";
int dLen = dictionary.Length;
var rand = new Random(12345);
const int KEY_LEN = 10, MAX_VAL_LEN = 50;
char[] keyData = new char[KEY_LEN];
char[] valueData = new char[MAX_VAL_LEN];
while (count-- != 0)
{
for (int i = 0; i < keyData.Length; i++)
keyData[i] = dictionary[rand.Next(dLen)];
var len = rand.Next(10, MAX_VAL_LEN);
for(int i = 0; i < len; i++)
valueData[i] = dictionary[rand.Next(dLen)];
yield return new KeyValuePair<RedisKey, SomeType>(
new string(keyData), new string(valueData, 0, len));
}
}
static IEnumerable<T[]> Batchify<T>(this IEnumerable<T> source, int batchSize)
{
var batch = new List<T>(batchSize);
foreach(var item in source)
{
batch.Add(item);
if (batch.Count == batchSize)
{
var arr = batch.ToArray();
batch.Clear();
yield return arr;
}
}
if (batch.Count != 0) yield return batch.ToArray(); // trailers
}