如何使用Fluent NHibernate创建聚簇索引?

时间:2009-08-31 08:38:09

标签: nhibernate fluent-nhibernate automapping clustered-index

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

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

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

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

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

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


注意:我仍然没有这篇文章的答案,但我现在有一些想法。

  1. 使用NHibernate而不使用Fluent,我认为可以直接在NHibernate中创建聚簇索引。我还不太了解NHibernate知道如何做到这一点。我很漂亮(几乎绝对是)确定可以做到。

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

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

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

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

4 个答案:

答案 0 :(得分:7)

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

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

我发现完成此任务的唯一方法是创建一个自定义Dialect,覆盖有效的PrimaryKeyString。 NHibernate的默认值来自 Dialect.cs

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

对于SQL Server

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

这将强制SQL Server创建NONCLUSTERED主键。 现在,您可以通过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>

答案 1 :(得分:1)

我无法回答这个问题,但是我会在这里给你一些数据库信息。

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

答案 2 :(得分:1)

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

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

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

答案 3 :(得分:0)

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

在构建配置之后(因此解析了映射),Fluent NHibernate为我们提供了查看configuration.ClassMappingsconfiguration.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: How to create a clustered index on a Many-to-Many Join Table?