diff --git a/executor/executor_test.go b/executor/executor_test.go index 771002b5c4699..3f278cc156291 100644 --- a/executor/executor_test.go +++ b/executor/executor_test.go @@ -3952,7 +3952,7 @@ func (s *testSuite) TestShowTableRegion(c *C) { // 4 regions to store record data. // 1 region to store index data. c.Assert(len(rows), Equals, 5) - c.Assert(len(rows[0]), Equals, 7) + c.Assert(len(rows[0]), Equals, 11) tbl := testGetTableByName(c, tk.Se, "test", "t_regions") // Check the region start key. c.Assert(rows[0][1], Equals, fmt.Sprintf("t_%d_r", tbl.Meta().ID)) diff --git a/executor/show.go b/executor/show.go index ed0864cb22f2f..2b5553bc16727 100644 --- a/executor/show.go +++ b/executor/show.go @@ -1289,5 +1289,10 @@ func (e *ShowExec) fillRegionsToChunk(regions []regionMeta) { } else { e.result.AppendInt64(6, 0) } + + e.result.AppendInt64(7, regions[i].writtenBytes) + e.result.AppendInt64(8, regions[i].readBytes) + e.result.AppendInt64(9, regions[i].approximateSize) + e.result.AppendInt64(10, regions[i].approximateKeys) } } diff --git a/executor/split.go b/executor/split.go index d21dfdfb0b107..0b4cf8a3b57ef 100755 --- a/executor/split.go +++ b/executor/split.go @@ -29,6 +29,7 @@ import ( "github.com/pingcap/parser/mysql" "github.com/pingcap/tidb/kv" "github.com/pingcap/tidb/sessionctx" + "github.com/pingcap/tidb/store/helper" "github.com/pingcap/tidb/store/tikv" "github.com/pingcap/tidb/table/tables" "github.com/pingcap/tidb/tablecodec" @@ -439,12 +440,16 @@ func (e *SplitTableRegionExec) getSplitTableKeys() ([][]byte, error) { // RegionMeta contains a region's peer detail type regionMeta struct { - region *metapb.Region - leaderID uint64 - storeID uint64 // storeID is the store ID of the leader region. - start string - end string - scattering bool + region *metapb.Region + leaderID uint64 + storeID uint64 // storeID is the store ID of the leader region. + start string + end string + scattering bool + writtenBytes int64 + readBytes int64 + approximateSize int64 + approximateKeys int64 } func getPhysicalTableRegions(physicalTableID int64, tableInfo *model.TableInfo, tikvStore tikv.Storage, s kv.SplitableStore, uniqueRegionMap map[uint64]struct{}) ([]regionMeta, error) { @@ -460,7 +465,7 @@ func getPhysicalTableRegions(physicalTableID int64, tableInfo *model.TableInfo, } recordPrefix := tablecodec.GenTableRecordPrefix(physicalTableID) tablePrefix := tablecodec.GenTablePrefix(physicalTableID) - recordRegions, err := getRegionMeta(recordRegionMetas, uniqueRegionMap, tablePrefix, recordPrefix, nil, physicalTableID, 0) + recordRegions, err := getRegionMeta(tikvStore, recordRegionMetas, uniqueRegionMap, tablePrefix, recordPrefix, nil, physicalTableID, 0) if err != nil { return nil, err } @@ -477,7 +482,7 @@ func getPhysicalTableRegions(physicalTableID int64, tableInfo *model.TableInfo, return nil, err } indexPrefix := tablecodec.EncodeTableIndexPrefix(physicalTableID, index.ID) - indexRegions, err := getRegionMeta(regionMetas, uniqueRegionMap, tablePrefix, recordPrefix, indexPrefix, physicalTableID, index.ID) + indexRegions, err := getRegionMeta(tikvStore, regionMetas, uniqueRegionMap, tablePrefix, recordPrefix, indexPrefix, physicalTableID, index.ID) if err != nil { return nil, err } @@ -504,7 +509,7 @@ func getPhysicalIndexRegions(physicalTableID int64, indexInfo *model.IndexInfo, recordPrefix := tablecodec.GenTableRecordPrefix(physicalTableID) tablePrefix := tablecodec.GenTablePrefix(physicalTableID) indexPrefix := tablecodec.EncodeTableIndexPrefix(physicalTableID, indexInfo.ID) - indexRegions, err := getRegionMeta(regions, uniqueRegionMap, tablePrefix, recordPrefix, indexPrefix, physicalTableID, indexInfo.ID) + indexRegions, err := getRegionMeta(tikvStore, regions, uniqueRegionMap, tablePrefix, recordPrefix, indexPrefix, physicalTableID, indexInfo.ID) if err != nil { return nil, err } @@ -585,7 +590,7 @@ func (d *regionKeyDecoder) decodeRegionKey(key []byte) string { return fmt.Sprintf("%x", key) } -func getRegionMeta(regionMetas []*tikv.Region, uniqueRegionMap map[uint64]struct{}, tablePrefix, recordPrefix, indexPrefix []byte, physicalTableID, indexID int64) ([]regionMeta, error) { +func getRegionMeta(tikvStore tikv.Storage, regionMetas []*tikv.Region, uniqueRegionMap map[uint64]struct{}, tablePrefix, recordPrefix, indexPrefix []byte, physicalTableID, indexID int64) ([]regionMeta, error) { regions := make([]regionMeta, 0, len(regionMetas)) for _, r := range regionMetas { if _, ok := uniqueRegionMap[r.GetID()]; ok { @@ -598,6 +603,37 @@ func getRegionMeta(regionMetas []*tikv.Region, uniqueRegionMap map[uint64]struct storeID: r.GetLeaderStoreID(), }) } + regions, err := getRegionInfo(tikvStore, regions) + if err != nil { + return regions, err + } decodeRegionsKey(regions, tablePrefix, recordPrefix, indexPrefix, physicalTableID, indexID) return regions, nil } + +func getRegionInfo(store tikv.Storage, regions []regionMeta) ([]regionMeta, error) { + // check pd server exists. + etcd, ok := store.(tikv.EtcdBackend) + if !ok { + return regions, nil + } + pdHosts := etcd.EtcdAddrs() + if len(pdHosts) == 0 { + return regions, nil + } + tikvHelper := &helper.Helper{ + Store: store, + RegionCache: store.GetRegionCache(), + } + for i := range regions { + regionInfo, err := tikvHelper.GetRegionInfoByID(regions[i].region.Id) + if err != nil { + return nil, err + } + regions[i].writtenBytes = regionInfo.WrittenBytes + regions[i].readBytes = regionInfo.ReadBytes + regions[i].approximateSize = regionInfo.ApproximateSize + regions[i].approximateKeys = regionInfo.ApproximateKeys + } + return regions, nil +} diff --git a/planner/core/planbuilder.go b/planner/core/planbuilder.go index 519e3326994b4..a2a717d694636 100644 --- a/planner/core/planbuilder.go +++ b/planner/core/planbuilder.go @@ -1103,7 +1103,7 @@ func buildShowDDLJobsFields() *expression.Schema { } func buildTableRegionsSchema() *expression.Schema { - schema := expression.NewSchema(make([]*expression.Column, 0, 10)...) + schema := expression.NewSchema(make([]*expression.Column, 0, 11)...) schema.Append(buildColumn("", "REGION_ID", mysql.TypeLonglong, 4)) schema.Append(buildColumn("", "START_KEY", mysql.TypeVarchar, 64)) schema.Append(buildColumn("", "END_KEY", mysql.TypeVarchar, 64)) @@ -1111,6 +1111,10 @@ func buildTableRegionsSchema() *expression.Schema { schema.Append(buildColumn("", "LEADER_STORE_ID", mysql.TypeLonglong, 4)) schema.Append(buildColumn("", "PEERS", mysql.TypeVarchar, 64)) schema.Append(buildColumn("", "SCATTERING", mysql.TypeTiny, 1)) + schema.Append(buildColumn("", "WRITTEN_BYTES", mysql.TypeLonglong, 4)) + schema.Append(buildColumn("", "READ_BYTES", mysql.TypeLonglong, 4)) + schema.Append(buildColumn("", "APPROXIMATE_SIZE(MB)", mysql.TypeLonglong, 4)) + schema.Append(buildColumn("", "APPROXIMATE_KEYS", mysql.TypeLonglong, 4)) return schema } diff --git a/store/helper/helper.go b/store/helper/helper.go index b1d543cc6c2d5..6c2aac6a0ffa9 100644 --- a/store/helper/helper.go +++ b/store/helper/helper.go @@ -17,8 +17,10 @@ import ( "bytes" "context" "encoding/json" + "io" "math" "net/http" + "strconv" "time" "github.com/pingcap/errors" @@ -406,34 +408,52 @@ type RegionsInfo struct { // GetRegionsInfo gets the region information of current store by using PD's api. func (h *Helper) GetRegionsInfo() (*RegionsInfo, error) { + var regionsInfo RegionsInfo + err := h.requestPD("GET", pdapi.Regions, nil, ®ionsInfo) + return ®ionsInfo, err +} + +// GetRegionInfoByID gets the region information of the region ID by using PD's api. +func (h *Helper) GetRegionInfoByID(regionID uint64) (*RegionInfo, error) { + var regionInfo RegionInfo + err := h.requestPD("GET", pdapi.RegionByID+strconv.FormatUint(regionID, 10), nil, ®ionInfo) + return ®ionInfo, err +} + +// request PD API, decode the response body into res +func (h *Helper) requestPD(method, uri string, body io.Reader, res interface{}) error { etcd, ok := h.Store.(tikv.EtcdBackend) if !ok { - return nil, errors.WithStack(errors.New("not implemented")) + return errors.WithStack(errors.New("not implemented")) } pdHosts := etcd.EtcdAddrs() if len(pdHosts) == 0 { - return nil, errors.New("pd unavailable") + return errors.New("pd unavailable") } - req, err := http.NewRequest("GET", protocol+pdHosts[0]+pdapi.Regions, nil) + + logutil.Logger(context.Background()).Debug("RequestPD URL", zap.String("url", protocol+pdHosts[0]+uri)) + req, err := http.NewRequest(method, protocol+pdHosts[0]+uri, body) if err != nil { - return nil, errors.Trace(err) + return errors.Trace(err) } resp, err := http.DefaultClient.Do(req) if err != nil { - return nil, errors.Trace(err) + return errors.Trace(err) } + defer func() { err = resp.Body.Close() if err != nil { logutil.Logger(context.Background()).Error("close body failed", zap.Error(err)) } }() - var regionsInfo RegionsInfo - err = json.NewDecoder(resp.Body).Decode(®ionsInfo) + + err = json.NewDecoder(resp.Body).Decode(res) if err != nil { - return nil, errors.Trace(err) + return errors.Trace(err) } - return ®ionsInfo, nil + + return nil } // StoresStat stores all information get from PD's api. diff --git a/util/pdapi/const.go b/util/pdapi/const.go index 436784f627245..29d3746e50122 100644 --- a/util/pdapi/const.go +++ b/util/pdapi/const.go @@ -15,8 +15,9 @@ package pdapi // The following constants are the APIs of PD server. const ( - HotRead = "/pd/api/v1/hotspot/regions/read" - HotWrite = "/pd/api/v1/hotspot/regions/write" - Regions = "/pd/api/v1/regions" - Stores = "/pd/api/v1/stores" + HotRead = "/pd/api/v1/hotspot/regions/read" + HotWrite = "/pd/api/v1/hotspot/regions/write" + Regions = "/pd/api/v1/regions" + RegionByID = "/pd/api/v1//region/id/" + Stores = "/pd/api/v1/stores" )