Multithreading 紧密循环-磁盘速度为100%,四核CPU使用率为25%,磁盘写入速度仅为15 MB
我有一个紧密的循环,它通过一堆Cart运行,Cart本身包含大约10个events事件对象,并通过一个中间存储库(jOliver common domain与GetEventStore.com重新连接)将它们以JSON格式写入磁盘:Multithreading 紧密循环-磁盘速度为100%,四核CPU使用率为25%,磁盘写入速度仅为15 MB,multithreading,performance,eventstoredb,Multithreading,Performance,Eventstoredb,我有一个紧密的循环,它通过一堆Cart运行,Cart本身包含大约10个events事件对象,并通过一个中间存储库(jOliver common domain与GetEventStore.com重新连接)将它们以JSON格式写入磁盘: //创建约200000个购物车,每个购物车有约5个事件 List testData=testData.GenerateFrom(产品); foreach(testData中的var cart) { count=count+(购物车作为IAggregate).GetU
//创建约200000个购物车,每个购物车有约5个事件
List testData=testData.GenerateFrom(产品);
foreach(testData中的var cart)
{
count=count+(购物车作为IAggregate).GetUncommittedEvents().count;
保存(购物车);
}
我看到磁盘上说它是100%,但吞吐量“低”(15MB/秒,每秒约5000个事件)。为什么会这样,我能想到的是:
public class GetEventStoreRepository : IRepository
{
private const string EventClrTypeHeader = "EventClrTypeName";
private const string AggregateClrTypeHeader = "AggregateClrTypeName";
private const string CommitIdHeader = "CommitId";
private const int WritePageSize = 500;
private const int ReadPageSize = 500;
IStreamNamingConvention streamNamingConvention;
private readonly IEventStoreConnection connection;
private static readonly JsonSerializerSettings serializerSettings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.None };
public GetEventStoreRepository(IEventStoreConnection eventStoreConnection, IStreamNamingConvention namingConvention)
{
this.connection = eventStoreConnection;
this.streamNamingConvention = namingConvention;
}
public void Save(IAggregate aggregate)
{
this.Save(aggregate, Guid.NewGuid(), d => { });
}
public void Save(IAggregate aggregate, Guid commitId, Action<IDictionary<string, object>> updateHeaders)
{
var commitHeaders = new Dictionary<string, object>
{
{CommitIdHeader, commitId},
{AggregateClrTypeHeader, aggregate.GetType().AssemblyQualifiedName}
};
updateHeaders(commitHeaders);
var streamName = this.streamNamingConvention.GetStreamName(aggregate.GetType(), aggregate.Identity);
var newEvents = aggregate.GetUncommittedEvents().Cast<object>().ToList();
var originalVersion = aggregate.Version - newEvents.Count;
var expectedVersion = originalVersion == 0 ? ExpectedVersion.NoStream : originalVersion - 1;
var eventsToSave = newEvents.Select(e => ToEventData(Guid.NewGuid(), e, commitHeaders)).ToList();
if (eventsToSave.Count < WritePageSize)
{
this.connection.AppendToStreamAsync(streamName, expectedVersion, eventsToSave).Wait();
}
else
{
var startTransactionTask = this.connection.StartTransactionAsync(streamName, expectedVersion);
startTransactionTask.Wait();
var transaction = startTransactionTask.Result;
var position = 0;
while (position < eventsToSave.Count)
{
var pageEvents = eventsToSave.Skip(position).Take(WritePageSize);
var writeTask = transaction.WriteAsync(pageEvents);
writeTask.Wait();
position += WritePageSize;
}
var commitTask = transaction.CommitAsync();
commitTask.Wait();
}
aggregate.ClearUncommittedEvents();
}
private static EventData ToEventData(Guid eventId, object evnt, IDictionary<string, object> headers)
{
var data = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(evnt, serializerSettings));
var eventHeaders = new Dictionary<string, object>(headers)
{
{
EventClrTypeHeader, evnt.GetType().AssemblyQualifiedName
}
};
var metadata = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(eventHeaders, serializerSettings));
var typeName = evnt.GetType().Name;
return new EventData(eventId, typeName, true, data, metadata);
}
}
公共类GetEventStoreRepository:IRepository
{
私有常量字符串EventClrTypeHeader=“EventClrTypeName”;
私有常量字符串aggregateClTypeHeader=“aggregateClTypeName”;
私有常量字符串CommitId=“CommitId”;
private const int WritePageSize=500;
private const int ReadPageSize=500;
IStreamNamingConvention streamNamingConvention;
私有只读IEventStoreConnection连接;
私有静态只读JsonSerializerSettings serializerSettings=new JsonSerializerSettings{TypeNameHandling=TypeNameHandling.None};
公共GetEventStoreRepository(IEventStoreConnection eventStoreConnection,IStreamNamingConvention namingConvention)
{
this.connection=eventStoreConnection;
this.streamNamingConvention=namingConvention;
}
公共作废保存(IAggregate聚合)
{
Save(聚合,Guid.NewGuid(),d=>{});
}
公共void保存(IAggregate聚合、Guid提交、操作更新头)
{
var commitHeaders=新字典
{
{commitId头,commitId},
{aggregateClTypeHeader,aggregate.GetType().AssemblyQualifiedName}
};
更新负责人(委员会负责人);
var streamName=this.streamNamingConvention.GetStreamName(aggregate.GetType(),aggregate.Identity);
var newEvents=aggregate.GetUncommittedEvents().Cast().ToList();
var originalVersion=aggregate.Version-newEvents.Count;
var expectedVersion=originalVersion==0?expectedVersion.NoStream:originalVersion-1;
var eventsToSave=newEvents.Select(e=>ToEventData(Guid.NewGuid(),e,committeaders)).ToList();
if(eventsToSave.CountCommitAsync
并在代码中等待,如果您的事件不是几kb大,这听起来是一个非常糟糕的主意。您每秒提交数千次微小写入,这对于SSD来说是一个噩梦。这也是一场噩梦。对于您写入的大约每一百个字节,磁盘必须擦除一个512kB的页面,从旧扇区复制511kB,并附加您正在写入的几个字节。当您的程序消耗100%内核时,它不会被磁盘卡住。这也是磁盘写入速率低的原因之一。JSON并不是一种非常便宜的格式,它包含很多字符串。使用合适的探查器。删除WaitA
public class GetEventStoreRepository : IRepository
{
private const string EventClrTypeHeader = "EventClrTypeName";
private const string AggregateClrTypeHeader = "AggregateClrTypeName";
private const string CommitIdHeader = "CommitId";
private const int WritePageSize = 500;
private const int ReadPageSize = 500;
IStreamNamingConvention streamNamingConvention;
private readonly IEventStoreConnection connection;
private static readonly JsonSerializerSettings serializerSettings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.None };
public GetEventStoreRepository(IEventStoreConnection eventStoreConnection, IStreamNamingConvention namingConvention)
{
this.connection = eventStoreConnection;
this.streamNamingConvention = namingConvention;
}
public void Save(IAggregate aggregate)
{
this.Save(aggregate, Guid.NewGuid(), d => { });
}
public void Save(IAggregate aggregate, Guid commitId, Action<IDictionary<string, object>> updateHeaders)
{
var commitHeaders = new Dictionary<string, object>
{
{CommitIdHeader, commitId},
{AggregateClrTypeHeader, aggregate.GetType().AssemblyQualifiedName}
};
updateHeaders(commitHeaders);
var streamName = this.streamNamingConvention.GetStreamName(aggregate.GetType(), aggregate.Identity);
var newEvents = aggregate.GetUncommittedEvents().Cast<object>().ToList();
var originalVersion = aggregate.Version - newEvents.Count;
var expectedVersion = originalVersion == 0 ? ExpectedVersion.NoStream : originalVersion - 1;
var eventsToSave = newEvents.Select(e => ToEventData(Guid.NewGuid(), e, commitHeaders)).ToList();
if (eventsToSave.Count < WritePageSize)
{
this.connection.AppendToStreamAsync(streamName, expectedVersion, eventsToSave).Wait();
}
else
{
var startTransactionTask = this.connection.StartTransactionAsync(streamName, expectedVersion);
startTransactionTask.Wait();
var transaction = startTransactionTask.Result;
var position = 0;
while (position < eventsToSave.Count)
{
var pageEvents = eventsToSave.Skip(position).Take(WritePageSize);
var writeTask = transaction.WriteAsync(pageEvents);
writeTask.Wait();
position += WritePageSize;
}
var commitTask = transaction.CommitAsync();
commitTask.Wait();
}
aggregate.ClearUncommittedEvents();
}
private static EventData ToEventData(Guid eventId, object evnt, IDictionary<string, object> headers)
{
var data = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(evnt, serializerSettings));
var eventHeaders = new Dictionary<string, object>(headers)
{
{
EventClrTypeHeader, evnt.GetType().AssemblyQualifiedName
}
};
var metadata = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(eventHeaders, serializerSettings));
var typeName = evnt.GetType().Name;
return new EventData(eventId, typeName, true, data, metadata);
}
}