当数据爆炸遇上SQL Server:优化策略全链路解析
<p>在数据驱动的时代,海量数据冲击下的数据库性能成为系统成败的关键。SQL Server作为企业级数据库的常青树,面对单表亿级数据量时,我们往往陷入分库分表与否的抉择困境。</p><p class="marklang-paragraph">我们站在SQL Server视角,浅浅的解析一下从索引优化到架构升级的全链路优化策略,看我们能不能在数据洪流中稳操胜券。</p>
<hr>
<h2>一、索引优化:让查询飞起来的核心秘诀</h2>
<p class="marklang-paragraph"><span style="font-size: 15px"><strong>执行计划分析</strong></span><br>SQL Server Management Studio(SSMS)内置的「显示预估执行计划」是性能调优的瑞士军刀。通过可视化界面查看逻辑读取次数、索引缺失警告等关键指标:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">SET</span> SHOWPLAN_XML <span style="color: rgba(0, 0, 255, 1)">ON</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">GO</span>
<span style="color: rgba(0, 0, 255, 1)">SELECT</span> <span style="color: rgba(128, 128, 128, 1)">*</span> <span style="color: rgba(0, 0, 255, 1)">FROM</span> Orders <span style="color: rgba(0, 0, 255, 1)">WHERE</span> CustomerID <span style="color: rgba(128, 128, 128, 1)">=</span> <span style="color: rgba(255, 0, 0, 1)">'</span><span style="color: rgba(255, 0, 0, 1)">VINET</span><span style="color: rgba(255, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">GO</span></pre>
</div>
<p class="marklang-paragraph"><span style="font-size: 15px"><strong>复合索引黄金法则</strong></span><br>采用「相等条件在前,范围查询在后」的索引构建原则。比如针对<code>WHERE Region='华东' AND CreateTime>'2023-01-01'</code>的查询,应建立<code>(Region, CreateTime)</code>的联合索引。</p>
<p class="marklang-paragraph">索引维护自动化<br>通过Ola Hallengren维护脚本实现索引碎片重组:</p>
<div class="code-header">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">EXECUTE</span><span style="color: rgba(0, 0, 0, 1)"> dbo.IndexOptimize
</span><span style="color: rgba(0, 128, 0, 1)">@Databases</span> <span style="color: rgba(128, 128, 128, 1)">=</span> <span style="color: rgba(255, 0, 0, 1)">'</span><span style="color: rgba(255, 0, 0, 1)">USER_DATABASES</span><span style="color: rgba(255, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(0, 128, 0, 1)">@FragmentationLow</span> <span style="color: rgba(128, 128, 128, 1)">=</span> <span style="color: rgba(0, 0, 255, 1)">NULL</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(0, 128, 0, 1)">@FragmentationMedium</span> <span style="color: rgba(128, 128, 128, 1)">=</span> <span style="color: rgba(255, 0, 0, 1)">'</span><span style="color: rgba(255, 0, 0, 1)">INDEX_REORGANIZE</span><span style="color: rgba(255, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(0, 128, 0, 1)">@FragmentationHigh</span> <span style="color: rgba(128, 128, 128, 1)">=</span> <span style="color: rgba(255, 0, 0, 1)">'</span><span style="color: rgba(255, 0, 0, 1)">INDEX_REBUILD</span><span style="color: rgba(255, 0, 0, 1)">'</span>;</pre>
</div>
</div>
<hr>
<h2>二、冷热数据分层:构建数据生命周期管理体系</h2>
<p class="marklang-paragraph">表分区方案<br>通过分区函数实现自动归档:</p>
<div class="code-header">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)">--</span><span style="color: rgba(0, 128, 128, 1)"> 创建分区函数</span>
<span style="color: rgba(0, 0, 255, 1)">CREATE</span> PARTITION <span style="color: rgba(0, 0, 255, 1)">FUNCTION</span> OrderDatePF (<span style="color: rgba(0, 0, 255, 1)">datetime</span><span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">AS</span> RANGE <span style="color: rgba(128, 128, 128, 1)">RIGHT</span> <span style="color: rgba(0, 0, 255, 1)">FOR</span> <span style="color: rgba(0, 0, 255, 1)">VALUES</span> (<span style="color: rgba(255, 0, 0, 1)">'</span><span style="color: rgba(255, 0, 0, 1)">2023-01-01</span><span style="color: rgba(255, 0, 0, 1)">'</span>, <span style="color: rgba(255, 0, 0, 1)">'</span><span style="color: rgba(255, 0, 0, 1)">2024-01-01</span><span style="color: rgba(255, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)">--</span><span style="color: rgba(0, 128, 128, 1)"> 创建分区方案</span>
<span style="color: rgba(0, 0, 255, 1)">CREATE</span><span style="color: rgba(0, 0, 0, 1)"> PARTITION SCHEME OrderDatePS
</span><span style="color: rgba(0, 0, 255, 1)">AS</span> PARTITION OrderDatePF <span style="color: rgba(0, 0, 255, 1)">TO</span><span style="color: rgba(0, 0, 0, 1)"> (fg_2022, fg_2023, fg_2024);
</span><span style="color: rgba(0, 128, 128, 1)">--</span><span style="color: rgba(0, 128, 128, 1)"> 创建分区表</span>
<span style="color: rgba(0, 0, 255, 1)">CREATE</span> <span style="color: rgba(0, 0, 255, 1)">TABLE</span><span style="color: rgba(0, 0, 0, 1)"> Orders (
OrderID </span><span style="color: rgba(0, 0, 255, 1)">INT</span> <span style="color: rgba(0, 0, 255, 1)">PRIMARY</span> <span style="color: rgba(0, 0, 255, 1)">KEY</span><span style="color: rgba(0, 0, 0, 1)">,
OrderDate </span><span style="color: rgba(0, 0, 255, 1)">DATETIME</span><span style="color: rgba(0, 0, 0, 1)">,
CustomerID </span><span style="color: rgba(0, 0, 255, 1)">NVARCHAR</span>(<span style="color: rgba(128, 0, 0, 1); font-weight: bold">5</span><span style="color: rgba(0, 0, 0, 1)">)
) </span><span style="color: rgba(0, 0, 255, 1)">ON</span> OrderDatePS(OrderDate);</pre>
</div>
<p><span style="font-size: 15px"><strong>文件组隔离策略</strong></span></p>
</div>
<p class="marklang-paragraph">将历史分区映射到低速存储:</p>
<div class="code-header">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">ALTER</span> <span style="color: rgba(0, 0, 255, 1)">DATABASE</span><span style="color: rgba(0, 0, 0, 1)"> Sales
</span><span style="color: rgba(0, 0, 255, 1)">ADD</span><span style="color: rgba(0, 0, 0, 1)"> FILEGROUP hist_fg;
</span><span style="color: rgba(0, 0, 255, 1)">ALTER</span> <span style="color: rgba(0, 0, 255, 1)">DATABASE</span><span style="color: rgba(0, 0, 0, 1)"> Sales
</span><span style="color: rgba(0, 0, 255, 1)">ADD</span> <span style="color: rgba(0, 0, 255, 1)">FILE</span> ( NAME <span style="color: rgba(128, 128, 128, 1)">=</span><span style="color: rgba(0, 0, 0, 1)"> hist_data,
FILENAME </span><span style="color: rgba(128, 128, 128, 1)">=</span> <span style="color: rgba(255, 0, 0, 1)">'</span><span style="color: rgba(255, 0, 0, 1)">D:\SlowDisk\Sales_hist.ndf</span><span style="color: rgba(255, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">TO</span> FILEGROUP hist_fg;</pre>
</div>
</div>
<hr>
<h2>三、读写分离:构建高可用舰队</h2>
<p class="marklang-paragraph"><span style="font-size: 15px"><strong>AlwaysOn可用性组</strong></span><br>搭建读写分离集群:</p>
<ol>
<li>配置可用性组监听器</li>
<li>设置只读路由列表</li>
<li>应用程序连接字符串配置:Server=AGListener; Database=Sales;ApplicationIntent=ReadOnly;</li>
</ol>
<p class="marklang-paragraph"><span style="font-size: 15px"><strong>扩展事件监控延迟</strong></span><br>实时跟踪数据同步状态:</p>
<div class="code-header">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">CREATE</span> EVENT SESSION <span style="color: rgba(255, 0, 0, 1)">[</span><span style="color: rgba(255, 0, 0, 1)">HADR_Latency</span><span style="color: rgba(255, 0, 0, 1)">]</span> <span style="color: rgba(0, 0, 255, 1)">ON</span><span style="color: rgba(0, 0, 0, 1)"> SERVER
</span><span style="color: rgba(0, 0, 255, 1)">ADD</span><span style="color: rgba(0, 0, 0, 1)"> EVENT sqlserver.hadr_apply_vfs_io_completion
</span><span style="color: rgba(0, 0, 255, 1)">ADD</span> TARGET package0.event_file(...)</pre>
</div>
</div>
<hr>
<h2>四、存储引擎黑科技:突破性能天花板</h2>
<p class="marklang-paragraph"><span style="font-size: 15px"><strong>列存储索引</strong></span><br>对分析型查询实现百倍加速:</p>
<div class="code-header">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">CREATE</span> COLUMNSTORE <span style="color: rgba(0, 0, 255, 1)">INDEX</span><span style="color: rgba(0, 0, 0, 1)"> CCSI_Orders
</span><span style="color: rgba(0, 0, 255, 1)">ON</span> Orders (OrderID, ProductID, Quantity);</pre>
</div>
</div>
<p class="marklang-paragraph"><span style="font-size: 15px"><strong>内存优化表</strong></span><br>针对高并发OLTP场景:</p>
<div class="code-header">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">CREATE</span> <span style="color: rgba(0, 0, 255, 1)">TABLE</span><span style="color: rgba(0, 0, 0, 1)"> SessionCache (
SessionID </span><span style="color: rgba(0, 0, 255, 1)">NVARCHAR</span>(<span style="color: rgba(128, 0, 0, 1); font-weight: bold">128</span>) <span style="color: rgba(0, 0, 255, 1)">PRIMARY</span> <span style="color: rgba(0, 0, 255, 1)">KEY</span> <span style="color: rgba(0, 0, 255, 1)">NONCLUSTERED</span><span style="color: rgba(0, 0, 0, 1)">,
Data </span><span style="color: rgba(0, 0, 255, 1)">VARBINARY</span>(<span style="color: rgba(255, 0, 255, 1)">MAX</span><span style="color: rgba(0, 0, 0, 1)">)
) </span><span style="color: rgba(0, 0, 255, 1)">WITH</span> (MEMORY_OPTIMIZED <span style="color: rgba(128, 128, 128, 1)">=</span> <span style="color: rgba(0, 0, 255, 1)">ON</span>);</pre>
</div>
</div>
<hr>
<h2>五、智能扩展:云原生时代的弹性方案</h2>
<p class="marklang-paragraph"><span style="font-size: 15px"><strong>弹性池(Azure SQL Database)</strong></span><br>实现多数据库资源共享:</p>
<div class="code-header">
<div class="cnblogs_code">
<pre>New<span style="color: rgba(128, 128, 128, 1)">-</span>AzSqlElasticPool <span style="color: rgba(128, 128, 128, 1)">-</span>ResourceGroupName "Group01" <span style="color: rgba(128, 128, 128, 1)">-</span><span style="color: rgba(0, 0, 0, 1)">ServerName "Server01"
</span><span style="color: rgba(128, 128, 128, 1)">-</span>ElasticPoolName "ElasticPool01" <span style="color: rgba(128, 128, 128, 1)">-</span>Dtu <span style="color: rgba(128, 0, 0, 1); font-weight: bold">200</span> <span style="color: rgba(128, 128, 128, 1)">-</span>DatabaseDtuMin <span style="color: rgba(128, 0, 0, 1); font-weight: bold">10</span> <span style="color: rgba(128, 128, 128, 1)">-</span>DatabaseDtuMax <span style="color: rgba(128, 0, 0, 1); font-weight: bold">100</span></pre>
</div>
</div>
<p class="marklang-paragraph"><span style="font-size: 15px"><strong>PolyBase联邦查询</strong></span><br>打通异构数据源:</p>
<div class="code-header">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">CREATE</span> EXTERNAL DATA SOURCE MongoDB <span style="color: rgba(0, 0, 255, 1)">WITH</span><span style="color: rgba(0, 0, 0, 1)"> (
LOCATION </span><span style="color: rgba(128, 128, 128, 1)">=</span> <span style="color: rgba(255, 0, 0, 1)">'</span><span style="color: rgba(255, 0, 0, 1)">mongodb://mongoserver:27017</span><span style="color: rgba(255, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">,
CREDENTIAL </span><span style="color: rgba(128, 128, 128, 1)">=</span><span style="color: rgba(0, 0, 0, 1)"> MongoCred
);
</span><span style="color: rgba(0, 0, 255, 1)">SELECT</span> <span style="color: rgba(128, 128, 128, 1)">*</span> <span style="color: rgba(0, 0, 255, 1)">FROM</span> <span style="color: rgba(255, 0, 255, 1)">OpenQuery</span>(MongoDB, <span style="color: rgba(255, 0, 0, 1)">'</span><span style="color: rgba(255, 0, 0, 1)">SalesDB.Orders.find()</span><span style="color: rgba(255, 0, 0, 1)">'</span>);</pre>
</div>
</div>
<hr>
<h2>六、终极武器:分库分表的SQL Server实践</h2>
<p class="marklang-paragraph"><span style="font-size: 16px"><strong>分片映射管理</strong></span><br>使用弹性数据库客户端库:</p>
<div class="code-header">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 创建分片映射管理器</span>
<span style="color: rgba(0, 0, 255, 1)">var</span> shardMapManager =<span style="color: rgba(0, 0, 0, 1)"> ShardMapManagerFactory.GetSqlShardMapManager(
connectionString, ShardMapManagerLoadPolicy.Lazy);
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 添加分片</span>
<span style="color: rgba(0, 0, 255, 1)">var</span> shard = shardMapManager.CreateListShardMap<<span style="color: rgba(0, 0, 255, 1)">int</span>>(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">CustomerShard</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)
.CreateShard(</span><span style="color: rgba(0, 0, 255, 1)">new</span> ShardLocation(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">ServerA</span><span style="color: rgba(128, 0, 0, 1)">"</span>, <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">ShardDB1</span><span style="color: rgba(128, 0, 0, 1)">"</span>));</pre>
</div>
</div>
<p class="marklang-paragraph"><span style="font-size: 15px"><strong>跨分片查询</strong></span><br>通过弹性查询实现分布式join:</p>
<div class="code-header">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">SELECT</span><span style="color: rgba(0, 0, 0, 1)"> o.OrderID, c.CompanyName
</span><span style="color: rgba(0, 0, 255, 1)">FROM</span><span style="color: rgba(0, 0, 0, 1)"> Sharded.Orders o
</span><span style="color: rgba(0, 0, 255, 1)">INNER</span> <span style="color: rgba(128, 128, 128, 1)">JOIN</span> Sharded.Customers c <span style="color: rgba(0, 0, 255, 1)">ON</span> o.CustomerID <span style="color: rgba(128, 128, 128, 1)">=</span> c.CustomerID;</pre>
</div>
</div>
<hr>
<h2>优化心法金字塔</h2>
<ol>
<li>
<p class="marklang-paragraph">基础层(Cost 0-10万)</p>
<ul>
<li>索引优化</li>
<li>查询重写</li>
<li>统计信息更新</li>
</ul>
</li>
<li>
<p class="marklang-paragraph">进阶层(Cost 10-50万)</p>
<ul>
<li>内存优化表</li>
<li>列存储索引</li>
<li>智能分区</li>
</ul>
</li>
<li>
<p class="marklang-paragraph">架构层(Cost 50万+)</p>
<ul>
<li>AlwaysOn集群</li>
<li>弹性分片</li>
<li>混合云部署</li>
</ul>
</li>
</ol><hr>
<p class="marklang-paragraph">当数据洪流来袭时,SQL Server提供的不是单一解决方案,而是从存储引擎到云服务的全景式武器库。</p>
<p class="marklang-paragraph">通过索引优化夯实地基,借助分区和AlwaysOn构建防御工事,运用内存OLTP和列存储实现降维打击,最终通过弹性扩展制胜未来。</p>
<p class="marklang-paragraph">记住:真正的架构优化,永远是业务需求与技术特性的交响乐。</p>
<p class="marklang-paragraph"> </p>
<p class="marklang-paragraph"> </p>
<p><span style="font-size: 16px"><strong>欢迎关注订阅微信公众号【熊泽有话说】,更多好玩易学知识等你来取</strong></span><br><span style="font-size: 16px"><strong>作者:熊泽-学习中的苦与乐<br><strong>公众号:熊泽有话说</strong></strong></span><br><span style="font-size: 16px"><strong>QQ群:<span style="color: rgba(255, 0, 0, 1)">711838388</span></strong></span><br><span style="font-size: 16px"><strong>出处:https://www.cnblogs.com/xiongze520/p/15821599.html</strong></span><br><span style="font-size: 16px"><strong>您可以随意转载、摘录,但请在文章内注明作者和原文链接。 </strong> </span></p>
<p> </p>
<p><img src="https://img2020.cnblogs.com/blog/1093832/202108/1093832-20210823163948985-309495620.png" alt="" width="514" height="239" loading="lazy"></p>
<p> </p>
</div>
<div id="MySignature" role="contentinfo">
<p>本文来自博客园,作者:熊泽-学习中的苦与乐,转载请注明原文链接:https://www.cnblogs.com/xiongze520/p/18855686</p><br><br>
来源:https://www.cnblogs.com/xiongze520/p/18855686
頁:
[1]