优化器直方图
This commit is contained in:
1 parent
7d96933e32
commit
d59e65b639
17 files changed
+353
-49
No files matched your search
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in new issue
Block a user