【问题标题】:Spreading Load on SQL Server database在 SQL Server 数据库上分散负载
【发布时间】:2013-09-16 15:24:29
【问题描述】:

我们有一个使用 Microsoft SQL Server 作为数据库后端的企业应用程序。我们遇到过一些案例,其中客户将应用程序发展成一个巨大的数据库,并且正在运行的一些查询导致他们自己和其他用户出现锁定和性能问题。

我们已尝试应用尽可能多的索引,并将所有查询调整到极限,但我们有一个应用程序必须适合许多不同的客户类型,因此很难创建一种适合所有人的解决方案。我们没有资源为每个客户应用客户特定的索引/性能。

我们知道导致问题的主要查询是为驱动报告和 kpi 生成的查询。

我的问题是,有没有一种方法可以分散应用程序的负载,以便日常使用不会受到报告/kpi 生成的阻碍。即,我们能否以某种方式镜像/复制数据库,以便将日常操作发送到 SQL 实体 A,并将数据密集型查询发送到 SQL 实体 B?因此,数据密集型查询对日常事务没有影响,我们可以将查询排队到 SQL 实体 B。

在这种情况下,SQL 实体 A 和 B 需要始终保持对齐,但 SQL 实体 B 将始终是只读的。

任何人都可以建议我们可以尝试实现此目的的任何路径吗?或者我应该考虑另一种方法来让我们在性能上获胜。

谢谢

【问题讨论】:

  • 恐怕你也想吃蛋糕。您可以设置第二台服务器并将您的“主”服务器复制到它,但如果您想让它们 100% 的时间同步,这意味着主服务器上的任何处理也会导致“报告”服务器上的负载.根据处理的类型,“复制工作量”(这只是实际的更改,而不是计算等)可能不会那么“繁重”,但它仍然会产生一些影响,并且会带来额外的负担(日志传送)。
  • 尝试过类似Snapshot Isolation 的方法吗?这可能会有所帮助,因为(只读)报告不会阻止实际处理,同时仍会返回对数据的“一致”视图。
  • 只是一点旁注。 OLTP 和分析在同一个数据库中不能很好地协同工作。也许大师在下面的答案中建议您进行的那种复制可以将读写部分(OLTP)与只读部分(分析)分开。通过这种方式,您可以在两者中使用特定的优化。

标签: sql-server database sql-server-2008 indexing enterprise


【解决方案1】:

您可以查看分区表,对数据进行分区,以使报告/BI 操作不会影响您的日常 OLTP 性能。当您需要清除旧数据时,它还可以节省一些宝贵的时间。

