Repository: pingcap/tidb
File: pkg/statistics/histogram.go (Go)

Code before:
	// LessCounts are equal only if no valid buckets or both values are out of range
	isInValidBucket := lessCountA != lessCountB
	// If values in the same bucket, use skewRatio to adjust the range estimate to account for potential skew.
	if isInValidBucket && bktIndexA == bktIndexB {
		// sctx may be nil for stats version 1
		if sctx != nil {
			skewRatio := sctx.GetSessionVars().RiskRangeSkewRatio
			sctx.GetSessionVars().RecordRelevantOptVar(vardef.TiDBOptRiskRangeSkewRatio)
			if skewRatio > 0 {
				// Worst case skew is if the range includes all the rows in the bucket
				skewEstimate := hg.Buckets[bktIndexA].Count
				if bktIndexA > 0 {
					skewEstimate -= hg.Buckets[bktIndexA-1].Count
				}
				// If range does not include last value of its bucket, remove the repeat count from the skew estimate.
				if lessCountB <= float64(hg.Buckets[bktIndexA].Count-hg.Buckets[bktIndexA].Repeat) {
					skewEstimate -= hg.Buckets[bktIndexA].Repeat
				}
				return CalculateSkewRatioCounts(rangeEst, float64(skewEstimate), skewRatio)
			}
		}
	}
	return DefaultRowEst(rangeEst)
}

// CalculateSkewRatioCounts calculates the default, min, and max skew estimates given a skew ratio.
func CalculateSkewRatioCounts(estimate, skewEstimate, skewRatio float64) RowEstimate {
	skewDiff := skewEstimate - estimate
	// Add a "ratio" of the skewEstimate to adjust the default row estimate.
	skewAmt := max(0, skewDiff*skewRatio)
	maxSkewAmt := min(skewDiff, 2*skewAmt)
	return RowEstimate{estimate + skewAmt, estimate, estimate + maxSkewAmt}
}

// RowEstimate stores the min, default, and max row count estimates.
type RowEstimate struct {
	Est    float64
	MinEst float64
	MaxEst float64
}

// DefaultRowEst returns a RowEstimate with same value for all three fields
func DefaultRowEst(est float64) RowEstimate {
	return RowEstimate{est, est, est}
}

// Add adds two RowEstimates together, storing result in the first RowEstimate.
func (r *RowEstimate) Add(r1 RowEstimate) {
	r.Est += r1.Est
	r.MinEst += r1.MinEst
	r.MaxEst += r1.MaxEst

Review comment (suggestion):
tidb_opt_risk_range_skew_ratio=0 still inflates MaxEst to the full bucket. Could you please explain why we need to do this? Perhaps we could add a comment here.

Code after:
	// LessCounts are equal only if no valid buckets or both values are out of range
	isInValidBucket := lessCountA != lessCountB
	// If values in the same bucket, use skewRatio to adjust the range estimate to account for potential skew.
	if isInValidBucket && bktIndexA == bktIndexB {
		// sctx may be nil for stats version 1
		if sctx != nil {
			skewRatio := sctx.GetSessionVars().RiskRangeSkewRatio
			sctx.GetSessionVars().RecordRelevantOptVar(vardef.TiDBOptRiskRangeSkewRatio)
			// Worst case skew is if the range includes all the rows in the bucket
			skewEstimate := hg.Buckets[bktIndexA].Count
			if bktIndexA > 0 {
				skewEstimate -= hg.Buckets[bktIndexA-1].Count
			}
			// If range does not include last value of its bucket, remove the repeat count from the skew estimate.
			if lessCountB <= float64(hg.Buckets[bktIndexA].Count-hg.Buckets[bktIndexA].Repeat) {
				skewEstimate -= hg.Buckets[bktIndexA].Repeat
			}
			if skewRatio > 0 {
				// Cap the max estimate to 2X the estimate, but not beyond the bucket's maximum possible row count.
				// TODO: RiskRangeSkewRatio is predominantly used for outOfRangeRowCount, and
				//       it's usage is diluted here. Consider how to address this if
				//       issues are reported here regarding under-estimation for in-bucket ranges.
				rangeEst = CalculateSkewRatioCounts(rangeEst.Est, min(rangeEst.Est*2, float64(skewEstimate)), skewRatio)
			}
			// Report the full max estimate for risk estimation usage in compareCandidates (skylinePruning).
			rangeEst.MaxEst = max(rangeEst.MaxEst, float64(skewEstimate))
		}
	}
	return rangeEst
}

// CalculateSkewRatioCounts calculates the default, min, and max skew estimates given a skew ratio.
func CalculateSkewRatioCounts(estimate, skewEstimate, skewRatio float64) RowEstimate {
	skewDiff := skewEstimate - estimate
	// Add a "ratio" of the skewEstimate to adjust the default row estimate.
	skewAmt := max(0, skewDiff*skewRatio)
	maxSkewAmt := min(skewDiff, 2*skewAmt)
	return RowEstimate{estimate + skewAmt, estimate, estimate + maxSkewAmt}
}

// RowEstimate stores the min, default, and max row count estimates.
type RowEstimate struct {
	Est    float64
	MinEst float64
	MaxEst float64
}

// DefaultRowEst returns a RowEstimate with same value for all three fields
func DefaultRowEst(est float64) RowEstimate {
	return RowEstimate{est, est, est}
}