Skip to content
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

Merged
merged 20 commits into from
Aug 18, 2017
Merged

*: implement auto analyze #4141

merged 20 commits into from
Aug 18, 2017

Conversation

alivxxx
Copy link
Contributor

@alivxxx alivxxx commented Aug 10, 2017

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

@alivxxx
Copy link
Contributor Author

alivxxx commented Aug 10, 2017

Currently, if the statsLease is 0, there will be no auto analyze. Wondering if it needs a separate parameter to control it.

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)
Copy link
Contributor

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.

}

// 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 {
Copy link
Contributor

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)
Copy link
Contributor

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)
Copy link
Contributor

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))
Copy link
Contributor

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.

@hanfei1991
Copy link
Member

@lamxTyler Can we write a owner pakcage independent of ddl package ?

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)
Copy link
Contributor

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.

@@ -260,10 +259,3 @@ func (m *ownerManager) watchOwner(ctx goctx.Context, etcdSession *concurrency.Se
}
}
}

func init() {
err := setManagerSessionTTL()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why remove this ?

Copy link
Contributor Author

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,
Copy link
Contributor

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,
Copy link
Contributor

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) {
Copy link
Contributor

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?

Copy link
Contributor Author

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 {
Copy link
Contributor

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?

Copy link
Contributor Author

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.

Copy link
Contributor

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?

Copy link
Contributor Author

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?

return true
}

// HandleAutoAnalyze analyze the newly created table or index.
Copy link
Contributor

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)
Copy link
Contributor

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?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Nope, currently every time we only analyze one table or index, to reduce the impact on the server.
  2. Only the log? It also analyzes the table.

Copy link
Contributor

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?

Copy link
Contributor Author

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?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gotcha.

@hanfei1991
Copy link
Member

@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"
Copy link
Contributor

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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why change this name?

Copy link
Contributor Author

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)
Copy link
Contributor

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 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. 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.
  2. 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.

Copy link
Contributor Author

@alivxxx alivxxx Aug 17, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. 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.
  2. 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.

Copy link
Member

@hanfei1991 hanfei1991 Aug 17, 2017

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.

Copy link
Contributor Author

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.

Copy link
Contributor Author

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.

@hanfei1991
Copy link
Member

LGTM

@hanfei1991 hanfei1991 added the status/LGT1 Indicates that a PR has LGTM 1. label Aug 17, 2017
owner/manager.go Outdated
@@ -256,9 +240,11 @@ func (m *ownerManager) watchOwner(ctx goctx.Context, etcdSession *concurrency.Se
}
}

func init() {
err := setManagerSessionTTL()
Copy link
Contributor

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.

Copy link
Contributor Author

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?

Copy link
Contributor

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)
Copy link
Contributor

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Like what?

Copy link
Contributor

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

Copy link
Contributor Author

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?

Copy link
Contributor

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.

Copy link
Contributor Author

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()
Copy link
Contributor

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.

Copy link
Contributor Author

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.

Copy link
Contributor

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 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this function useless?

Copy link
Contributor Author

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?

Copy link
Contributor

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)
Copy link
Contributor

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

@zimulala
Copy link
Contributor

LGTM
Please resolve the conflict. @lamxTyler

@zimulala zimulala added status/LGT2 Indicates that a PR has LGTM 2. and removed status/LGT1 Indicates that a PR has LGTM 1. labels Aug 18, 2017
zimulala
zimulala previously approved these changes Aug 18, 2017
@alivxxx
Copy link
Contributor Author

alivxxx commented Aug 18, 2017

@zimulala Resolved.

@alivxxx alivxxx merged commit 7c273c0 into master Aug 18, 2017
@alivxxx alivxxx deleted the xhb/auto branch August 18, 2017 10:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status/LGT2 Indicates that a PR has LGTM 2.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants