# 1. Cost Models ## Cost-Based Query Planning - 在当前数据库状态下,估算 执行特定 query plan 的成本 - 估算只在内部有意义(比如不同 DB 的 cost 数值不具备可比较性) ## Cost Model Components - **Choice #1: Physical Costs** - 预测 CPU cycles,IO,cache miss,RAM 占用,pre-fetching 等等 - 严重依赖硬件 - **Choice #2: Logical Costs** - 估算每个 operator 结果集大小(比如 scan 多少记录) - 与 operator 的算法无关 - 估算结果集大小不容易(算子间结果集有依赖) - **Choice #3: Algorithmic Costs** - operator 算法实现的复杂度 ## Disk-Based DBMS Cost Model - 磁盘的访问次数始终主导着查询的执行时间 - CPU 开销可以忽略 - 必须考虑顺序 vs. 随机 IO - 如果 DBMS 可以访问控制 buffer management,则更容易建模 - 我们可以知道淘汰策略、pinning,并假设独占磁盘访问 ## Postgres Cost Model - 使用 magic constant factor 作为权重来 组合 CPU 和 IO 的 costs - 默认配置: - 在内存中处理一个 tuple 比从磁盘读取一个 tuple 快 400 倍 - 顺序IO 是 随机IO 的 4 倍 - [Doc: Planner Cost Constants](https://www.postgresql.org/docs/current/runtime-config-query.html#RUNTIME-CONFIG-QUERY-CONSTANTS) ## IBM DB2 Cost Model 包含如下多种 cost: - Database characteristics in system catalogs(schema、indexes等) - Hardware environment(microbenchmarks确定权重) - Storage device characteristics(mircobenchmarks) - Communications bandwidth(仅限分布式) - Memory resources(buffer pools、sort heaps) - Concurrency Envrionment - 平均用户数 - Isolation level / blocking - Number of available locks ## In-Memory DBMS Cost Model - 不涉及 IO costs,只需要考虑 CPU 和 内存访问的 cost - memory cost 很难控制因为 DBMS 无法干涉 cache management - 无法感知 replacement、pinning、shared caches、NUMA - CPU cost 可以根据每个 operator 处理的 tuples 数量相对合理地估计 ## Smallbase Cost Model - Two-phase model that automatically generates hardware costs from a logical model. - **Phase #1 : Identify Execution Primitives** - DBMS 执行查询时的一系列操作 - 例如:谓词求值、index probe、sorting - **Phase #2: Microbenchmark** - 启动后,profile 每个操作来计算它的 CPU / memory costs - 这些测量值用于根据 table size 计算 operator cost 的公式中。 ----- # 2. Cost Estimation ## Selectivity - 定义:一个 operator 的 selectivity 指的是一个 predicate 访问数据的百分比 - 即 tuple 满足 predicate 的概率 - DBMS 如何预估 selectivities: - Domain constraints - Precomputed Statistics(Zone Maps) - Histograms / Approximations - Sampling ## Observation - 每个 operator 处理的 tuples 数量取决于三个因素: - 每个 table 的 access methods(sequential scan、index scan) - The distribution of values in the database's attributes(estimation stuff) - query 中使用的 predicates - 简单的查询很容易估算,很多复杂的查询却很难 ## Approximations - 维护 database 的精确统计很昂贵、且很慢 - 使用 **sketches** 这样的近似数据结构来生成误差有界(error-bounded)的预估 - Count Distinct - Quantiles - Frequent Items - Tuple Sketch - [Yahoo! Sketching Library](https://github.com/apache/datasketches-website/) ## Sampling - Execute a predicate on a random sample of the target data set. - 采样的 tuple 个数取决于 table 的大小 - **Approach #1 : Maintain Read-Only Copy** - offline 方式 - 定期 refresh 来保证准确度 - **Approach #2 : Sample Real Tables** - online 方式 - 实际去执行一部分查询 - Use READ UNCOMMITTED isolation,May read multiple versions of same logical tuple. ## Result Cardinality - 每个 operator 输出的 tuples 数量等于:它的 selectivity 乘以 输入的 tuples 数量 - **Assumption #1: Uniform Data** - values 的分布是均匀的(除了 heavy hitters) - **Assumption #2: Independent Predicates** - predicates 之间是相互独立的 - **Assumption #3: Inclusion Principle** - join keys 的 domain 是重叠的:inner relation 中的每个 key 也存在于 outer table 中。 ### Correlated Attributes - [[15-optimization-2#^1b06de]] - [IS QUERY OPTIMIZATION A “SOLVED” PROBLEM?](http://wp.sigmod.org/?p=1075) ### Column Group Statistics - DBMS 可以跟踪一组 attributes 的统计信息,而非仅仅把它们看作是独立变量 - 只有商业系统支持(Oracle、DB2) - 需要 DBA 手动指定 ## Estimation Problem ```SQL SELECT A.id FROM A,B,C WHERE A.id = B.id AND A.id = C.id AND B.id > 100 ``` ![200](https://img.jonahgao.com/oss/note/2025p1/16721_2023_cost_estimate.png) - 计算 base tables 的 cardinality - $A \rightarrow |A|$ - $B.id > 100 \rightarrow |B| \times sel(B.id>100)$ - $C \rightarrow |C|$ - 计算 join 结果的 cardinality - $A \bowtie B = (|A| \times |B| ) / max(sel(A.id=B.id), sel(B.id>100))$ - $(A \bowtie B) \bowtie C = (|A| \times |B| \times |C|) / max(sel(A.id=B.id), sel(B.id>100), sel(A.id=c.id))$ - join涉及的 tables 越多,预估就越可能出错 ## Estimator Quality - [How Good Are Query Optimizers, Really?](https://15721.courses.cs.cmu.edu/spring2017/papers/16-costmodels/p204-leis.pdf) - 随着 joins 数量的增加,测试 DBMS 估算 cardinality 的正确性。 - 使用了 100k queries 比较了 5 款 DBMSs - postgres: 基于估算的 cardinality 分配 hashtable for hash join -------- # Lessons From The Germans - 查询优化比一个快速的 engine 更重要 - cost-based join ordering 是必要的 - Cardinality estimates 通常是错误的 - 尽量使用不依赖于估算的 operators - Hash joins + seq scans 是一个健壮的执行模型 - The more indexes that are available, the more brittle the plans become (but also faster on average) - 在精确的模型上工作是浪费时间 - 最好是改进估算 cardinality --------- # IBM DB2 - Learning Optimizer - [LEO - DB2's LEarning Optimizer](https://dl.acm.org/doi/10.5555/645927.672349) - 当 DBMS 执行查询扫描 table 时,更新 table statistics - 检查 optimizer 的估算是否匹配遇到的真实数据,然后逐步更新它们。 ---- # Parting Thoughts - 对于内存 DBMSs,使用处理的 tuples 数量来作为 cost model 是合理的 - 但是计算也不可忽略 - sampling + sketches 是实现准确估算的方法。