具有多个插入请求的 NHibernate 性能不佳
NHibernate bad performance with multiple insert request
我使用 nhibernate 的 .net 5 网站遇到性能问题。
我有控制器和应用程序使用的操作方法,也被转为 api 调用
action 简单地插入一条记录到这种类型
public class Ente : BaseObject<Ente, string>
{
public override string Id { get; set; }
public virtual string Descrizione { get; set; }
public virtual bool Perimetro { get; set; }
public virtual int Ordine { get; set; }
}
其中 baseObject 实现了 Equals 方法
当应用程序收到多个 rest 调用时会出现性能问题:每次插入大约需要 2 秒
我可以连续接听数百个电话,系统只需几分钟即可管理所有电话
这是存储库中的保存方法:
protected ISession _session;
public CrudlRepository(ISession session)
{
_session = session;
}
public virtual void Create(TEntity entity)
{
_session.Save(entity);
}
这是动作方法
[HttpPost]
public IActionResult Insert(string values)
{
var obj = new Ente ();
using (myUow = myUowFactory.Create())
{
JsonConvert.PopulateObject(values, obj);
myUow.Repository.Create(obj);
myUow.SaveAll();
}
return Ok("Operazione completata con successo.");
}
这是我的配置:
string connectionString = DbSettings.Current.ConnectionStrings.OracleWHB;
services.AddSingleton<ISessionFactory>(sp => Fluently.Configure()
.Database(OracleManagedDataClientConfiguration.Oracle10
.ConnectionString(DbSettings.Current.ConnectionStrings.OracleWHB)
.DoNot.ShowSql()
.DoNot.FormatSql())
.Mappings(m =>
m.FluentMappings.AddFromAssembly(Assembly.GetExecutingAssembly())
.Conventions.AddFromAssemblyOf<UnitOfWork>()
)
.ExposeConfiguration(config => SetupEnvers(config, sp))
.ExposeConfiguration(config => new SchemaUpdate(config).Execute(false, genereteDB))
//https://weblogs.asp.net/ricardoperes/on-nhibernate-performance per dettagli
.ExposeConfiguration(config => config.SetProperty(NHibernate.Cfg.Environment.GenerateStatistics, Boolean.FalseString))
.ExposeConfiguration(config => config.SetProperty(NHibernate.Cfg.Environment.Hbm2ddlKeyWords, Hbm2DDLKeyWords.None.ToString()))
.ExposeConfiguration(config => config.SetProperty(NHibernate.Cfg.Environment.PrepareSql, Boolean.TrueString))
.ExposeConfiguration(config => config.SetProperty(NHibernate.Cfg.Environment.PropertyBytecodeProvider, "lcg"))
.ExposeConfiguration(config => config.SetProperty(NHibernate.Cfg.Environment.PropertyUseReflectionOptimizer, Boolean.TrueString))
.ExposeConfiguration(config => config.SetProperty(NHibernate.Cfg.Environment.QueryStartupChecking, Boolean.FalseString))
.ExposeConfiguration(config => config.SetProperty(NHibernate.Cfg.Environment.UseProxyValidator, Boolean.FalseString))
.ExposeConfiguration(config => config.SetProperty(NHibernate.Cfg.Environment.UseSecondLevelCache, Boolean.FalseString))
.ExposeConfiguration(config => config.SetProperty(NHibernate.Cfg.Environment.UseSqlComments, Boolean.FalseString))
.ExposeConfiguration(config => config.SetProperty(NHibernate.Cfg.Environment.UseQueryCache, Boolean.FalseString))
.ExposeConfiguration(config => config.SetProperty(NHibernate.Cfg.Environment.WrapResultSets, Boolean.TrueString))
.BuildSessionFactory()
);
services.AddScoped<ISession>(sp => sp.GetService<ISessionFactory>().OpenSession());
services.AddTransient<IRevisionListener, EnversRevisionListener>();
services.AddScoped<IUnitOfWorkFactory, UnitOfWorkFactory>();
services.AddScoped<IUnitOfWork, UnitOfWork>();
return services;
基础对象
public abstract class BaseObject<TEntity, TKey> : EquatableObject<TEntity>
where TEntity : class
{
public abstract TKey Id { get; set; }
public override int GetHashCode()
{
return Id.GetHashCode();
}
public override bool Equals(object obj)
{
if (obj == null)
return false;
BaseObject<TEntity, TKey> other = obj as BaseObject<TEntity, TKey>;
return BaseObjectEquals(other);
}
public override bool Equals(TEntity obj)
{
if (obj == null)
return false;
BaseObject<TEntity, TKey> other = obj as BaseObject<TEntity, TKey>;
return BaseObjectEquals(other);
}
public virtual bool BaseObjectEquals(BaseObject<TEntity, TKey> other)
{
if (other == null)
return false;
return EqualityComparer<TKey>.Default.Equals(this.Id , other.Id);
}
private IEnumerable<FieldInfo> GetFields()
{
Type t = GetType();
List<FieldInfo> fields = new List<FieldInfo>();
while (t != typeof(object))
{
fields.AddRange(t.GetTypeInfo().DeclaredFields.Where(x => x.FieldType.Name != typeof(ICollection<>).Name));//.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public));
t = t.GetTypeInfo().BaseType;
}
return fields;
}
public static bool operator ==(BaseObject<TEntity, TKey> x, BaseObject<TEntity, TKey> y)
{
// If both are null, or both are same instance, return true.
if (System.Object.ReferenceEquals(x, y))
{
return true;
}
// If one is null, but not both, return false.
if (((object)x == null) || ((object)y == null))
{
return false;
}
return x.Equals(y);
}
public static bool operator !=(BaseObject<TEntity, TKey> x, BaseObject<TEntity, TKey> y)
{
return !(x == y);
}
}
EquatableObject
public abstract class EquatableObject<TObject>: IEquatable<TObject>
where TObject : class
{
/// < summary >
/// Default Constructor.
/// < /summary >
protected EquatableObject()
{
}
public override int GetHashCode()
{
Type t = GetType();
TypeInfo typeInfo = t.GetTypeInfo();
IEnumerable<FieldInfo> fields = typeInfo.DeclaredFields.Where(x => x.FieldType.Name != typeof(ICollection<>).Name);
int startValue = 17;
int multiplier = 59;
int hashCode = startValue;
foreach (FieldInfo field in fields)
{
object value = field.GetValue(this);
if (value != null)
hashCode = hashCode * multiplier + value.GetHashCode();
}
return hashCode;
}
public override bool Equals(object obj)
{
if (obj == null)
return false;
TObject other = obj as TObject;
return Equals(other);
}
public virtual bool Equals(TObject other)
{
if (other == null)
return false;
Type t = GetType();
TypeInfo typeInfo = t.GetTypeInfo();
IEnumerable<FieldInfo> fields = typeInfo.DeclaredFields.Where(x => x.FieldType.Name != typeof(ICollection<>).Name);
foreach (FieldInfo field in fields)
{
object value1 = field.GetValue(other);
object value2 = field.GetValue(this);
if (value1 == null)
{
if (value2 != null)
return false;
}
else if (!value1.Equals(value2))
return false;
}
return true;
}
public static bool operator ==(EquatableObject<TObject> x, EquatableObject<TObject> y)
{
// If both are null, or both are same instance, return true.
if (System.Object.ReferenceEquals(x, y))
{
return true;
}
// If one is null, but not both, return false.
if (((object)x == null) || ((object)y == null))
{
return false;
}
return x.Equals(y);
}
public static bool operator !=(EquatableObject<TObject> x, EquatableObject<TObject> y)
{
return !(x == y);
}
}
存储库中全部保存
public void SaveAll()
{
if (_transaction != null)
{
_transaction.Commit();
_transaction = null;
}
}
您应该检查问题是否不在数据库级别,性能调整只能在测量所有涉及的部分后才能进行,但基于您显示的代码:
如果您使用的 nhibernate 版本支持异步调用,您可以使用类似
await _transaction.CommitAsync();
然后,在任何情况下,您都可以在像您描述的长 I/O 调用的并发场景中从中受益。如果数据库服务器运行正常,我猜这是您的主要问题。
async in depth on microsoft.com
Throughout this entire process, a key takeaway is that no thread is dedicated to running the task. Although work is executed in some context (that is, the OS does have to pass data to a device driver and respond to an interrupt), there is no thread dedicated to waiting for data from the request to come back. This allows the system to handle a much larger volume of work rather than waiting for some I/O call to finish.
为避免被迫将所有方法更改为异步,请添加异步版本的 SaveAll(),例如SaveAllAsync(),这将需要更改为仅异步此控制器中的方法。整体收益将小于完全改变,但您至少可以测试一下它是否是一条好路。
另一方面,在您的 equatable 对象中,您可以将反射的枚举缓存在某处,这样您就不需要每次都执行此操作:
Type t = GetType();
TypeInfo typeInfo = t.GetTypeInfo();
IEnumerable<FieldInfo> fields = typeInfo.DeclaredFields.Where(x => x.FieldType.Name != typeof(ICollection<>).Name);
我使用 nhibernate 的 .net 5 网站遇到性能问题。
我有控制器和应用程序使用的操作方法,也被转为 api 调用
action 简单地插入一条记录到这种类型
public class Ente : BaseObject<Ente, string>
{
public override string Id { get; set; }
public virtual string Descrizione { get; set; }
public virtual bool Perimetro { get; set; }
public virtual int Ordine { get; set; }
}
其中 baseObject 实现了 Equals 方法
当应用程序收到多个 rest 调用时会出现性能问题:每次插入大约需要 2 秒
我可以连续接听数百个电话,系统只需几分钟即可管理所有电话
这是存储库中的保存方法:
protected ISession _session;
public CrudlRepository(ISession session)
{
_session = session;
}
public virtual void Create(TEntity entity)
{
_session.Save(entity);
}
这是动作方法
[HttpPost]
public IActionResult Insert(string values)
{
var obj = new Ente ();
using (myUow = myUowFactory.Create())
{
JsonConvert.PopulateObject(values, obj);
myUow.Repository.Create(obj);
myUow.SaveAll();
}
return Ok("Operazione completata con successo.");
}
这是我的配置:
string connectionString = DbSettings.Current.ConnectionStrings.OracleWHB;
services.AddSingleton<ISessionFactory>(sp => Fluently.Configure()
.Database(OracleManagedDataClientConfiguration.Oracle10
.ConnectionString(DbSettings.Current.ConnectionStrings.OracleWHB)
.DoNot.ShowSql()
.DoNot.FormatSql())
.Mappings(m =>
m.FluentMappings.AddFromAssembly(Assembly.GetExecutingAssembly())
.Conventions.AddFromAssemblyOf<UnitOfWork>()
)
.ExposeConfiguration(config => SetupEnvers(config, sp))
.ExposeConfiguration(config => new SchemaUpdate(config).Execute(false, genereteDB))
//https://weblogs.asp.net/ricardoperes/on-nhibernate-performance per dettagli
.ExposeConfiguration(config => config.SetProperty(NHibernate.Cfg.Environment.GenerateStatistics, Boolean.FalseString))
.ExposeConfiguration(config => config.SetProperty(NHibernate.Cfg.Environment.Hbm2ddlKeyWords, Hbm2DDLKeyWords.None.ToString()))
.ExposeConfiguration(config => config.SetProperty(NHibernate.Cfg.Environment.PrepareSql, Boolean.TrueString))
.ExposeConfiguration(config => config.SetProperty(NHibernate.Cfg.Environment.PropertyBytecodeProvider, "lcg"))
.ExposeConfiguration(config => config.SetProperty(NHibernate.Cfg.Environment.PropertyUseReflectionOptimizer, Boolean.TrueString))
.ExposeConfiguration(config => config.SetProperty(NHibernate.Cfg.Environment.QueryStartupChecking, Boolean.FalseString))
.ExposeConfiguration(config => config.SetProperty(NHibernate.Cfg.Environment.UseProxyValidator, Boolean.FalseString))
.ExposeConfiguration(config => config.SetProperty(NHibernate.Cfg.Environment.UseSecondLevelCache, Boolean.FalseString))
.ExposeConfiguration(config => config.SetProperty(NHibernate.Cfg.Environment.UseSqlComments, Boolean.FalseString))
.ExposeConfiguration(config => config.SetProperty(NHibernate.Cfg.Environment.UseQueryCache, Boolean.FalseString))
.ExposeConfiguration(config => config.SetProperty(NHibernate.Cfg.Environment.WrapResultSets, Boolean.TrueString))
.BuildSessionFactory()
);
services.AddScoped<ISession>(sp => sp.GetService<ISessionFactory>().OpenSession());
services.AddTransient<IRevisionListener, EnversRevisionListener>();
services.AddScoped<IUnitOfWorkFactory, UnitOfWorkFactory>();
services.AddScoped<IUnitOfWork, UnitOfWork>();
return services;
基础对象
public abstract class BaseObject<TEntity, TKey> : EquatableObject<TEntity>
where TEntity : class
{
public abstract TKey Id { get; set; }
public override int GetHashCode()
{
return Id.GetHashCode();
}
public override bool Equals(object obj)
{
if (obj == null)
return false;
BaseObject<TEntity, TKey> other = obj as BaseObject<TEntity, TKey>;
return BaseObjectEquals(other);
}
public override bool Equals(TEntity obj)
{
if (obj == null)
return false;
BaseObject<TEntity, TKey> other = obj as BaseObject<TEntity, TKey>;
return BaseObjectEquals(other);
}
public virtual bool BaseObjectEquals(BaseObject<TEntity, TKey> other)
{
if (other == null)
return false;
return EqualityComparer<TKey>.Default.Equals(this.Id , other.Id);
}
private IEnumerable<FieldInfo> GetFields()
{
Type t = GetType();
List<FieldInfo> fields = new List<FieldInfo>();
while (t != typeof(object))
{
fields.AddRange(t.GetTypeInfo().DeclaredFields.Where(x => x.FieldType.Name != typeof(ICollection<>).Name));//.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public));
t = t.GetTypeInfo().BaseType;
}
return fields;
}
public static bool operator ==(BaseObject<TEntity, TKey> x, BaseObject<TEntity, TKey> y)
{
// If both are null, or both are same instance, return true.
if (System.Object.ReferenceEquals(x, y))
{
return true;
}
// If one is null, but not both, return false.
if (((object)x == null) || ((object)y == null))
{
return false;
}
return x.Equals(y);
}
public static bool operator !=(BaseObject<TEntity, TKey> x, BaseObject<TEntity, TKey> y)
{
return !(x == y);
}
}
EquatableObject
public abstract class EquatableObject<TObject>: IEquatable<TObject>
where TObject : class
{
/// < summary >
/// Default Constructor.
/// < /summary >
protected EquatableObject()
{
}
public override int GetHashCode()
{
Type t = GetType();
TypeInfo typeInfo = t.GetTypeInfo();
IEnumerable<FieldInfo> fields = typeInfo.DeclaredFields.Where(x => x.FieldType.Name != typeof(ICollection<>).Name);
int startValue = 17;
int multiplier = 59;
int hashCode = startValue;
foreach (FieldInfo field in fields)
{
object value = field.GetValue(this);
if (value != null)
hashCode = hashCode * multiplier + value.GetHashCode();
}
return hashCode;
}
public override bool Equals(object obj)
{
if (obj == null)
return false;
TObject other = obj as TObject;
return Equals(other);
}
public virtual bool Equals(TObject other)
{
if (other == null)
return false;
Type t = GetType();
TypeInfo typeInfo = t.GetTypeInfo();
IEnumerable<FieldInfo> fields = typeInfo.DeclaredFields.Where(x => x.FieldType.Name != typeof(ICollection<>).Name);
foreach (FieldInfo field in fields)
{
object value1 = field.GetValue(other);
object value2 = field.GetValue(this);
if (value1 == null)
{
if (value2 != null)
return false;
}
else if (!value1.Equals(value2))
return false;
}
return true;
}
public static bool operator ==(EquatableObject<TObject> x, EquatableObject<TObject> y)
{
// If both are null, or both are same instance, return true.
if (System.Object.ReferenceEquals(x, y))
{
return true;
}
// If one is null, but not both, return false.
if (((object)x == null) || ((object)y == null))
{
return false;
}
return x.Equals(y);
}
public static bool operator !=(EquatableObject<TObject> x, EquatableObject<TObject> y)
{
return !(x == y);
}
}
存储库中全部保存
public void SaveAll()
{
if (_transaction != null)
{
_transaction.Commit();
_transaction = null;
}
}
您应该检查问题是否不在数据库级别,性能调整只能在测量所有涉及的部分后才能进行,但基于您显示的代码:
如果您使用的 nhibernate 版本支持异步调用,您可以使用类似
await _transaction.CommitAsync();
然后,在任何情况下,您都可以在像您描述的长 I/O 调用的并发场景中从中受益。如果数据库服务器运行正常,我猜这是您的主要问题。
async in depth on microsoft.com
Throughout this entire process, a key takeaway is that no thread is dedicated to running the task. Although work is executed in some context (that is, the OS does have to pass data to a device driver and respond to an interrupt), there is no thread dedicated to waiting for data from the request to come back. This allows the system to handle a much larger volume of work rather than waiting for some I/O call to finish.
为避免被迫将所有方法更改为异步,请添加异步版本的 SaveAll(),例如SaveAllAsync(),这将需要更改为仅异步此控制器中的方法。整体收益将小于完全改变,但您至少可以测试一下它是否是一条好路。
另一方面,在您的 equatable 对象中,您可以将反射的枚举缓存在某处,这样您就不需要每次都执行此操作:
Type t = GetType();
TypeInfo typeInfo = t.GetTypeInfo();
IEnumerable<FieldInfo> fields = typeInfo.DeclaredFields.Where(x => x.FieldType.Name != typeof(ICollection<>).Name);