<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Selectivity on TouchingFish.top</title><link>https://touchingfish.top/tags/selectivity/</link><description>Recent content in Selectivity on TouchingFish.top</description><generator>Hugo</generator><language>zh-cn</language><lastBuildDate>Wed, 17 Sep 2025 00:00:00 +0000</lastBuildDate><atom:link href="https://touchingfish.top/tags/selectivity/index.xml" rel="self" type="application/rss+xml"/><item><title>selectivity 是一道概率题</title><link>https://touchingfish.top/2025/pgsql-selectivity/</link><pubDate>Wed, 17 Sep 2025 00:00:00 +0000</pubDate><guid>https://touchingfish.top/2025/pgsql-selectivity/</guid><description>&lt;p&gt;EXPLAIN 跑出来的执行计划里，有一列叫 &lt;code&gt;rows&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;刚开始学 PostgreSQL 的时候，我以为这列是随便填的——反正真正执行的时候数据库会知道实际行数。后来发现不是。优化器在选执行计划时根本没看过真实数据，它靠的是 &lt;code&gt;rows&lt;/code&gt; 这一列的估计值。估计错了，索引选错，连接顺序选错，整个查询慢得像在泥里走路。&lt;/p&gt;
&lt;p&gt;那这个数字哪来的？&lt;/p&gt;
&lt;p&gt;答案藏在 &lt;code&gt;pg_stats&lt;/code&gt; 系统视图里，藏在 &lt;code&gt;ANALYZE&lt;/code&gt; 命令收集的统计信息里。但统计信息本身不会直接告诉你&amp;quot;这个 WHERE 条件会命中多少行&amp;quot;——中间还隔着一层叫 &lt;strong&gt;selectivity&lt;/strong&gt; 的东西。&lt;/p&gt;
&lt;p&gt;selectivity 本质上不是数据库魔法，是一个很典型的统计估计问题。&lt;/p&gt;
&lt;p&gt;翻译成统计语言：已知样本统计量，估计总体里满足条件事件的概率。&lt;/p&gt;
$$
\text{selectivity} = P(\text{条件成立})
$$&lt;p&gt;数据库里遇到的 $P(X=x)$、$P(X&gt;a)$、$P(A \cap B)$，都是概率。优化器干的事，就是用 &lt;code&gt;ANALYZE&lt;/code&gt; 采的样本，去猜这些概率。&lt;/p&gt;
&lt;h2 id="均匀分布是最朴素的假设"&gt;均匀分布是最朴素的假设&lt;/h2&gt;
&lt;p&gt;从最简单的情况开始。&lt;/p&gt;
&lt;p&gt;一张 &lt;code&gt;users&lt;/code&gt; 表，有 &lt;code&gt;age&lt;/code&gt; 列，总行数 $N = 1000000$。查询：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#bbc3d4;background-color:#242933;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#d08770;font-weight:bold"&gt;WHERE&lt;/span&gt; age &lt;span style="color:#5e81ac"&gt;=&lt;/span&gt; &lt;span style="color:#b48ead"&gt;20&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;ANALYZE&lt;/code&gt; 之后，&lt;code&gt;pg_stats&lt;/code&gt; 里记着 &lt;code&gt;n_distinct = 100&lt;/code&gt;，意思是估计有 100 个不同年龄。&lt;/p&gt;
&lt;p&gt;PostgreSQL 默认先假设均匀分布。&lt;/p&gt;
$$
P(\text{age} = 20) = \frac{1}{100} = 0.01
$$&lt;p&gt;selectivity = 0.01，预计返回 $1000000 \times 0.01 = 10000$ 行。&lt;/p&gt;</description></item></channel></rss>