-
Notifications
You must be signed in to change notification settings - Fork 5.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
*: implement auto analyze #4141
Conversation
Currently, if the |
ddl/ddl.go
Outdated
@@ -273,7 +273,7 @@ func newDDL(ctx goctx.Context, etcdCli *clientv3.Client, store kv.Storage, | |||
manager = NewMockOwnerManager(id, cancelFunc) | |||
syncer = NewMockSchemaSyncer() | |||
} else { | |||
manager = NewOwnerManager(etcdCli, id, cancelFunc) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add ddl
as a constant.
ddl/owner_manager.go
Outdated
} | ||
|
||
// NewOwnerManager creates a new OwnerManager. | ||
func NewOwnerManager(etcdCli *clientv3.Client, id string, cancel goctx.CancelFunc) OwnerManager { | ||
func NewOwnerManager(etcdCli *clientv3.Client, prompt string, id string, key string, cancel goctx.CancelFunc) OwnerManager { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/prompt string, id string, key string/prompt, id, key string
domain/domain.go
Outdated
if do.etcdClient == nil { | ||
statsOwner = ddl.NewMockOwnerManager(id, cancelFunc) | ||
} else { | ||
statsOwner = ddl.NewOwnerManager(do.etcdClient, "stats", id, statistics.StatsOwnerKey, cancelFunc) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The same as stats
.
domain/domain.go
Outdated
} else { | ||
statsOwner = ddl.NewOwnerManager(do.etcdClient, "stats", id, statistics.StatsOwnerKey, cancelFunc) | ||
} | ||
statsOwner.CampaignOwners(cancelCtx) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to check the error?
domain/domain.go
Outdated
if statsOwner.IsOwner() { | ||
err := do.statsHandle.HandleAutoAnalyze(do.InfoSchema()) | ||
if err != nil { | ||
log.Error(errors.ErrorStack(err)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Complete the error message. Add the package and function information. You can refer to other logs.
@lamxTyler Can we write a |
ddl/owner_manager.go
Outdated
log.Infof("[ddl] %s etcd session is done, creates a new one", idInfo) | ||
etcdSession, err = newSession(ctx, idInfo, m.etcdCli, newSessionRetryUnlimited, ManagerSessionTTL) | ||
log.Infof("[%s] %s etcd session is done, creates a new one", m.prompt, idInfo) | ||
etcdSession, err = newSession(ctx, m.prompt, idInfo, m.etcdCli, newSessionRetryUnlimited, ManagerSessionTTL) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we use a log prefix instead of m.prompt
and idInfo
here? We can pass an argument.
ddl/owner_manager.go
Outdated
@@ -260,10 +259,3 @@ func (m *ownerManager) watchOwner(ctx goctx.Context, etcdSession *concurrency.Se | |||
} | |||
} | |||
} | |||
|
|||
func init() { | |||
err := setManagerSessionTTL() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why remove this ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I search the project and find that no one uses it. Need to add back?
ddl/syncer.go
Outdated
@@ -123,7 +123,7 @@ func (s *schemaVersionSyncer) Init(ctx goctx.Context) error { | |||
if err != nil { | |||
return errors.Trace(err) | |||
} | |||
s.session, err = newSession(ctx, s.selfSchemaVerPath, s.etcdCli, | |||
s.session, err = newSession(ctx, "ddl", s.selfSchemaVerPath, s.etcdCli, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use ddlPrompt
directly.
ddl/syncer.go
Outdated
@@ -140,7 +140,7 @@ func (s *schemaVersionSyncer) Done() <-chan struct{} { | |||
|
|||
// Restart implements SchemaSyncer.Restart interface. | |||
func (s *schemaVersionSyncer) Restart(ctx goctx.Context) error { | |||
session, err := newSession(ctx, s.selfSchemaVerPath, s.etcdCli, | |||
session, err := newSession(ctx, "ddl", s.selfSchemaVerPath, s.etcdCli, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ditto.
domain/domain.go
Outdated
@@ -575,6 +575,29 @@ func (do *Domain) UpdateTableStatsLoop(ctx context.Context) error { | |||
} | |||
} | |||
}(do) | |||
go func(do *Domain) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function is too long. Can we extract this logic into a function?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok.
domain/domain.go
Outdated
statsOwner = ddl.NewOwnerManager(do.etcdClient, statistics.StatsPrompt, id, statistics.StatsOwnerKey, cancelFunc) | ||
} | ||
err := statsOwner.CampaignOwner(cancelCtx) | ||
if err != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we campaign failed, it may be not an owner always. Do we need to do something to handle it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As long as there is at most one owner, it will be ok.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If all TiDB are failed, then what should we do?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Then there will be no auto analyze since there is no owner. Or do we have better solutions?
statistics/update.go
Outdated
return true | ||
} | ||
|
||
// HandleAutoAnalyze analyze the newly created table or index. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/analyze/analyzes
sql := fmt.Sprintf("analyze table %s", tblName) | ||
log.Infof("[stats] auto analyze table %s now", tblName) | ||
_, _, err := h.ctx.(sqlexec.RestrictedSQLExecutor).ExecRestrictedSQL(h.ctx, sql) | ||
return errors.Trace(err) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to analyze the next table? Here we only print the log?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Nope, currently every time we only analyze one table or index, to reduce the impact on the server.
- Only the log? It also analyzes the table.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
About the first point of view,you use db.Tables
and haven't a break
after handling one table or index, so how do we do it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is a return
after the execute?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Gotcha.
@lamxTyler I think owner is not a kind of "util". We'd better isolate this package. |
ddl/syncer.go
Outdated
@@ -34,6 +35,9 @@ const ( | |||
// DDLGlobalSchemaVersion is the path on etcd that is used to store the latest schema versions. | |||
// It's exported for testing. | |||
DDLGlobalSchemaVersion = "/tidb/ddl/global_schema_version" | |||
// DDLOwnerKey is the ddl owner path that is saved to etcd, and it's exported for testing. | |||
DDLOwnerKey = "/tidb/ddl/fg/owner" | |||
ddlPrompt = "ddl" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's better to put this code into ddl.go
.
// OwnerManager is used to campaign the owner and manage the owner information. | ||
type OwnerManager interface { | ||
// ID returns the ID of DDL. | ||
// Manager is used to campaign the owner and manage the owner information. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why change this name?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because the package's name is owner
, so it will make golint unhappy if the names' prefix is owner
.
sql := fmt.Sprintf("analyze table %s", tblName) | ||
log.Infof("[stats] auto analyze table %s now", tblName) | ||
_, _, err := h.ctx.(sqlexec.RestrictedSQLExecutor).ExecRestrictedSQL(h.ctx, sql) | ||
return errors.Trace(err) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Gotcha.
for _, tbl := range tbls { | ||
tblInfo := tbl.Meta() | ||
statsTbl := h.GetTableStats(tblInfo.ID) | ||
if statsTbl.Pseudo || statsTbl.Count == 0 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- We should analyze table with least version firstly. We can use a 'select table_id from stats_meta where modify_count >= count and count >= 1000 order by version limit 1.
- If a table is deleted, it's count is not zero but histogram is empty. We shouldn't analyze it. If its version is old enough, we can delete it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- First, There is no direct mapping from table_id to table_name, which would make it hard to produce the analyze statement. Second, modify_count >= count does not ensure that this is a newly created table. Third, Since I will implement a job queue, the job queue could be a priority queue, which I think is a better solution.
- Well, I did not notice it before. A deleted table has non-zero count is unreasonable. So, let's directly make the deleted table's count 0.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you can refine 1 in the future. And currently the deleted table is not with count 0, so we still need to process this case. Owner's grabage collection is also nessesary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Make the deleted table 0 could be done in this PR. Garbage collection is not relevant with auto analyze, so I will do it in another PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, even if the deleted table's count is not 0, we do not need to process it, because if it is deleted, then it will not be in the info schema or the stats cache.
LGTM |
owner/manager.go
Outdated
@@ -256,9 +240,11 @@ func (m *ownerManager) watchOwner(ctx goctx.Context, etcdSession *concurrency.Se | |||
} | |||
} | |||
|
|||
func init() { | |||
err := setManagerSessionTTL() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why remove this function? It's useful.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But it is not used?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's used in the test.
domain/domain.go
Outdated
} else { | ||
statsOwner = owner.NewOwnerManager(do.etcdClient, statistics.StatsPrompt, id, statistics.StatsOwnerKey, cancelFunc) | ||
} | ||
err := statsOwner.CampaignOwner(cancelCtx) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we need to do something when err != nil
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Like what?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The network between PD and TiDB is unstable
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Try until the err
is not nil?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It‘s better to put it out of a goroutine. If it hard to handle, you can add a TODO
here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, I add a TODO here.
@@ -223,3 +223,50 @@ func (s *testStatsUpdateSuite) TestTxnWithFailure(c *C) { | |||
stats1 = h.GetTableStats(tableInfo1.ID) | |||
c.Assert(stats1.Count, Equals, int64(rowCount1+1)) | |||
} | |||
|
|||
func (s *testStatsUpdateSuite) TestAutoUpdate(c *C) { | |||
store, do, err := newStoreWithBootstrap() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we use a unified store in testStatsUpdateSuite? Bootstrap make the test run a long time.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do it in another pr? There are lots of bootstrap in this package.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK
} | ||
} | ||
|
||
func isContextDone(ctx goctx.Context) bool { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this function useless?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nope. In fact, this function also exists in DDL package, but it seems awkward to export it here. Any advice?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's OK now.
domain/domain.go
Outdated
} else { | ||
statsOwner = owner.NewOwnerManager(do.etcdClient, statistics.StatsPrompt, id, statistics.StatsOwnerKey, cancelFunc) | ||
} | ||
err := statsOwner.CampaignOwner(cancelCtx) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The network between PD and TiDB is unstable
LGTM |
@zimulala Resolved. |
This PR implements the auto analyze.
It uses the
OwnerManager
to choose the stats owner, and only the stats owner can do auto analyze.It will only auto analyze for the newly created table and index. For the table, only after
20*statsLease
with no insert, the analyze operation will be triggered. For the index, we will analyze immediately after we know it's existence.To reduce the impact on the system, it will only analyze only one table or index in one time. Also, the priority of the
analyze
is set to low priority.PTAL @hanfei1991 @winoros