## Query optimization ### Query Optimizer - Heart of Query Processor - Tasks: - Translate query into RA (naive query plan, **really?**) - Transform into efficient plan (physical operators, operator sequence + grouping) - Keep semantics - Annotate plan - Give to evaluation engine - Approaches - Bottom-up: Optimize queries, generalize algorithms - Top-down: Algorithms for classes of queries (common) - Optimization (hybrid common) - Heuristics: Works for most queries - Cost-based: Good for specific queries ![](images/queryoptimizer.png) ### Pipelining - Idea: Pipeline results between operators - e.g. pipeline join into selection, on-the-fly selection - Advantage: - No temporary tables - No expensive writing/reading - How does it work? - Tuples passed over operators (operation has buffer) - Execution: - Demand-driven (top-down, pull) - Producer-driven (bottom-up, push) - Demand-driven - Important: Coordinate execution - Use Iterator interface (open, getNext, close) to hide implementation details - Restriction - Works for selection, projection, index nested loop join - Works not for sorting, hash join, merge join - Materialization can be more efficient ### Algebraic query rewriting - Idea: Find best plan before executing query - Rewriting: RA equivalences (use transformation rules, result never changed) - Static plans: - Best plan known a-priori for query class - Access paths, operator sequence saved - Dynamic plans: - Best plan must be found at run-time - e.g. heterogenous query behavior #### Selection - Cascading: --TODO: Formula-- - Commutativity: --TODO: Formula-- #### Projection - Cascading: --TODO: Formula-- - Commutativity PI with SIGMA: (selection work on projection) #### Joins - Commutativity: --TODO: Formula-- - Associativity: --TODO: Formula-- - Join construction: --TODO: Formula-- - Commutativity SIGMA with JOIN: --TODO: Formula-- - Commutativity PI with JOIN: --TODO: Formula-- #### Set operators - Commutativity UNION / INTERSECTION: --TODO: Formula-- - Associativity UNION / INTERSECTION: --TODO: Formula-- - Commutativity SIGMA with SETS: --TODO: Formula-- - Commutativity PI with SETS: --TODO: Formula-- ### Cost-based optimization - Problem: No always optimal plan exists - Efficiency depends on instance, implementation, access paths and indexes, etc. - Idea: Assign average costs to operators -> estimate total cost (profit function) - Usage: - Database statistics - Result sizes - Block accesses #### Database Statistics - Statistics reveal bottlenecks - Focus - Block accesses: assumes I/O-bound bottleneck (common, index-reading ignored) - CPU statistics: assumes CPU-bound bottleneck (IMDBs) - Annotate operators: - Cost of performing (input size, indexes, I/O, processing, pipelining, materialization) - Size of result (expected input for parent, distinguish sorted/unsorted) - Estimation parameters (based on system catalog) - Database buffer size - Cardinality of input base relations (number of records, number distinct domain values) - Relation size (pages on disk) - Available access paths (index cardinalities: \#keys, \#pages, height, ranges) #### Estimation Result Sizes - Goal: Keep intermediate results small - Max. size: Product of relation cardinalities - Selections: Selectivity, reduction factors - Projection: No effect - Assumption: All reduction statistical independent - --TODO: Formula 5.36-- - Estimate reduction factor - Column = value - Column = Column - Column <,> value - Column IN {List} ##### Estimation Reduction Factor (Equality selection) - COLUMN = VALUE - No index: Use distinct values + histograms (or `$ rf = 0.1 $`) - Index: `$ rf = 1 / \#keys(I) $` - COLUMN = COLUMN - Index on both: `$ rf = 1 / _max(\#keys(I_1), \#keys(I_2)) $` (Assumption: smaller I SUBSET larger I) - Index on one: `$ 1 / \#keys(I) $` - No index: distinct values ##### Estimation Reduction Factor (Comparison + List) - COLUMN <,> VALUE - Index: `$ rf = (high(I) - value) / (high(I) - low(I)) $` - No index / no arithmetic type: `$ rf = 0.5 $`(Assumption: value in middle of domain) - Range queries: Disjunction of both conditions - Column IN {list} - Index: `$ rf = \#listvalues * (1 / \#keys(I)) $`(usually at most 0.5) - Column IN (subquery) - rf = estimated subquery result / number of distinct values in column outer relation ##### Estimation Block Accesses (Equality selection) - COLUMN = VALUE - Assumption: Uniform value distribution, available index - Depends on type of index + result size - Primary index: \#blocks = 1 - Cluster index: \#blocks = \#result size / (\#recordsR / \#blocksR) - Secondary index: \#blocks = \#result size - No index: \#blocks = \#blocks - COLUMN = COLUMN - Depends on selectivity of join (-> Join order optimization) - Worst case: Cartesian product (\#blocks = \#blocksR * \#recordsS ##### Estimation Block Accesses (Range selection) - COLUMN <,> VALUE - Assumption: Uniform value distribution, available index - Depends on type of index + result size - Primary/cluster index: \#blocks = \#blocksR / 2 - Secondary index: \#blocks = \#recordsR / 2 - No index: \#blocks = \#blocksR #### Access Path Decision - Problem: Multiple indexes available - Idea: Choose right access -> low I/O bound efficiency - Selectivity of access path: \#pagesFetched / \#allPages ##### Single-level index - Implementation: Predicate splitting - Per condition: Compute least expensive form of evaluation - Start with least expensive method - Apply all other conditions - Result: Suboptimal but sufficient ##### Multi-level index - Useful if conditions are index with secondary index - Set intersection of block- or record-IDs - Order result pointer set by block-IDs -> efficient access - Apply non-indexed selection afterwards ##### Sorted-index - Useful if tree index exists and traversable in specific order - e.g. GROUP BY / aggregations - Apply selection on-the-fly - Good for cluster indexes ##### Index-only - Rarely possible, very efficient - Requirement: All attributes are in dense index - Apply selection directly on index entry ### SQL EXPLAIN - Show operation execution order - Collect metrics for each operation - Allows bottleneck analysis + manual query optimization - EXPLAIN PLAN SET queryno= FOR ### Query Plan generation - Naive: 1. Apply transformations -> generate all possible plans 2. Assign costs 3. Choose least expensive plan - Problem: Prohibitively expensive - Idea: Don’t search for best plan, but avoid bad plans (Local minima are ok) ### Heuristic optimization - Goal: Alter tree to lower costs (use heuristics) - Principle: Improve operator tree step by step - Common assumption: Keep intermediate result small (less work, better buffer utilization) - Optimization heuristics - not always equally effective (query profile, statistics) - „magic“ (DBMS’ unique selling point) - Query Optimizer decides about heuristic #### Simple heuristics - Most important heuristics: - Select as early as possible - Project as early as possible - Avoid cartesian product (or as late as possible) - Use pipelining for adjacent unary operators - Result: Vastly improved operator tree ##### Simple heuristics (Selections) - Early selection => small intermediate result - Advantage: Better DB buffer utilization / faster processing (less records) - Break selections (where statement CNF) - Push selections down - Caution: Joins (dont push into indexed join => don’t destroy index) ##### Simple heuristics (Projections) - Early projection => Minimize record size - Shorter records => more records per block - Push projections down (caution: selections) ##### Simple heuristics (Cart. products) - Problem: Cartesian product => expensive - Solution: Use native joins (more efficient) - Force joins (merge selection and Cartesian product) ##### Hill climbing - Greedy strategy (apply simple heuristics) - Input: canonical query plan - Output: Improved query plan - Break selections - Push selections down (as far as possible) - Break, Eliminate, Push, Introduce projection - Collect seletions/projections (OP1 - SIGMA - PI) - Introduce joins (merge selections and cartesian product) - Prepare pipelining #### Complex heuristics - Sophisticated heuristics - Special operations - View merging - Eliminate common sub-expressions - Replace uncorrelated sub-queries by joins (Flattening) - Sort elimination - Dynamic filters - Exploit integrity constraints (semantic knowledge) - Selectivity reordering - Problem: Can fail (no perfect optimizer) - Trade-Off: Sub-optimal query execution vs. optimization time - Solution: Optimization hints ##### CH: Special operations - Use special algorithms for specific subtrees / operation patterns - Example: Non-standard join (Semi-join, anti-join, nest-join) ##### CH: View merging - Problem: View computation at query time (expensive) - Solution: Replace view with view definition (if not required) - Advantage: More freedom to optimize operator tree => better plan ##### CH: Common sub-expressions - Different operations need same input (multiple evaluations) - Solution - Logical rewriting (DeMorgan’s law) - Materialization ##### CH: Sub-Query flattening - Subqueries are optimized independently - Problem: Plan can be suboptimal (e.g. selection not applied early) - Problem: Result not processed after retrieval (but duplicate elimination could be efficient) - Solution: Rewrite subquery as join (or semi join for duplicate removing) ##### CH: Sort elimination - Problem: Most resource intensive operation - Intermediate result already sorted - Sort-merge join - Retrieval in-order (e.g. PK) - Unique-constraints - Solution: Don’t sort again ##### CH: Dynamic filters - Situation: Joins/Views computed completely but are later restricted - Solution: Create dynamic restriction (filters), push into operator tree - E.g. create semi-join with parents to only select „John“ - Result: Small intermediate results - —Image 43— ##### CH: Semantic knowledge - Use semantic knowledge for optimization - Use dependencies/integrity constraints - Strong technique but requires manual interaction - e.g. replace conditions (higher selectivity), semantic transformations (different access paths) ##### CH: Selectivity reordering - Apply operator with lowest selectivity first (smallest size, fewest records) - Use statistics (difficult) / guessing - Important: Join order optimization - Correlation of columns: Expensive, exponential combinations - Transient data (intermediate results): No statistics available - Dynamic sampling (Oracle 10g): Gather additional statistics during optimization - Gather sample set of tables - Test statistical connections on the fly ### Optimizer hints - DB Administrator gives optimization hint (what-if-analysis) - Alternative access path (provide fallback) - Modify PLAN_TABLE - When to use? - Temporary fix for bad plans - Access path regresses (version update, statistics update) => Revert plan - Optimizer unable to find good plan (additional statistics required, complex query) - Manual access path (prevent optimizer from changing plan) - Excessive optimization time (complex join, memory/CPU consumption) ### Join Trees - Join commutative/associative => eval. in every oder - Result: Join tree (different shapes, different assignments) - Different trees => different evaluation costs - Number of shapes (Catalan number): —Formula— - Number of assignments per shape: n! - Number of assignments: T(n) * n! #### Join Order Optimization - Find most efficient join tree / join implementation: NP-hard - Task: Find good plan in sensible time - Naming: Left: Build relation / Right: Probe relation - Desirable join cases: - Block-nested loop join (smaller rel. in-memory, inner loop) - Single-pass join (best case) - Build rel. fits into main memory - Index-nested loop join (index on probe, inner loop, small build) - Optimizer choices: - Consider all trees: Impossible - Consider class of tree: Select shape - Use heuristics - Find best solution: Dynamic programming / Branch and bound - Approximation: Greedy strategies, randomized strategies (II/SA), genetic algorithms #### Join Metrics - Goal: Evaluation join costs - Estimated result size - same for all assignments - Worst: Cartesian product - Estimated cost - Cost for performing join - Consider CPU, I/O, buffer statistics, join algorithm ##### Join Metrics (Size) - |R JOINc S| = |R| * |S| * rf - 1) Theta-Join: rf = 0.5 - 2) Natural/equi-join (R.A = S.A) - 2.1) R and S disjoint: rf = 0 - 2.2) Keys and foreign keys - A is key of S / foreign key of R: rf = 1 / #dv(R, A) = 1/|S| - A is key of R / foreign key of S: rf = 1 / #dv(S, A) = 1/|R| - Keys unknown: rf = 1 / max(#dv(R, A), #dv(S, A)) - 2.3) R and S same values: rf = 1 - Result size - Single equality condition: rf = 1 / max(#dv(R, A), #dv(S, A)) - Multiple equality conditions: Multiply reduction factors - Multiple relations: Cascade formula (order irrelevant) - Improve accuracy: - Histograms - Dynamic sampling - Simulating common queries - Incorporating previous query results - More statistics are expensive w/ high change rate ##### Join Metrics (Execution cost) - Goal: Minimize execution cost - Cost metrics - Size of intermediate results - Block accesses - Size of intermediate results - costs(R JOIN S) = 0 - costs(R JOIN S JOIN T) = |R JOIN S| - Ignores: Real I/O, memory, CPU, Join algorithm ##### Join Metrics (Access cost) - Problem: Major bottleneck (depends on join algorithm) - Costs writing result: |R JOIN S| / \#blockingFactor - Block-nested loop join - Cost = bR * (1 + bS) + resultCost - Index-nested loop join - Cost = bR + (|R| * (Cix + 1)) + resultCost - Index retrieval cost Cix - In-memory index = 0 - Cluster index: Cost = indexAccessCost + SSIGMA / blockingFactor - Secondary index: Cost = SSIGMA - Hash index: Cost >= 1 (depends on hash size/collision) - Sort-Merge-Join - Cost = bR + bS + sortingCost + resultCost #### Left-deep Join Trees - Simple heuristic: Use left-deep join trees (only one tree shape) - Advantage: - Decrease build relation (good performance, good buffer usage) - Pipeline results into next (no temp. table, but: not for sort-merge-join) - Number of trees smaller (assignments, n!) - Disadvantages - Impractical for > 15 joins - No parallel execution (dependencies) #### Dynamic Programming - Idea: Break problem into sub-problem / Memorize best solution - Cost table - Store lowest costs for joins (remember good solutions) - Columns: Relation subset, estimated result size, estimated lowest cost, expression - But: Uses buffer space - Claim: Table always contains join expressions with lowest cost for gives relation subsets - Guarantees best join order - Exponential effort 2^n (10-15 joins) - Space / memory consumption ##### Dynamic Programming (Induction) - For each single relation subset: One row (cost = 0, size = |R|) - For each relation subset size two - One row (cost=0) - Use heuristic to choose ordering (smaller to left) - Induction: - One table row per set - Estimate size and costs (use cheapest subset) - Find cheapest expression (size+cost) - Left-deep tree: Add new relation to right - Arbitrary shape: Add smallest to left, consider all partitions #### Greedy Strategy - Result not optimal but sufficient - Goal: Quickly construct left-deep tree - Algorithm - Start with join pair with lowest cost (smaller relation to left) - Incrementally add relation (the one with cheapest cost, attach to right, use distinct values) - Drawback: Only heuristic (will not find optimal solution) #### Randomized Algorithms - Light-weight algorithm (low memory, can stop after specific time, usually finds good solution) - Good: Solution space not enumerable (better than heuristics) - Idea - Solutions are points in space - Connections (moves): Transform solutions - Typical algorithms - Iterative improvement (short optimization time) - Simulated annealing (good solutions) ##### Randomized Algorithms (Typical moves) - Typical moves (left-deep) - Swap: Exchange position of two elements - 3-Cycle: Rotate the arbitrary positions - Typical moves (bushy tree) - Commutativity - Associativity - Left join exchange - Right join exchange ###### Iterative Improvement - Problem with hill climbing: Too expensive (check all solutions) - Start at random point - Apply random move - Check solution - Less costly: Repeat - More expensive: Undo operation, repeat (no better move found -> quit) - Result: Random walk, local optimum - Constant improvement - quite efficient - can’t leave local minima ###### Simulated Annealing - Simulate natural annealing (first heating, slowly cooling) - Start with random tree (hight temperature) - Apply random move - Proceed if less expensive or with probability P —Slide 57— - Reduce temperature - Problem: Parameterization (starting T, reduction, stopping condition) - Two-phase-version (often used) - Iterative improvement for several solutions - Use least expensive result for sim.ann. (can start with lower T) #### Randomized Trees - Join Tree Generation - Generate shape - Generate relation assignment - Cases: - Generate Deep-Left tree - Generate arbitrary shape ##### Randomized Trees (Deep-left) - Task: Find random permutation of relations (quickly) - Ranking/unranking - Ranking: f:S->[0,n[ - Unranking: f:[0,n[->S - Efficient unranking: - Elements: [R1,…Rn] - Algorithm: —Slide 63— ##### Randomized Trees (arbitrary shape) - Generate code word: Dyck word - Bijection Dyck word - binary tree - Words of balanced number of characters - Encoding Binary Tree - Traverse tree pre-order (visit, left, right) - Skip last leaf node - Inner node: (/1, leaf node: )/0 - Mapping to Triangle - Number of paths: Catalan number - Unranking (use triangle grid) - Integer to Dyck word - n+1 relations (leafs) => Dyck word length 2n - Ballot number p(i,j): Number of possible paths from (0,0) to (i,j) - Use q(i,j): Number possible paths from (i,j) to (2n,0) ##### Randomized Trees (arbitrary shape, algorithm) - *???*