3

我正在使用 Fluent-NHibernate(带有自动映射)来生成我的表,但想选择与默认使用的 ID 字段不同的聚集索引。如何使用 Fluent NHibernate 在默认主键字段以外的字段上创建聚集索引?

这背后的主要原因很简单。我将 Guids 用于我的主键字段。默认情况下,NHibernate 在主键字段上创建聚集索引。由于 Guid 通常不是顺序的,因此在主键字段上进行聚类会导致性能问题。

众所周知,在表的末尾追加记录比在表中插入记录要便宜得多。此外,表中的记录在物理上按照聚集索引中的项目顺序存储。由于 Guid 在某种程度上是“随机的”并且不是连续的,因此可能会生成小于表中已经存在的其他 Id Guid 的值的新 Guid - 导致表插入而不是追加。

为了尽量减少这种情况,我有一个名为 CreatedOn 的列,它的类型为 DateTime。我需要将表聚集在这个 CreatedOn 列上,以便附加而不是插入所有新记录。

欢迎任何关于如何实现这一点的想法!!!

注意:我意识到我可以使用 Sequential Guid,但出于安全原因,我不想走这条路。


注意:我仍然没有对这篇文章的答案,但我目前正在思考一些想法。

  1. 在没有 Fluent 的情况下使用 NHibernate,我认为可以直接在 NHibernate 中创建聚集索引。我对 NHibernate 的了解还不够,不知道如何做到这一点。我只是很漂亮(几乎绝对)确信它可以完成。

  2. Fluent-NHibernate 曾经包含一种在最近重写之前在 SQL 对象上设置属性(例如,聚集索引)的方法。现在这个选项似乎已经消失了。我可能会在某处发布一个问题,看看该选项是否仍然可用。如果是这样,我可能会使用它来设置聚集索引。

  3. Fluent-NHibernate 提供了在流畅构建后公开配置以进行手动编辑的能力。我没有尝试过这个功能,但希望它可以提供设置聚集索引所需的粒度级别。

  4. 最坏的情况是,我可以编写一个 SQL 脚本来更改所有表上的聚簇索引,一旦它们生成。但是,我对这种方法有几个问题。A. 由于我使用自动模式生成,NHibernate 会在下次评估配置时“撤消”我的聚集索引更改吗?2. NHibernate 检测到聚集索引被改变了会报错吗?我需要对此进行测试,但还没有这样做。我真的很讨厌这个解决方案。我正在针对 SQLServer2008 和 MySQL 测试我的数据库。NHibernate 的部分优点在于它与数据库无关。一旦我们引入脚本,所有的赌注都没有了。

  5. 有一个在称为 IPropertyInstance 的流式约定中使用的接口,从该接口继承的类具有一个 Index 属性,该属性允许在该字段上创建索引。问题是没有标志或其他选项允许将索引创建为聚集索引。最简单的解决方案是向此方法添加一个属性以允许创建聚集索引。我想我可能会向 Fluent-NHibernate 开发人员建议这个。

4

4 回答 4

7

这是一个旧帖子,但我希望可以帮助其他人。这来自我在 MS SQL Server 上的经验。我相信不同的平台需要不同的解决方案,但这应该是一个很好的起点。

NHibernate 不会在主键上设置 CLUSTERED 索引。这是 SQL Server 的默认行为。由于每个表只能有一个 CLUSTERED,我们首先需要避免在主键上创建 CLUSTERED。

我发现完成此操作的唯一方法是创建自定义方言,覆盖属性 PrimaryKeyString。NHibernate 的默认值来自Dialect.cs

    public virtual string PrimaryKeyString
    {
        get { return "primary key"; }
    }

对于 SQL 服务器

    public override string PrimaryKeyString
    {
        get { return "primary key nonclustered"; }
    }

这将强制 SQL Server 创建一个非集群主键。现在,您可以通过 XML 映射文件中的标记在您喜欢的列上添加自己的 CLUSTERED 索引。

<database-object>
  <create>
    create clustered index IX_CustomClusteredIndexName on TableName (ColumnName ASC)
  </create>
  <drop>
    drop index IX_CustomClusteredIndexName ON TableName
  </drop>
</database-object>
于 2011-01-14T13:52:47.083 回答
1

我不能具体回答这个问题,但是既然我在这里,我会给你一些数据库信息。

您需要告诉 NHibernate 在非聚集索引处创建主键。每个表只能有聚集索引,所以需要将表创建为堆,然后在其上放置聚集索引。

于 2009-08-31T09:01:28.357 回答
1

正如您自己所说,另一种选择是切换到 guid.comb ID 生成策略,其中 PK uid 基于 Guid 的一部分和确保生成的 ID 是顺序的部分。

在此处查看Jeffrey Palermo 的帖子中的更多信息。

但是您提到出于安全原因不想这样做-为什么?

于 2009-09-01T05:16:52.743 回答
0

就像@abx78 所说的那样,这是一篇旧帖子,但我也想分享我对解决此问题的知识。我为想法 3“Fluent NHibernate 公开映射”构建了解决方案:

在构建配置后(因此解析映射),Fluent NHibernate 为我们提供了使用configuration.ClassMappings和来查看实际映射的机会configuration.CollectionMappings。后者在下面的示例中用于设置复合主键,从而在 Sql Server 中生成聚集索引(正如@abx78 指出的那样):

foreach (var collectionMapping in configuration.CollectionMappings) {
    // Fetch the columns (in this example: build the columns in a hacky way)
    const string columnFormat = "{0}_id";
    var leftColumn = new Column(string.Format(
        columnFormat,
        collectionMapping.Owner.MappedClass.Name));
    var rightColumn = new Column(string.Format(
        columnFormat,
        collectionMapping.GenericArguments[0].Name));
    // Fetch the actual table of the many-to-many collection
    var manyToManyTable = collectionMapping.CollectionTable;
    // Shorten the name just like NHibernate does
    var shortTableName = (manyToManyTable.Name.Length <= 8)
                                ? manyToManyTable.Name
                                : manyToManyTable.Name.Substring(0, 8);
    // Create the primary key and add the columns
    // >> This part could be changed to a UniqueKey or to an Index
    var primaryKey = new PrimaryKey {
        Name = string.Format("PK_{0}", shortTableName),
    };
    primaryKey.AddColumn(leftColumn);
    primaryKey.AddColumn(rightColumn);
    // Set the primary key to the junction table
    manyToManyTable.PrimaryKey = primaryKey;
    // <<
}

资料来源:Fluent NHibernate:如何在多对多连接表上创建聚集索引?

于 2012-06-13T07:57:14.807 回答