Skip to content

Commit

Permalink
*: improve performance of show variables (pingcap#5297)
Browse files Browse the repository at this point in the history
  • Loading branch information
XuHuaiyu authored and coocood committed Dec 5, 2017
1 parent 6e85171 commit 714b68c
Show file tree
Hide file tree
Showing 7 changed files with 98 additions and 25 deletions.
2 changes: 1 addition & 1 deletion context/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ func (t basicCtxType) String() string {
const (
// QueryString is the key for original query string.
QueryString basicCtxType = 1
// Initing is the key for indicating if the server is running bootstrap or upgrad job.
// Initing is the key for indicating if the server is running bootstrap or upgrade job.
Initing basicCtxType = 2
// LastExecuteDDL is the key for whether the session execute a ddl command last time.
LastExecuteDDL basicCtxType = 3
Expand Down
41 changes: 34 additions & 7 deletions executor/show.go
Original file line number Diff line number Diff line change
Expand Up @@ -370,23 +370,50 @@ func (e *ShowExec) fetchShowCharset() error {
return nil
}

func (e *ShowExec) fetchShowVariables() error {
sessionVars := e.ctx.GetSessionVars()
func (e *ShowExec) fetchShowVariables() (err error) {
var (
value string
ok bool
sessionVars = e.ctx.GetSessionVars()
unreachedVars = make([]string, 0, len(variable.SysVars))
)
for _, v := range variable.SysVars {
var err error
var value string
if !e.GlobalScope {
// Try to get Session Scope variable value first.
value, err = varsutil.GetSessionSystemVar(sessionVars, v.Name)
// For a session scope variable,
// 1. try to fetch value from SessionVars.Systems;
// 2. if this variable is session-only, fetch value from SysVars
// otherwise, fetch the value from table `mysql.Global_Variables`.
value, ok, err = varsutil.GetSessionOnlySysVars(sessionVars, v.Name)
} else {
value, err = varsutil.GetGlobalSystemVar(sessionVars, v.Name)
// If the scope of a system variable is ScopeNone,
// it's a read-only variable, so we return the default value of it.
// Otherwise, we have to fetch the values from table `mysql.Global_Variables` for global variable names.
value, ok, err = varsutil.GetScopeNoneSystemVar(v.Name)
}
if err != nil {
return errors.Trace(err)
}
if !ok {
unreachedVars = append(unreachedVars, v.Name)
continue
}
row := types.MakeDatums(v.Name, value)
e.rows = append(e.rows, row)
}
if len(unreachedVars) != 0 {
systemVars, err := sessionVars.GlobalVarsAccessor.GetAllSysVars()
if err != nil {
return errors.Trace(err)
}
for _, varName := range unreachedVars {
varValue, ok := systemVars[varName]
if !ok {
varValue = variable.SysVars[varName].Value
}
row := types.MakeDatums(varName, varValue)
e.rows = append(e.rows, row)
}
}
return nil
}

Expand Down
4 changes: 2 additions & 2 deletions new_session_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -292,9 +292,9 @@ func (s *testSessionSuite) TestGlobalVarAccessor(c *C) {
result = tk.MustQuery("show session variables where variable_name='sql_select_limit';")
result.Check(testkit.Rows("sql_select_limit 18446744073709551615"))
tk.MustExec("set session sql_select_limit=100000000000;")
result = tk.MustQuery("show global variables where variable_name='sql_select_limit';")
result = tk.MustQuery("show global variables where variable_name='sql_select_limit';")
result.Check(testkit.Rows("sql_select_limit 18446744073709551615"))
result = tk.MustQuery("show session variables where variable_name='sql_select_limit';")
result = tk.MustQuery("show session variables where variable_name='sql_select_limit';")
result.Check(testkit.Rows("sql_select_limit 100000000000"))
}

Expand Down
19 changes: 19 additions & 0 deletions session.go
Original file line number Diff line number Diff line change
Expand Up @@ -594,6 +594,25 @@ func (s *session) getExecRet(ctx context.Context, sql string) (string, error) {
return value, nil
}

// GetAllSysVars implements GlobalVarAccessor.GetAllSysVars interface.
func (s *session) GetAllSysVars() (map[string]string, error) {
if s.Value(context.Initing) != nil {
return nil, nil
}
sql := `SELECT VARIABLE_NAME, VARIABLE_VALUE FROM %s.%s;`
sql = fmt.Sprintf(sql, mysql.SystemDB, mysql.GlobalVariablesTable)
rows, _, err := s.ExecRestrictedSQL(s, sql)
if err != nil {
return nil, errors.Trace(err)
}
ret := make(map[string]string)
for _, r := range rows {
k, v := r.GetString(0), r.GetString(1)
ret[k] = v
}
return ret, nil
}

// GetGlobalSysVar implements GlobalVarAccessor.GetGlobalSysVar interface.
func (s *session) GetGlobalSysVar(name string) (string, error) {
if s.Value(context.Initing) != nil {
Expand Down
2 changes: 2 additions & 0 deletions sessionctx/variable/sysvar.go
Original file line number Diff line number Diff line change
Expand Up @@ -640,6 +640,8 @@ const (

// GlobalVarAccessor is the interface for accessing global scope system and status variables.
type GlobalVarAccessor interface {
// GetAllSysVars gets all the global system variable values.
GetAllSysVars() (map[string]string, error)
// GetGlobalSysVar gets the global system variable value for name.
GetGlobalSysVar(name string) (string, error)
// SetGlobalSysVar sets the global system variable name to value.
Expand Down
51 changes: 36 additions & 15 deletions sessionctx/varsutil/varsutil.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,46 +30,67 @@ import (
// Returns error if there is no such variable.
func GetSessionSystemVar(s *variable.SessionVars, key string) (string, error) {
key = strings.ToLower(key)
gVal, ok, err := GetSessionOnlySysVars(s, key)
if err != nil || ok {
return gVal, errors.Trace(err)
}
gVal, err = s.GlobalVarsAccessor.GetGlobalSysVar(key)
if err != nil {
return "", errors.Trace(err)
}
s.Systems[key] = gVal
return gVal, nil
}

// GetSessionOnlySysVars get the default value defined in code for session only variable.
// The return bool value indicates whether it's a session only variable.
func GetSessionOnlySysVars(s *variable.SessionVars, key string) (string, bool, error) {
sysVar := variable.SysVars[key]
if sysVar == nil {
return "", variable.UnknownSystemVar.GenByArgs(key)
return "", false, variable.UnknownSystemVar.GenByArgs(key)
}
// For virtual system variables:
switch sysVar.Name {
case variable.TiDBCurrentTS:
return fmt.Sprintf("%d", s.TxnCtx.StartTS), nil
return fmt.Sprintf("%d", s.TxnCtx.StartTS), true, nil
}
sVal, ok := s.Systems[key]
if ok {
return sVal, nil
return sVal, true, nil
}
if sysVar.Scope&variable.ScopeGlobal == 0 {
// None-Global variable can use pre-defined default value.
return sysVar.Value, nil
return sysVar.Value, true, nil
}
gVal, err := s.GlobalVarsAccessor.GetGlobalSysVar(key)
return "", false, nil
}

// GetGlobalSystemVar gets a global system variable.
func GetGlobalSystemVar(s *variable.SessionVars, key string) (string, error) {
key = strings.ToLower(key)
gVal, ok, err := GetScopeNoneSystemVar(key)
if err != nil || ok {
return gVal, errors.Trace(err)
}
gVal, err = s.GlobalVarsAccessor.GetGlobalSysVar(key)
if err != nil {
return "", errors.Trace(err)
}
s.Systems[key] = gVal
return gVal, nil
}

// GetGlobalSystemVar gets a global system variable.
func GetGlobalSystemVar(s *variable.SessionVars, key string) (string, error) {
key = strings.ToLower(key)
// GetScopeNoneSystemVar checks the validation of `key`,
// and return the default value if its scope is `ScopeNone`.
func GetScopeNoneSystemVar(key string) (string, bool, error) {
sysVar := variable.SysVars[key]
if sysVar == nil {
return "", variable.UnknownSystemVar.GenByArgs(key)
return "", false, variable.UnknownSystemVar.GenByArgs(key)
}
if sysVar.Scope == variable.ScopeNone {
return sysVar.Value, nil
return sysVar.Value, true, nil
}
gVal, err := s.GlobalVarsAccessor.GetGlobalSysVar(key)
if err != nil {
return "", errors.Trace(err)
}
return gVal, nil
return "", false, nil
}

// epochShiftBits is used to reserve logical part of the timestamp.
Expand Down
4 changes: 4 additions & 0 deletions sessionctx/varsutil/varsutil_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -189,3 +189,7 @@ func (m *mockGlobalAccessor) SetGlobalSysVar(name string, value string) error {
m.vars[name] = value
return nil
}

func (m *mockGlobalAccessor) GetAllSysVars() (map[string]string, error) {
return m.vars, nil
}

0 comments on commit 714b68c

Please sign in to comment.