Skip to content

Commit da6a40c

Browse files
Merge pull request #85 from github/arthur/backport-tablesize-perf
Backport schema reload performance fix
2 parents 1b7e877 + 74806d0 commit da6a40c

File tree

8 files changed

+82
-18
lines changed

8 files changed

+82
-18
lines changed

go/mysql/flavor.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@ type flavor interface {
155155
enableBinlogPlaybackCommand() string
156156
disableBinlogPlaybackCommand() string
157157

158+
baseShowTables() string
158159
baseShowTablesWithSizes() string
159160

160161
supportsCapability(serverVersion string, capability FlavorCapability) (bool, error)
@@ -571,8 +572,13 @@ func (c *Conn) DisableBinlogPlaybackCommand() string {
571572
return c.flavor.disableBinlogPlaybackCommand()
572573
}
573574

574-
// BaseShowTables returns a query that shows tables and their sizes
575+
// BaseShowTables returns a query that shows tables
575576
func (c *Conn) BaseShowTables() string {
577+
return c.flavor.baseShowTables()
578+
}
579+
580+
// BaseShowTablesWithSizes returns a query that shows tables and their sizes
581+
func (c *Conn) BaseShowTablesWithSizes() string {
576582
return c.flavor.baseShowTablesWithSizes()
577583
}
578584

go/mysql/flavor_filepos.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,11 @@ func (*filePosFlavor) disableBinlogPlaybackCommand() string {
326326
return ""
327327
}
328328

329+
// baseShowTables is part of the Flavor interface.
330+
func (*filePosFlavor) baseShowTables() string {
331+
return mysqlFlavor{}.baseShowTables()
332+
}
333+
329334
// baseShowTablesWithSizes is part of the Flavor interface.
330335
func (*filePosFlavor) baseShowTablesWithSizes() string {
331336
return TablesWithSize56

go/mysql/flavor_mariadb_binlog_playback.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,11 @@ func (mariadbFlavor) disableBinlogPlaybackCommand() string {
3030
return ""
3131
}
3232

33+
// baseShowTables is part of the Flavor interface.
34+
func (mariadbFlavor) baseShowTables() string {
35+
return mysqlFlavor{}.baseShowTables()
36+
}
37+
3338
// baseShowTablesWithSizes is part of the Flavor interface.
3439
func (mariadbFlavor101) baseShowTablesWithSizes() string {
3540
return TablesWithSize56

go/mysql/flavor_mysql.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,11 @@ func (mysqlFlavor) disableBinlogPlaybackCommand() string {
308308
return ""
309309
}
310310

311+
// baseShowTables is part of the Flavor interface.
312+
func (mysqlFlavor) baseShowTables() string {
313+
return "SELECT table_name, table_type, unix_timestamp(create_time), table_comment FROM information_schema.tables WHERE table_schema = database()"
314+
}
315+
311316
// TablesWithSize56 is a query to select table along with size for mysql 5.6
312317
const TablesWithSize56 = `SELECT table_name, table_type, unix_timestamp(create_time), table_comment, SUM( data_length + index_length), SUM( data_length + index_length)
313318
FROM information_schema.tables WHERE table_schema = database() group by table_name`

go/mysql/flavor_mysqlgr.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,10 @@ func (mysqlGRFlavor) primaryStatus(c *Conn) (PrimaryStatus, error) {
239239
return mysqlFlavor{}.primaryStatus(c)
240240
}
241241

242+
func (mysqlGRFlavor) baseShowTables() string {
243+
return mysqlFlavor{}.baseShowTables()
244+
}
245+
242246
func (mysqlGRFlavor) baseShowTablesWithSizes() string {
243247
return TablesWithSize80
244248
}

go/vt/vttablet/tabletserver/connpool/dbconn.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -442,11 +442,16 @@ func (dbc *DBConn) ID() int64 {
442442
return dbc.conn.ID()
443443
}
444444

445-
// BaseShowTables returns a query that shows tables and their sizes
445+
// BaseShowTables returns a query that shows tables
446446
func (dbc *DBConn) BaseShowTables() string {
447447
return dbc.conn.BaseShowTables()
448448
}
449449

450+
// BaseShowTablesWithSizes returns a query that shows tables and their sizes
451+
func (dbc *DBConn) BaseShowTablesWithSizes() string {
452+
return dbc.conn.BaseShowTablesWithSizes()
453+
}
454+
450455
func (dbc *DBConn) reconnect(ctx context.Context) error {
451456
dbc.conn.Close()
452457
// Reuse MySQLTimings from dbc.conn.

go/vt/vttablet/tabletserver/query_executor.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -510,7 +510,15 @@ func (qre *QueryExecutor) execDDL(conn *StatefulConnection) (*sqltypes.Result, e
510510
}
511511

512512
defer func() {
513-
if err := qre.tsv.se.Reload(qre.ctx); err != nil {
513+
// Call se.Reload() with includeStats=false as obtaining table
514+
// size stats involves joining `information_schema.tables`,
515+
// which can be very costly on systems with a large number of
516+
// tables.
517+
//
518+
// Instead of synchronously recalculating table size stats
519+
// after every DDL, let them be outdated until the periodic
520+
// schema reload fixes it.
521+
if err := qre.tsv.se.ReloadAtEx(qre.ctx, mysql.Position{}, false); err != nil {
514522
log.Errorf("failed to reload schema %v", err)
515523
}
516524
}()

go/vt/vttablet/tabletserver/schema/engine.go

Lines changed: 41 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ func (se *Engine) Open() error {
194194
}
195195
se.notifiers = make(map[string]notifier)
196196

197-
if err := se.reload(ctx); err != nil {
197+
if err := se.reload(ctx, true); err != nil {
198198
return err
199199
}
200200
if !se.SkipMetaCheck {
@@ -285,6 +285,8 @@ func (se *Engine) EnableHistorian(enabled bool) error {
285285

286286
// Reload reloads the schema info from the db.
287287
// Any tables that have changed since the last load are updated.
288+
// The includeStats argument controls whether table size statistics should be
289+
// emitted, as they can be expensive to calculate for a large number of tables
288290
func (se *Engine) Reload(ctx context.Context) error {
289291
return se.ReloadAt(ctx, mysql.Position{})
290292
}
@@ -294,25 +296,35 @@ func (se *Engine) Reload(ctx context.Context) error {
294296
// It maintains the position at which the schema was reloaded and if the same position is provided
295297
// (say by multiple vstreams) it returns the cached schema. In case of a newer or empty pos it always reloads the schema
296298
func (se *Engine) ReloadAt(ctx context.Context, pos mysql.Position) error {
299+
return se.ReloadAtEx(ctx, pos, true)
300+
}
301+
302+
// ReloadAtEx reloads the schema info from the db.
303+
// Any tables that have changed since the last load are updated.
304+
// It maintains the position at which the schema was reloaded and if the same position is provided
305+
// (say by multiple vstreams) it returns the cached schema. In case of a newer or empty pos it always reloads the schema
306+
// The includeStats argument controls whether table size statistics should be
307+
// emitted, as they can be expensive to calculate for a large number of tables
308+
func (se *Engine) ReloadAtEx(ctx context.Context, pos mysql.Position, includeStats bool) error {
297309
se.mu.Lock()
298310
defer se.mu.Unlock()
299311
if !se.isOpen {
300312
log.Warning("Schema reload called for an engine that is not yet open")
301313
return nil
302314
}
303315
if !pos.IsZero() && se.reloadAtPos.AtLeast(pos) {
304-
log.V(2).Infof("ReloadAt: found cached schema at %s", mysql.EncodePosition(pos))
316+
log.V(2).Infof("ReloadAtEx: found cached schema at %s", mysql.EncodePosition(pos))
305317
return nil
306318
}
307-
if err := se.reload(ctx); err != nil {
319+
if err := se.reload(ctx, includeStats); err != nil {
308320
return err
309321
}
310322
se.reloadAtPos = pos
311323
return nil
312324
}
313325

314326
// reload reloads the schema. It can also be used to initialize it.
315-
func (se *Engine) reload(ctx context.Context) error {
327+
func (se *Engine) reload(ctx context.Context, includeStats bool) error {
316328
defer func() {
317329
se.env.LogError()
318330
}()
@@ -332,7 +344,14 @@ func (se *Engine) reload(ctx context.Context) error {
332344
if se.SkipMetaCheck {
333345
return nil
334346
}
335-
tableData, err := conn.Exec(ctx, conn.BaseShowTables(), maxTableCount, false)
347+
348+
var showTablesQuery string
349+
if includeStats {
350+
showTablesQuery = conn.BaseShowTablesWithSizes()
351+
} else {
352+
showTablesQuery = conn.BaseShowTables()
353+
}
354+
tableData, err := conn.Exec(ctx, showTablesQuery, maxTableCount, false)
336355
if err != nil {
337356
return err
338357
}
@@ -353,12 +372,15 @@ func (se *Engine) reload(ctx context.Context) error {
353372
tableName := row[0].ToString()
354373
curTables[tableName] = true
355374
createTime, _ := evalengine.ToInt64(row[2])
356-
fileSize, _ := evalengine.ToUint64(row[4])
357-
allocatedSize, _ := evalengine.ToUint64(row[5])
358-
359-
// publish the size metrics
360-
se.tableFileSizeGauge.Set(tableName, int64(fileSize))
361-
se.tableAllocatedSizeGauge.Set(tableName, int64(allocatedSize))
375+
var fileSize, allocatedSize uint64
376+
377+
if includeStats {
378+
fileSize, _ = evalengine.ToUint64(row[4])
379+
allocatedSize, _ = evalengine.ToUint64(row[5])
380+
// publish the size metrics
381+
se.tableFileSizeGauge.Set(tableName, int64(fileSize))
382+
se.tableAllocatedSizeGauge.Set(tableName, int64(allocatedSize))
383+
}
362384

363385
// Table schemas are cached by tabletserver. For each table we cache `information_schema.tables.create_time` (`tbl.CreateTime`).
364386
// We also record the last time the schema was loaded (`se.lastChange`). Both are in seconds. We reload a table only when:
@@ -372,8 +394,10 @@ func (se *Engine) reload(ctx context.Context) error {
372394
// #1 will not identify the renamed table as a changed one.
373395
tbl, isInTablesMap := se.tables[tableName]
374396
if isInTablesMap && createTime == tbl.CreateTime && createTime < se.lastChange {
375-
tbl.FileSize = fileSize
376-
tbl.AllocatedSize = allocatedSize
397+
if includeStats {
398+
tbl.FileSize = fileSize
399+
tbl.AllocatedSize = allocatedSize
400+
}
377401
continue
378402
}
379403

@@ -389,8 +413,10 @@ func (se *Engine) reload(ctx context.Context) error {
389413
rec.RecordError(vterrors.Wrapf(err, "in Engine.reload(), reading table %s", tableName))
390414
continue
391415
}
392-
table.FileSize = fileSize
393-
table.AllocatedSize = allocatedSize
416+
if includeStats {
417+
table.FileSize = fileSize
418+
table.AllocatedSize = allocatedSize
419+
}
394420
table.CreateTime = createTime
395421
changedTables[tableName] = table
396422
if isInTablesMap {

0 commit comments

Comments
 (0)