对于多租户单共享数据库,tenantid字段是否应包含在主键和聚簇索引中?还是在tenantid上添加与性能相同的附加索引?

我们在生产系统中遇到性能问题,该系统的唯一索引是主键上的聚集索引。

所有sql select语句在其linq到诸如

invoiceitems.tenantid = thecurrenttenantid order by invoicedate


当前模式

租户(tenantid uniqueidentifier主键,租户名称)
外键(tenantid)
索引(聚集在tenantid上)

客户(tenantid uniqueidentifier,customerid uniqueidentifier主键,customername varchar(50))
外键(tenantid,customerid)
索引(聚集在customerid上)

发票(tenantid uniqueidentifier,invoiceid uniqueidentifier主键,billcustomerid uniqueidentifier,shipcustomerid uniqueidentifier,invoicedate日期时间)
外键(tenantid,billcustomerid,shipcustomerid)
索引(聚集在发票上)

InvoiceItems(tenantid uniqueidentifier,invoiceitemid uniqueidentifier主键,invoiceid uniqueidentifier,lineitemorder int)
外键(tenantid,发票)
索引(聚集在invoiceitemid上)

SqlAzure要求每个表都具有聚簇索引,因此它是默认值,因此当前仅位于primarykeyid上。现在,这是每个表上的唯一索引。整个系统中的表中都有各种外键,并且没有索引任何外键表字段。

我们正在尝试解决一些性能问题,并且想知道什么是最佳的聚集索引,以及是否还有其他索引可能会有所帮助。我们希望除非绝对必要,否则我们不必更改现有的聚集索引,但我们愿意这样做。在SqlAzure AFAIK中,您不能简单地调整现有表中的聚簇索引-您必须创建具有所需聚簇索引的新表并将所有记录从旧表插入到新表中(并处理所有外键约束和其他表相关性)。

所有sql select语句均在其linq to实体语句中以tenantid开头。

invoiceitems.tenantid = thecurrenttenantid order by invoicedate


一些sql select语句仅具有顺序-引入子表时,某些具有其他联接条件值,例如

invoiceitems.tenantid = thecurrenttenantid and invoice.invoiceid = invoiceitems.invoiceid order by invoicedate


这里有一些想法(除此之外,我们还对其他人开放)-其中哪一个最好,为什么?

主键索引选项

加快对租户记录的访问

选项1-在tenantid上添加非聚集索引

发票(tenantid uniqueidentifier,invoiceid uniqueidentifier主键,billcustomerid uniqueidentifier,shipcustomerid uniqueidentifier,invoicedate日期时间)
外键(tenantid,billcustomerid,shipcustomerid)
索引(聚集在发票上,非聚集在tenantid上)

选项2-将主键从primaryid更改为tenantid + primaryid,并将聚集索引更改为tenantid + primaryid。

发票(tenantid uniqueidentifier主键,invoiceid uniqueidentifier主键,billcustomerid uniqueidentifier,shipcustomerid uniqueidentifier,invoicedate日期时间)
外键(tenantid,billcustomerid,shipcustomerid)
索引(聚集在tenantid +发票上)

外键索引选项

加快加入

选项3-仅在Foreignkeyid上的所有外键字段上添加非聚集索引。

发票(tenantid uniqueidentifier,invoiceid uniqueidentifier主键,billcustomerid uniqueidentifier,shipcustomerid uniqueidentifier,invoicedate日期时间)
外键(tenantid,billcustomerid,shipcustomerid)
索引(聚集在发票上,非聚集在billcustomerid上,非聚集在shipcustomerid上)

选项4-将所有外键从foreignkeyid更改为tenantid + Foreignkeyid,并在tenantid + Foreignkeyid上添加索引

发票(tenantid uniqueidentifier,invoiceid uniqueidentifier主键,billcustomerid uniqueidentifier,shipcustomerid uniqueidentifier,invoicedate日期时间)
外键(tenantid,tenantid + billcustomerid,tenantid + shipcustomerid)
索引(在发票上集群,在tenantid + billcustomerid上非集群,在tenantid + shipcustomerid上非集群)

SQL SELECT优化索引选项

为了加快常用查询的速度,例如从发票中选择字段,其中tenantid =按发票日期排序

选项5-在除tenantid之外的每个表中最常用的排序顺序字段上添加索引。

发票(tenantid uniqueidentifier,invoiceid uniqueidentifier主键,billcustomerid uniqueidentifier,shipcustomerid uniqueidentifier,invoicedate日期时间)
外键(tenantid,billcustomerid,shipcustomerid)
索引(聚集在发票上,非聚集在发票上)

选项6-在每个表中的tenantid +“最常用的排序顺序字段”上添加索引,并在tenantid +“最常用的排序顺序字段”上添加非聚集索引

发票(tenantid uniqueidentifier,invoiceid uniqueidentifier主键,billcustomerid uniqueidentifier,shipcustomerid uniqueidentifier,invoicedate日期时间)
外键(tenantid,billcustomerid,shipcustomerid)
索引(聚集在发票上,非聚集在tenantid +发票上)

最佳答案

看来您已经提出了很多想法。不管我或其他人说什么,唯一可以确定的方法就是自己测量。在这种情况下,这不再是SQL Azure问题,而是更多的常规SQL Server查询优化问题。

对于您的情况,有一些提示可以帮助您入门。使用LINQ时,您无法直接访问在SQL中运行的实际查询。您可能认为您知道查询的外观,但是根据您使用的EF版本,它可以对如何构造查询做出一些有趣的决定。为了确切地了解正在运行的查询,您需要使用SQL Profiler或Extended Events。 SQL Profiler不适用于SQL Azure,因此您需要使用扩展事件或在某个地方的本地服务器上获取数据库的副本,然后运行指向本地的应用程序。 SQL Server Management Studio(SSMS)中的export data tier application和相关的import对于此非常有用。

使用实际查询,然后可以针对Azure中的数据库在SSMS中运行它们,以获取执行计划。然后,您可以更改索引,再次运行查询并比较计划。如果您不想弄乱您的主要开发数据库,​​则可以使用in a number of ways轻松创建副本,包括使用CREATE DATABASE xxx AS COPY OF yyyy命令。

不要试图在本地数据库上进行优化。与大多数本地SQL安装相比,SQL Azure具有不同的性能概述。

综上所述,如果所有查询始终都包含租户ID,则可以,我希望将其作为聚簇索引的第一部分将改善查询性能。对于所有其他索引,我不太确定,因此要进行测量,测量和测量。还要记住,索引不是免费提供的,创建的每个索引都会影响写入性能以及数据库的大小,因此我不会胡乱地为所有索引编制索引。

最后,如果您的数据库足够大以至于您需要按租户ID联合它(您的结构看起来将很好地处理它),则不必担心为PK使用GUID IDENTITY列将不再是一个选择。

08-07 17:32