【讨论】:

    【解决方案2】:

    看来您可以使用任何复制选项并且没问题。 在我之前的一项工作中,我们为此使用了 Log Shipping http://technet.microsoft.com/en-us/library/ms187103.aspx

    您还可以处理复制类型:http://technet.microsoft.com/en-us/library/bb677158.aspx,看看哪一个最适合您,因为您可以做的不仅仅是在辅助数据库上报告。

    如果我没记错我最初的经历,日志传送非常容易设置,因此您可能想从那里开始。

    【讨论】:

      【解决方案3】:

      看看ScaleArc。它是 SQL 连接管理器,它通过跨多个实例对读取和写入进行分区来实现负载平衡。这确实意味着你必须签署做做复制..

      【讨论】:

        【解决方案4】:

        啊...性能调整 SQL Server 等。人。我最喜欢的东西!

        任何人都可以建议我们可以尝试实现此目标的任何路径吗?

        根据您提供的信息,我将对数据进行垂直分区。这意味着维护一个数据库(服务器 A)用于实际 OLTP(CRUD 事务)和一个用于 KPI(服务器 B)。

        对于复制,我将使用事务复制 - 正确执行时延迟将低于一秒。我想不出这是不合适的实际场景。事实上,大多数报告都是在前一天结束时完成的,“实时”通常意味着最后 5 分钟

        为了管理复制过程,我将从一个简单的控制台应用程序开始,并期望在适当的时候对其进行扩展以满足需求。控制台应用程序应使用以下命名空间(重新考虑后可能有可用于 SQL2012 的后续命名空间)

        using Microsoft.SqlServer.Management.Common;
        using Microsoft.SqlServer.Management.Smo;
        using Microsoft.SqlServer.Replication;
        

        使用控制台应用程序,您可以在单个界面中管理发布、订阅和任何跟踪令牌。配置(所有这些权限、密码和路径)会很痛苦,但是一旦它启动并运行,您将能够优化事务数据库的数据和报告服务器的……报告。

        我将有一个复制拓扑,对于大表来说,每个表实际上只有一个订阅,其余的(查找表、视图 sp)只有一个订阅。我会复制主键,但不会复制约束、表引用、触发器(依赖源数据库来保证完整性)。您也不应该复制索引 - 可以为报告服务器手动配置/优化它们。

        您还可以选择适合 KPI 的文章,即(无需复制文本、varchar(max) 等)

        下面发布了一些帮助函数以帮助您进行操作。

        或者我应该寻找另一种方法来获取我们 性能获胜?

        根据我的卑微经验,总是可以做一些事情来提高性能。它归结为时间->成本->收益。有时,在功能上稍作妥协会给您带来很多性能优势。

        魔鬼在细节中,但有这个警告......

        进一步的随机想法

        您已确定您的基础架构问题之一 - 混合 OLTP 和 BI/报告。我不清楚您的经验以及您的性能问题有多糟糕,而复制绝对是正确的方法,如果您处于“灭火”模式,您可以尝试。

        1. KPI 结果(5 分钟、1 小时、1 天?)在数据库或 RAM 中的服务器端缓存
        2. 使用模式绑定视图,您可以创建索引视图(在标准版和企业版上),具体取决于 KPI 的类型 - 这甚至可能是您需要做的所有事情!请参阅http://msdn.microsoft.com/en-us/library/ms191432.aspx 了解更多信息。从本质上讲,如果您的 KPI 是汇总/分组依据,您应该好好看看。
        3. 预先计算每日 KPI 的总和。然后,您可以选择仅添加当天的数据。
        4. 排序 - 由于order by 子句,KPI 是否昂贵。确保您的聚集索引是正确的(REM:它们不需要存在于主键上)。获得数据后尝试在客户端上进行排序
        5. 聚集索引大小。越小越好。如果您使用 GUID,请从这里开始
        6. 对数据进行垂直分区 - 例如,如果您有一个包含 200 列的表,但 KPI 仅使用 10 列 - 将 10 列放入不同的表中 - 您将在每次读取 i/o 页面时获得更多数据(如果你的磁盘是瓶颈)
        7. 提供“通过电子邮件发送报告”的功能 - 消除实时性。当事情比较安静并且白天的“实时”报告量较少时,您可能能够在一夜之间提供一定百分比的报告。有些客户实际上可能更喜欢此功能
        8. 让您的客户为报告付费! “只需在此处输入您的信用卡详细信息……”这是减少报告数量的可靠方法:)

        有关您的配置的更多信息会很有用 - 当您说巨大时,有多大?有多大/什么类型的磁盘,什么是 RAM 规格等等——我问的原因是这个……你可能会花接下来的 40 个工作日(每天 500 美元以上?)进行调整——这会给你带来很多硬件! - 更多 RAM、更多磁盘、更快的磁盘 - 用于 tempdb 或索引分区的 SSD。换句话说……你可能对硬件要求太高了(而你的老板对你要求太高了)

        接下来,您描述一个企业应用程序,这是一个 Enterprise SQL Server 许可证。如果是这样,您很幸运 - 您可以创建模式绑定分区视图并将查询委托给“正确”服务器。虽然这种模型存在一些问题 - 即连接,但它确实为您提供了一个有效的替代选项。

        复制代码

        我知道我在某处拥有它。在下面找到一些 RMO 辅助函数,您可能会发现它们对开始复制很有用。在过去的某个时候,它是实时代码,但可能比我想的要早 - 请视为伪代码。

        (PS 很高兴您可以直接联系)

         public static class RMOHelper
            {
                public static void PreparePublicationDb(MyServer Src, MyServer Dist)
                {
                    ReplicationDatabase publicationDb = new ReplicationDatabase(Src.Database, Src.ServerConnection);
                    if (publicationDb.LoadProperties())
                    {
                        if (!publicationDb.EnabledTransPublishing)
                        {
                            publicationDb.EnabledTransPublishing = true;
                        }
        
                        // If the Log Reader Agent does not exist, create it.
                        if (!publicationDb.LogReaderAgentExists)
                        {
                            // Specify the Windows account under which the agent job runs.
                            // This account will be used for the local connection to the 
                            // Distributor and all agent connections that use Windows Authentication.
                            publicationDb.LogReaderAgentProcessSecurity.Login = Dist.WinUId;
                            publicationDb.LogReaderAgentProcessSecurity.Password = Dist.WinPwd;
        
                            // Explicitly set authentication mode for the Publisher connection
                            // to the default value of Windows Authentication.
                            publicationDb.LogReaderAgentPublisherSecurity.WindowsAuthentication = true;
        
                            // Create the Log Reader Agent job.
                            publicationDb.CreateLogReaderAgent();
                            DeleteJobAgentSchedule(publicationDb.LogReaderAgentName);
                        }
                    }
                    else
                    {
                        throw new ApplicationException(String.Format(
                            "The {0} database does not exist at {1}.",
                            publicationDb,
                            Src.ServerName));
                    }
                }
                public static TransPublication PrepareTransPublication(MyServer Src, MyServer Dist, string publicationName)
                {
                    // Set the required properties for the transactional publication.
                    TransPublication publication = new TransPublication();
                    publication.ConnectionContext = Src.ServerConnection;
                    publication.Name = publicationName;
                    publication.DatabaseName = Src.Database;
                    if (publicationName == "relation")
                    {
                        float d = 0;
        
                    }
        
                    // Specify a transactional publication (the default).
                    publication.Type = PublicationType.Transactional;
                    publication.ConflictRetention = 4;
                    publication.RetentionPeriod = 72;
                    // Activate the publication so that we can add subscriptions.
                    publication.Status = State.Active;
                    // Enable push and pull subscriptions and independent Distribition Agents.
                    publication.Attributes = PublicationAttributes.AllowPull|PublicationAttributes.AllowPush|PublicationAttributes.IndependentAgent;
                    //publication.Attributes &= PublicationAttributes.AllowSyncToAlternate;
                    // Specify the Windows account under which the Snapshot Agent job runs.
                    // This account will be used for the local connection to the 
                    // Distributor and all agent connections that use Windows Authentication.
                    publication.SnapshotGenerationAgentProcessSecurity.Login = Dist.WinUId;
                    publication.SnapshotGenerationAgentProcessSecurity.Password = Dist.WinPwd;
        
                    // Explicitly set the security mode for the Publisher connection
                    // Windows Authentication (the default).
                    publication.SnapshotGenerationAgentPublisherSecurity.WindowsAuthentication = true;
                    publication.SnapshotGenerationAgentProcessSecurity.Login =Dist.WinUId;
                    publication.SnapshotGenerationAgentProcessSecurity.Password =  Dist.WinPwd;
        
                    publication.AltSnapshotFolder = @"\\192.168.35.4\repldata\";
                    if (!publication.IsExistingObject)
                    {
                        // Create the transactional publication.
                        publication.Create();
        
                        // Create a Snapshot Agent job for the publication.
                        publication.CreateSnapshotAgent();
                        // DeleteJobAgentSchedule(ByVal jobID As Guid) As Boolean
                    }
                    else
                    {
                        //throw new ApplicationException(String.Format(
                        //    "The {0} publication already exists.", publicationName));
                    }
                    return publication;
                }
                public static TransArticle PrepareTransArticle(TransPublication TransPub, Happy.MI.Replication.Article Article)
                {
                    TransArticle TransArticle = new TransArticle();
                    TransArticle.ConnectionContext = TransPub.ConnectionContext;
                    TransArticle.Name = Article.Name;
                    TransArticle.DatabaseName = TransPub.DatabaseName;
                    TransArticle.SourceObjectName = Article.Name;
                    TransArticle.SourceObjectOwner = "dbo";
                    TransArticle.PublicationName = TransPub.Name;
                    //article.Type = ArticleOptions.LogBased;
                    //article.FilterClause = "DiscontinuedDate IS NULL";
        
                    // Ensure that we create the schema owner at the Subscriber.
        
        
                    if (TransArticle.IsExistingObject)
                    {
                        //do somethinbg??
                    }
                    else
                    {
        
                        TransArticle.SchemaOption |= CreationScriptOptions.Schema;
                        TransArticle.SchemaOption |= CreationScriptOptions.AttemptToDropNonArticleDependencies;
        
                        if (!Article.ObjectType.HasValue)
                        {
                            throw new Exception(string.Format("unknown schema object type for trans article {0}", Article.Name));
                        }
                        if (Article.ObjectType.Value== DataAccessADO.ObjectType.USER_TABLE)
                        {
                            TransArticle.SchemaOption |= CreationScriptOptions.ClusteredIndexes;
                            TransArticle.SchemaOption |= CreationScriptOptions.DriChecks;
                            TransArticle.SchemaOption |= CreationScriptOptions.DriDefaults;
                            TransArticle.SchemaOption |= CreationScriptOptions.DriPrimaryKey;
                            TransArticle.SchemaOption |= CreationScriptOptions.DriUniqueKeys;
                            //TransArticle.SchemaOption |= CreationScriptOptions.ExtendedProperties;
                            //TransArticle.SchemaOption |= CreationScriptOptions.NonClusteredIndexes;
                            TransArticle.Type = ArticleOptions.LogBased;
                            TransArticle.AddReplicatedColumns(Article.IncludedColumns.ToArray());
                        }
                        else if (Article.ObjectType.Value == DataAccessADO.ObjectType.VIEW)
                        {
                            TransArticle.Type= ArticleOptions.ViewSchemaOnly;
                        }
                        else if (Article.ObjectType.Value == DataAccessADO.ObjectType.SQL_SCALAR_FUNCTION)
                        {
                            TransArticle.Type = ArticleOptions.FunctionSchemaOnly;
                        }
                        else if (Article.ObjectType.Value == DataAccessADO.ObjectType.SQL_STORED_PROCEDURE)
                        {
                            TransArticle.Type = ArticleOptions.ProcSchemaOnly;
                        }
                        else
                        {
                            throw new Exception(string.Format("unsupported schema object type {0}", Article.ObjectType.Value));
                        }
                        // Create the article.
                        TransArticle.Create();
                    }
                    return TransArticle;
                }
                public static TransSubscription PrepareSubscription(TransPublication TransPub, MyServer Src, MyServer Dest, MyServer Dist)
                {
                    // Define the push subscription.
                    //TransPullSubscription subscription = new TransPullSubscription();
                    //subscription.ConnectionContext =  Dest.ServerConnection;
                    //subscription.PublisherName = Src.ServerName;
                    //subscription.PublicationName = TransPub.Name;
                    //subscription.PublicationDBName = Src.Database;
                    //subscription.DatabaseName = Dest.Database;
                    TransSubscription subscription = new TransSubscription();
                    subscription.ConnectionContext = TransPub.ConnectionContext;
                    subscription.PublicationName = TransPub.Name;
                    subscription.DatabaseName =  TransPub.DatabaseName;
                    subscription.SubscriptionDBName = Dest.Database;
                    subscription.SubscriberName = Dest.ServerName;
                    subscription.LoadProperties();
        
                    //subscription.Remove();
        
                    // Specify the Windows login credentials for the Distribution Agent job.
                    subscription.SynchronizationAgentProcessSecurity.Login = Dist.WinUId;
                    subscription.SynchronizationAgentProcessSecurity.Password = Dist.WinPwd;
        
        
                    if(!subscription.IsExistingObject){
        
        
                        // Create the push subscription.
        
        
                        // By default, subscriptions to transactional publications are synchronized 
                        // continuously, but in this case we only want to synchronize on demand.
                        subscription.AgentSchedule.FrequencyType = ScheduleFrequencyType.Continuously;
        
                        subscription.Create();
        
                        PrepareSnapshot(TransPub, Src, Dist);
                    }
                    return subscription;
                }
                public static void PrepareSnapshot(TransPublication TPub, MyServer Src, MyServer Dist)
                {
                    SnapshotGenerationAgent agent = new SnapshotGenerationAgent();
                    agent.Distributor = Dist.ServerName;
                    agent.DistributorSecurityMode = SecurityMode.Standard;
                    agent.DistributorLogin = Dist.SQLUId;
                    agent.DistributorPassword = Dist.WinPwd;
        
                    agent.Publisher = TPub.SqlServerName;
                    agent.PublisherSecurityMode = SecurityMode.Standard;
                    agent.PublisherLogin = Src.SQLUId;
                    agent.PublisherPassword = Src.SQLPwd;
        
                    agent.Publication = TPub.Name;
                    agent.PublisherDatabase = TPub.DatabaseName;
                    agent.ReplicationType = ReplicationType.Transactional;
        
                    // Start the agent synchronously.
                    agent.GenerateSnapshot();
        
                }
                public static void ApplySubscription(Happy.MI.Replication.Subscription _subscription)
                {
        
                    Happy.MI.Replication.Publication p = _subscription.Publication;
                    RMOHelper.PreparePublicationDb(_subscription.Publication.Src, _subscription.Publication.Dist);
                    TransPublication TransPub = RMOHelper.PrepareTransPublication(p.Src, p.Dist, p.PublicationName);
                    foreach (Happy.MI.Replication.Article a in p.Articles)
                    {
                        a.LoadProperties();
                        TransArticle ta = RMOHelper.PrepareTransArticle(TransPub, a);
                        ta.ConnectionContext.Disconnect();
        
                    }
                    TransSubscription TransSub = RMOHelper.PrepareSubscription(TransPub, p.Src, _subscription.Dest, p.Dist);
                    if (TransSub.LoadProperties() && TransSub.AgentJobId == null)
                    {
                        // Start the Distribution Agent asynchronously.
                        TransSub.SynchronizeWithJob();
        
                    }
                    TransSub.ConnectionContext.Disconnect();
        
                    //foreach (Happy.MI.Replication.Subscription s in p.Subscriptions)
                    //{
                    //    TransSubscription TransSub = RMOHelper.PrepareSubscription(TransPub, p.Src, s.Dest, p.Dist);
        
                    //    if (TransSub.LoadProperties() && TransSub.AgentJobId == null)
                    //    {
                    //        // Start the Distribution Agent asynchronously.
                    //        TransSub.SynchronizeWithJob();
        
                    //    }
                    //    TransSub.ConnectionContext.Disconnect();
                    //}
                    //TransPub.ConnectionContext.Disconnect();
        
                }
                public static void Create(Happy.MI.Replication.Publication p)
                {
                    RMOHelper.PreparePublicationDb(p.Src, p.Dist);
        
                    TransPublication TransPub = RMOHelper.PrepareTransPublication(p.Src, p.Dist, p.PublicationName);
                    foreach (Happy.MI.Replication.Article a in p.Articles)
                    {
                        a.LoadProperties();
                        RMOHelper.PrepareTransArticle(TransPub, a);
                    }
                    foreach (Happy.MI.Replication.Subscription s in p.Subscriptions)
                    {
                        TransSubscription TransSub = RMOHelper.PrepareSubscription(TransPub, p.Src, s.Dest, p.Dist);
        
                        if (TransSub.LoadProperties() && TransSub.AgentJobId == null)
                        {
                            // Start the Distribution Agent asynchronously.
                            TransSub.SynchronizeWithJob();
        
                        }
                    }
                }
                private static  void DeleteJobAgentSchedule(string s)
                {
                    //    Private Function DeleteSchedule(ByVal scheduleID As Integer) As Boolean
                    //    Dim result As Boolean
        
                    //    If (scheduleID > 0) Then
                    //        Dim msdbConnectionString As String = Me.PublicationConnectionString.Replace(String.Format("Initial Catalog={0};", Me.PublicationDbName), "Initial Catalog=msdb;")
        
                    //        Dim db As New SQLDataAccessHelper.DBObject(msdbConnectionString)
        
                    //        '-- Delete Job Schedule
                    //        Dim parameters As New List(Of System.Data.SqlClient.SqlParameter)
        
                    //        parameters.Add(New System.Data.SqlClient.SqlParameter("@schedule_id", SqlDbType.Int))
                    //        parameters.Add(New System.Data.SqlClient.SqlParameter("@force_delete", SqlDbType.Bit))
        
                    //        parameters(0).Value = scheduleID
                    //        parameters(1).Value = True
        
                    //        Dim rowsAffected As Integer
        
                    //        result = (db.RunNonQueryProcedure("sp_delete_schedule", parameters, rowsAffected) = 0)
        
                    //        db.Connection.Close()
                    //        db.Connection.Dispose()
                    //    Else
                    //        Throw New ArgumentException("DeleteSchedule(): ScheduleID must be greater than 0")
                    //    End If
        
                    //    Return result
                    //End Function
                }
        
                public static int PublicationEstimatedTimeBehind(Happy.MI.Replication.Subscription s)
                {
        
                    PublicationMonitor mon = new PublicationMonitor();
                    mon.DistributionDBName = s.Publication.Dist.Database;
                    mon.PublisherName = s.Publication.Src.ServerName;
                    mon.PublicationDBName = s.Publication.Src.Database;
                    mon.Name = s.Publication.PublicationName;
                    mon.ConnectionContext = s.Publication.Src.ServerConnection;
                    DataSet ds1 = mon.EnumSubscriptions2(SubscriptionResultOption.AllSubscriptions);
                    ds1.WriteXml(@"c:\desktop\ds1.xml");
        
                    //ds.Tables[0].ToString();
        
        
                    if (mon.LoadProperties())
                    {
                        PendingCommandInfo pci = mon.TransPendingCommandInfo(s.Dest.ServerName, s.Dest.Database, SubscriptionOption.Push);
                        return pci.EstimatedTimeBehind;
                    }
                    else
                    {
                        throw new Exception(string.Format("Unable to load properties for subscription [{0}][{1}]",s.Dest.ServerName, s.Publication.PublicationName));
                    }
                }
        
                public static int TraceTokenPost(Happy.MI.Replication.Subscription s)
                {
                    TransPublication TransPub = new TransPublication();
                    TransPub.ConnectionContext = s.Publication.Src.ServerConnection;
                    TransPub.Name = s.Publication.PublicationName;
                    TransPub.DatabaseName = s.Publication.Src.Database;
                    if (TransPub.LoadProperties())
                    {
                        return TransPub.PostTracerToken();
                    }
                    return 0;
                }
                public static bool TraceTokenReceive(Happy.MI.Replication.Subscription s, int TokenId){
                    PublicationMonitor mon = new PublicationMonitor();
                    mon.DistributionDBName = s.Publication.Dist.Database;
                    mon.PublisherName = s.Publication.Src.ServerName;
                    mon.PublicationDBName = s.Publication.Src.Database;
                    mon.Name = s.Publication.PublicationName;
                    mon.ConnectionContext = s.Publication.Src.ServerConnection;
                    if (mon.LoadProperties())
                    {
                        DataSet ds= mon.EnumTracerTokenHistory(TokenId);
                        int latency;
                        string str = ds.Tables[0].Rows[0]["overall_latency"].ToString();
                        bool res = int.TryParse(str, out latency);
        
                        return res;
                    }
                    else
                    {
                        throw new Exception(string.Format("Unable to load properties for subscription [{0}][{1}]", s.Dest.ServerName, s.Publication.PublicationName));
                    }
                }
        
                public static void Cmd(string cnct)
                {
                    string script = System.IO.File.ReadAllText(@"C:\tfs\CA\Dev\MI\Happy.MI\PostReplicationScripts\GC1.txt");
                    SqlConnection connection = new SqlConnection(cnct+";Connection Timeout=5");
                    Server server = new Server(new ServerConnection(connection));
                    //server.ConnectionContext.InfoMessage += new System.Data.SqlClient.SqlInfoMessageEventHandler(ConnectionContext_InfoMessage);
                    server.ConnectionContext.ExecuteNonQuery(script);
                    server.ConnectionContext.Disconnect();
                }
            }
        

        【讨论】:

          【解决方案5】:

          我想说的是,在决定解决问题的任何方法之前,先将问题分成更小的部分。

          当您真正需要时,SQL SERVER 的数据库镜像、复制和其他高可用性或灾难恢复功能就在那里。 但这些功能也不能提供 100% 的实时同步。正如其他经验丰富的 DBA 已经提到的那样,您必须计划“计划停机”和/或“几分钟延迟”,并且如果您选择这些选项,还需要相应地设置客户的期望。

          再一次,这些功能可能会被证明是转移了问题,但实际上并没有解决它,除非我们首先查看根本原因本身。 下面的建议看起来像通用陈述,但这个问题也是如此。您的问题范围太广,无法涵盖,首先需要发现很多领域才能有人回答。

          现在我想指出的是,针对手头的问题提出一些小问题。

          你提到:

          “正在运行的某些查询会导致他们自己和其他用户出现锁定和性能问题”

              Are these queries blocking other reads and/or write?  If both lets handle separately.
                  Ideally any read should not be blocked by other read/write. What type of ISOLATIONLVEL you have in DB?
                      If “READ COMMITED”, or any other more strict level than think about SNAPSHOT ISOLATION. 
                  Does the queries have lot of table and/or index hints in them ?
                      Try not to optimize queries by hints as first option instead treat as last option.
                  if issue is blocking of write/write then couple of point you can consider.
                      Does write queries written properly to acquires appropriate locks.  (due to the table hints if any)
                      Have you look at the server configurations MAX memory/Thread/DOP/AUTO Statistics Async?
                      Can the large insert/update be queued from APP tier ?
                      Can the large insert/update be chunked in smaller batch ?
                      Can the large insert/update be executed as Asynchronous operation ?
                      Can you take advantage of Partitioning in database? 
          

          以上所有的“Can Questions”都需要更多的输入,具体取决于您的第一个答案是什么。 如果数据库或代码最初不是为适应此类功能而设计的,那么这是开始的好时机 考虑一下。

              In terms of performance are you seeing write is getting slower or read ? 
                  What is causing slowness?
                      CPU, Memory, DISK IO, DB properties, Too many Object Recompilations ?
                  Have you extracted the Execution plan for identified main queries ?
                      If queries are very long and complex instead look for how can we simplify/optimize the logic ? 
                  Does Tables are overly  indexed ?
                      Write operation can suffer severely if tables are over optimized for read by adding lot of indexes.
                      See the index fragmentation and statistics. You must have the db maintenance plan in place. How soon the indexes become fragmented again.
                  Is there lot of aggregations and calculation in the query that runs frequently?
                      If query has lot of aggregations/UDF’s/Views that runs frequently we can also find out if we can store semi aggregated data separately.
                  Does the reporting query retrieves lot of data?
                      If the queries serving results to report they may end up being retuning thousands of rows. Think about what does user do with this much of results on UI?
                      Does it really necessary ? if not can we limit the result set to return certain number of row based on user settings.
                      If yes, can we implement PAGING for this queries. (that can be controlled by setting as well)
                      If this much of data is feeding another sub-system (SSRS) then anyways any reporting tool will have some latency depending on how much data we are dumping in front of user.
          

          “我们尝试应用尽可能多的索引并将所有查询优化到极限,但我们有一个应用程序必须适合许多不同的客户类型,因此很难创建一种解决方案适合所有。 我们没有资源为每个客户应用客户特定的索引/性能。”

              Can we find out customizations and think about how can we implement separately?  
              This is a huge piece by itself. But I am telling from my own experience that it took us almost an year 
              to transform our DB design that can accommodate over 300 clients w/o worrying how one customization will 
              affect  other Client custom functionality and/or our core product features. 
              If you can manage to get a right plan laid out first, sure you can get resources to accomplish that.
          

          “我们知道导致问题的主要查询是为驱动报告和 kpi 而生成的查询。”

              How many tables this queries covers ?
              If numbers less 30% of DB, then instead of whole DB, 
              we should think around these tables and queries only.
          
          
              If you find any/some of above points you haven’t visited yet do so. 
              You will find very simple things that can save you lot.
              It is better to look at root of the problem instead covering/overcoming it temporarily by using alternatives.
              Many of the DBAs and Developer on this community will be happy to assist you for “END-TO-END Resolution” or “help as needed” .
          

          【讨论】:

            【解决方案6】:

            您可以创建快照复制,将一个数据库用于生产,另一个用于报告。将用于报告的索引移动到报告数据库中,并将应用程序所需的其他索引保留在应用程序使用的数据库中。

            【讨论】:

              猜你喜欢
              • 2017-11-03
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2012-12-07
              • 2015-06-23
              相关资源
              最近更新 更多