优化器直方图

This commit is contained in:
LIm1tZz7 committed 2026-10-03 21:03:17 +08:00
1 parent 7d96933e32
commit d59e65b639
17 files changed
+353 -49

No files matched your search

+44 -2
View File
@@ -10,8 +10,11 @@ module ChuSQL.Core.Model
, Value (..)
, Row
, TableMeta (..)
, Histogram (..)
, Table (..)
, Database
, histogramOf
, histogramFor
, colNames
, colType
, lookupTable
@@ -42,7 +45,7 @@ module ChuSQL.Core.Model
import Data.Char (isDigit, isSpace, toLower)
import Data.Hashable (Hashable (..))
import Data.List (isSuffixOf, stripPrefix)
import Data.Maybe (fromMaybe)
import Data.Maybe (fromMaybe, mapMaybe)
-- 数据模型:列类型、列约束、值、行、表、数据库,外加查表小工具。
@@ -441,10 +444,19 @@ instance Hashable Value where
-- | 一行数据
type Row = [(String, Value)]
-- | 一张表的统计:行数、各列不同值数(capped 表示只数到上限)、有索引的列
-- | 一列的等宽直方图:区间下界、上界与每桶计数
data Histogram = Histogram
{ histLow :: Double
, histHigh :: Double
, histBuckets :: [Int]
}
deriving (Show, Eq)
-- | 一张表的统计:行数、各列不同值数、列直方图与索引
data TableMeta = TableMeta
{ metaRowCount :: Int
, metaDistinct :: [(String, Int, Bool)]
, metaHistograms :: [(String, Histogram)]
, metaIndexes :: [String]
}
deriving (Show)
@@ -461,6 +473,36 @@ data Table = Table
-- | 库:库名到表的映射
type Database = [(String, Table)]
-- | 这一列的直方图
histogramFor :: TableMeta -> String -> Maybe Histogram
histogramFor m col = lookup col (metaHistograms m)
-- | 数值列按 16 个等宽桶统计,无数值给 Nothing
histogramOf :: [Value] -> Maybe Histogram
histogramOf values = case mapMaybe numericOf values of
[] -> Nothing
vs -> let lo = minimum vs; hi = maximum vs in Just (Histogram lo hi (bucketCounts lo hi vs))
-- | 值转成用于分桶的数;非数值给 Nothing
numericOf :: Value -> Maybe Double
numericOf (VInt n) = Just (fromIntegral n)
numericOf (VFloat d) = Just d
numericOf _ = Nothing
-- | 每个桶里落了多少个值
bucketCounts :: Double -> Double -> [Double] -> [Int]
bucketCounts lo hi vs = [length [v | v <- vs, bucketIndexOf lo hi v == i] | i <- [0 .. bucketTotal - 1]]
-- | 值落在第几个桶;区间退化时都算第 0 桶
bucketIndexOf :: Double -> Double -> Double -> Int
bucketIndexOf lo hi v
| hi <= lo = 0
| otherwise = max 0 (min (bucketTotal - 1) (floor ((v - lo) / (hi - lo) * fromIntegral bucketTotal) :: Int))
-- | 直方图的桶数
bucketTotal :: Int
bucketTotal = 16
-- | 取表的列名
colNames :: Table -> [String]
colNames = map fst . tableCols
+23 -7
View File
@@ -15,7 +15,7 @@ module ChuSQL.Core.Protocol (
queryResultJson,
) where
import ChuSQL.Core.Model (Row, Value (..))
import ChuSQL.Core.Model (Histogram (..), Row, Value (..))
import Data.Aeson (FromJSON (..), ToJSON (..), object, withObject, (.:), (.:?), (.!=), (.=))
import qualified Data.Aeson as A
import qualified Data.Aeson.Key as K
@@ -244,6 +244,7 @@ data TableInfo = TableInfo
, tiRows :: Int
, tiIndexes :: [String]
, tiStats :: [(String, Int, Bool)]
, tiHistograms :: [(String, Histogram)]
}
deriving (Show, Eq)
@@ -255,11 +256,21 @@ instance ToJSON TableInfo where
, "columns" .= tiColumns info
, "row_count" .= tiRows info
, "indexes" .= map (\name -> object ["column" .= name]) (tiIndexes info)
, "stats" .= map statJson (tiStats info)
, "stats" .= map (statJson (tiHistograms info)) (tiStats info)
]
where
statJson (name, distinct, capped) =
object ["name" .= name, "distinct" .= distinct, "capped" .= capped]
statJson hists (name, distinct, capped) =
object (statFields ++ histFields (lookup name hists))
where
statFields = ["name" .= name, "distinct" .= distinct, "capped" .= capped]
histFields Nothing = []
histFields (Just h) = ["lo" .= histLow h, "hi" .= histHigh h, "hist" .= histBuckets h]
-- | 线上直方图还原;区间或桶缺了就当作没有
histogramFromWire :: Maybe Double -> Maybe Double -> [Int] -> Maybe Histogram
histogramFromWire (Just low) (Just high) counts
| not (null counts) = Just (Histogram low high counts)
histogramFromWire _ _ _ = Nothing
-- | JSON 解回表信息
instance FromJSON TableInfo where
@@ -270,14 +281,19 @@ instance FromJSON TableInfo where
indexes <- o .:? "indexes" .!= []
stats <- o .:? "stats" .!= []
indexColumns <- mapM (\v -> withObject "IndexWire" (.: "column") v) (indexes :: [A.Value])
parsedStats <- mapM parseStat (stats :: [A.Value])
pure (TableInfo table columns count indexColumns parsedStats)
parsed <- mapM parseStat (stats :: [A.Value])
let parsedStats = [(name, distinct, capped) | (name, distinct, capped, _) <- parsed]
parsedHistograms = [(name, h) | (name, _, _, Just h) <- parsed]
pure (TableInfo table columns count indexColumns parsedStats parsedHistograms)
where
parseStat = withObject "ColumnStat" $ \o -> do
name <- o .: "name"
distinct <- o .: "distinct"
capped <- o .:? "capped" .!= False
pure (name, distinct, capped)
low <- o .:? "lo"
high <- o .:? "hi"
counts <- o .:? "hist" .!= []
pure (name, distinct, capped, histogramFromWire low high counts)
-- | 一条账号记录
data Account = Account