github.com/XiaoMi/Gaea@v1.2.5/proxy/server/namespace.go (about) 1 // Copyright 2019 The Gaea Authors. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package server 16 17 import ( 18 "errors" 19 "fmt" 20 "net" 21 "strconv" 22 "strings" 23 "time" 24 25 "github.com/XiaoMi/Gaea/backend" 26 "github.com/XiaoMi/Gaea/log" 27 "github.com/XiaoMi/Gaea/models" 28 "github.com/XiaoMi/Gaea/mysql" 29 "github.com/XiaoMi/Gaea/proxy/plan" 30 "github.com/XiaoMi/Gaea/proxy/router" 31 "github.com/XiaoMi/Gaea/proxy/sequence" 32 "github.com/XiaoMi/Gaea/util" 33 "github.com/XiaoMi/Gaea/util/cache" 34 ) 35 36 const ( 37 namespaceDelayClose = 60 38 ) 39 40 const ( 41 defaultSQLCacheCapacity = 64 42 defaultPlanCacheCapacity = 128 43 44 defaultSlowSQLTime = 1000 // millisecond 45 defaultMaxSqlExecuteTime = 0 // 默认为0,不开启慢sql熔断功能 46 defaultMaxSqlResultSize = 10000 // 默认为10000, 限制查询返回的结果集大小不超过该阈值 47 ) 48 49 // UserProperty means runtime user properties 50 type UserProperty struct { 51 RWFlag int 52 RWSplit int 53 OtherProperty int 54 } 55 56 // Namespace is struct driected used by server 57 type Namespace struct { 58 name string 59 allowedDBs map[string]bool 60 defaultPhyDBs map[string]string // logicDBName-phyDBName 61 sqls map[string]string //key: sql fingerprint 62 slowSQLTime int64 // session slow sql time, millisecond, default 1000 63 allowips []util.IPInfo 64 router *router.Router 65 sequences *sequence.SequenceManager 66 slices map[string]*backend.Slice // key: slice name 67 userProperties map[string]*UserProperty // key: user name ,value: user's properties 68 defaultCharset string 69 defaultCollationID mysql.CollationID 70 openGeneralLog bool 71 maxSqlExecuteTime int // session max sql execute time,millisecond 72 maxSqlResultSize int 73 defaultSlice string 74 75 slowSQLCache *cache.LRUCache 76 errorSQLCache *cache.LRUCache 77 backendSlowSQLCache *cache.LRUCache 78 backendErrorSQLCache *cache.LRUCache 79 planCache *cache.LRUCache 80 } 81 82 // DumpToJSON means easy encode json 83 func (n *Namespace) DumpToJSON() []byte { 84 return models.JSONEncode(n) 85 } 86 87 // NewNamespace init namespace 88 func NewNamespace(namespaceConfig *models.Namespace) (*Namespace, error) { 89 var err error 90 namespace := &Namespace{ 91 name: namespaceConfig.Name, 92 sqls: make(map[string]string, 16), 93 userProperties: make(map[string]*UserProperty, 2), 94 openGeneralLog: namespaceConfig.OpenGeneralLog, 95 slowSQLCache: cache.NewLRUCache(defaultSQLCacheCapacity), 96 errorSQLCache: cache.NewLRUCache(defaultSQLCacheCapacity), 97 backendSlowSQLCache: cache.NewLRUCache(defaultSQLCacheCapacity), 98 backendErrorSQLCache: cache.NewLRUCache(defaultSQLCacheCapacity), 99 planCache: cache.NewLRUCache(defaultPlanCacheCapacity), 100 defaultSlice: namespaceConfig.DefaultSlice, 101 } 102 103 defer func() { 104 if err != nil { 105 namespace.Close(false) 106 } 107 }() 108 109 // init black sql 110 namespace.sqls = parseBlackSqls(namespaceConfig.BlackSQL) 111 112 // init session slow sql time 113 namespace.slowSQLTime, err = parseSlowSQLTime(namespaceConfig.SlowSQLTime) 114 if err != nil { 115 return nil, fmt.Errorf("parse slowSQLTime error: %v", err) 116 } 117 118 // init session slow sql max execute time 119 if namespaceConfig.MaxSqlExecuteTime <= 0 { 120 namespace.maxSqlExecuteTime = defaultMaxSqlExecuteTime 121 } else { 122 namespace.maxSqlExecuteTime = namespaceConfig.MaxSqlExecuteTime 123 } 124 125 // init session slow sql max result size 126 if namespaceConfig.MaxSqlResultSize <= 0 && namespaceConfig.MaxSqlResultSize != -1 { 127 namespace.maxSqlResultSize = defaultMaxSqlResultSize 128 } else { 129 namespace.maxSqlResultSize = namespaceConfig.MaxSqlResultSize 130 } 131 132 allowDBs := make(map[string]bool, len(namespaceConfig.AllowedDBS)) 133 for db, allowed := range namespaceConfig.AllowedDBS { 134 allowDBs[strings.TrimSpace(db)] = allowed 135 } 136 namespace.allowedDBs = allowDBs 137 138 defaultPhyDBs := make(map[string]string, len(namespaceConfig.DefaultPhyDBS)) 139 for db, phyDB := range namespaceConfig.DefaultPhyDBS { 140 defaultPhyDBs[strings.TrimSpace(db)] = strings.TrimSpace(phyDB) 141 } 142 143 namespace.defaultPhyDBs, err = parseDefaultPhyDB(defaultPhyDBs, allowDBs) 144 if err != nil { 145 return nil, fmt.Errorf("parse defaultPhyDBs error: %v", err) 146 } 147 148 // init allow ip 149 allowips, err := parseAllowIps(namespaceConfig.AllowedIP) 150 if err != nil { 151 return nil, fmt.Errorf("parse allowips error: %v", err) 152 } 153 namespace.allowips = allowips 154 155 namespace.defaultCharset, namespace.defaultCollationID, err = parseCharset(namespaceConfig.DefaultCharset, namespaceConfig.DefaultCollation) 156 if err != nil { 157 return nil, fmt.Errorf("parse charset error: %v", err) 158 } 159 160 // init user properties 161 for _, user := range namespaceConfig.Users { 162 up := &UserProperty{RWFlag: user.RWFlag, RWSplit: user.RWSplit, OtherProperty: user.OtherProperty} 163 namespace.userProperties[user.UserName] = up 164 } 165 166 // init backend slices 167 namespace.slices, err = parseSlices(namespaceConfig.Slices, namespace.defaultCharset, namespace.defaultCollationID) 168 if err != nil { 169 return nil, fmt.Errorf("init slices of namespace: %s failed, err: %v", namespaceConfig.Name, err) 170 } 171 172 // init router 173 namespace.router, err = router.NewRouter(namespaceConfig) 174 if err != nil { 175 return nil, fmt.Errorf("init router of namespace: %s failed, err: %v", namespace.name, err) 176 } 177 178 // init global sequences config 179 // 目前只支持基于mysql的序列号 180 sequences := sequence.NewSequenceManager() 181 for _, v := range namespaceConfig.GlobalSequences { 182 globalSequenceSlice, ok := namespace.slices[v.SliceName] 183 if !ok { 184 return nil, fmt.Errorf("init global sequence error: slice not found, sequence: %v", v) 185 } 186 seqName := strings.ToUpper(v.DB) + "." + strings.ToUpper(v.Table) 187 seq := sequence.NewMySQLSequence(globalSequenceSlice, seqName, v.PKName) 188 sequences.SetSequence(v.DB, v.Table, seq) 189 } 190 namespace.sequences = sequences 191 192 return namespace, nil 193 } 194 195 // GetName return namespace of namespace 196 func (n *Namespace) GetName() string { 197 return n.name 198 } 199 200 // GetSlice return slice of namespace 201 func (n *Namespace) GetSlice(name string) *backend.Slice { 202 return n.slices[name] 203 } 204 205 // GetRouter return router of namespace 206 func (n *Namespace) GetRouter() *router.Router { 207 return n.router 208 } 209 210 func (n *Namespace) GetSequences() *sequence.SequenceManager { 211 return n.sequences 212 } 213 214 // IsClientIPAllowed check ip 215 func (n *Namespace) IsClientIPAllowed(clientIP net.IP) bool { 216 if len(n.allowips) == 0 { 217 return true 218 } 219 for _, ip := range n.allowips { 220 if ip.Match(clientIP) { 221 return true 222 } 223 } 224 return false 225 } 226 227 func (n *Namespace) getSessionSlowSQLTime() int64 { 228 return n.slowSQLTime 229 } 230 231 // IsAllowWrite check if user allow to write 232 func (n *Namespace) IsAllowWrite(user string) bool { 233 return n.userProperties[user].RWFlag == models.ReadWrite 234 } 235 236 // IsRWSplit chekc if read write split 237 func (n *Namespace) IsRWSplit(user string) bool { 238 return n.userProperties[user].RWSplit == models.ReadWriteSplit 239 } 240 241 // IsStatisticUser check if user is used to statistic 242 func (n *Namespace) IsStatisticUser(user string) bool { 243 return n.userProperties[user].OtherProperty == models.StatisticUser 244 } 245 246 // GetUserProperty return user information 247 func (n *Namespace) GetUserProperty(user string) int { 248 return n.userProperties[user].OtherProperty 249 } 250 251 func (n *Namespace) GetMaxExecuteTime() int { 252 return n.maxSqlExecuteTime 253 } 254 255 func (n *Namespace) GetMaxResultSize() int { 256 return n.maxSqlResultSize 257 } 258 259 // IsSQLAllowed check black sql 260 func (n *Namespace) IsSQLAllowed(reqCtx *util.RequestContext, sql string) bool { 261 if len(n.sqls) == 0 { 262 return true 263 } 264 265 fingerprint := mysql.GetFingerprint(sql) 266 reqCtx.Set("fingerprint", fingerprint) 267 md5 := mysql.GetMd5(fingerprint) 268 if _, ok := n.sqls[md5]; ok { 269 return false 270 } 271 272 return true 273 } 274 275 // IsAllowedDB if allowed database 276 func (n *Namespace) IsAllowedDB(dbname string) bool { 277 allowed, ok := n.allowedDBs[dbname] 278 return ok && allowed 279 } 280 281 // GetAllowedDBs return all allowed databases 282 func (n *Namespace) GetAllowedDBs() []string { 283 var ret []string 284 for db := range n.allowedDBs { 285 ret = append(ret, db) 286 } 287 return ret 288 } 289 290 // GetDefaultPhyDB return default real database 291 func (n *Namespace) GetDefaultPhyDB(dbname string) (string, error) { 292 if dbname == "" { 293 return "", nil 294 } 295 phyDB, ok := n.defaultPhyDBs[dbname] 296 if !ok { 297 return "", fmt.Errorf("invalid db %s", dbname) 298 } 299 return phyDB, nil 300 } 301 302 func (n *Namespace) GetPhysicalDBs() map[string]string { 303 return n.defaultPhyDBs 304 } 305 306 // GetDefaultCharset return default charset 307 func (n *Namespace) GetDefaultCharset() string { 308 return n.defaultCharset 309 } 310 311 func (n *Namespace) GetDefaultSlice() string { 312 return n.defaultSlice 313 } 314 315 // GetDefaultCollationID return default collation id 316 func (n *Namespace) GetDefaultCollationID() mysql.CollationID { 317 return n.defaultCollationID 318 } 319 320 // GetCachedPlan get plan in cache 321 func (n *Namespace) GetCachedPlan(db, sql string) (plan.Plan, bool) { 322 v, ok := n.planCache.Get(db + "|" + sql) 323 if !ok { 324 return nil, false 325 } 326 return v.(plan.Plan), true 327 } 328 329 // SetCachedPlan set plan in cache 330 func (n *Namespace) SetCachedPlan(db, sql string, p plan.Plan) { 331 n.planCache.SetIfAbsent(db+"|"+sql, p) 332 } 333 334 // SetSlowSQLFingerprint store slow sql fingerprint 335 func (n *Namespace) SetSlowSQLFingerprint(md5, fingerprint string) { 336 n.slowSQLCache.Set(md5, cache.CachedString(fingerprint)) 337 } 338 339 // GetSlowSQLFingerprint return slow sql fingerprint 340 func (n *Namespace) GetSlowSQLFingerprint(md5 string) (string, bool) { 341 v, ok := n.slowSQLCache.Get(md5) 342 if !ok { 343 return "", false 344 } 345 return string(v.(cache.CachedString)), true 346 } 347 348 // GetSlowSQLFingerprints return slow sql fingerprints 349 func (n *Namespace) GetSlowSQLFingerprints() map[string]string { 350 ret := make(map[string]string) 351 items := n.slowSQLCache.Items() 352 for _, item := range items { 353 ret[item.Key] = string(item.Value.(cache.CachedString)) 354 } 355 return ret 356 } 357 358 // ClearSlowSQLFingerprints clear all slow sql fingerprints 359 func (n *Namespace) ClearSlowSQLFingerprints() { 360 n.slowSQLCache.Clear() 361 } 362 363 // SetErrorSQLFingerprint store error sql fingerprint 364 func (n *Namespace) SetErrorSQLFingerprint(md5, fingerprint string) { 365 n.errorSQLCache.Set(md5, cache.CachedString(fingerprint)) 366 } 367 368 // GetErrorSQLFingerprint return error sql fingerprint 369 func (n *Namespace) GetErrorSQLFingerprint(md5 string) (string, bool) { 370 v, ok := n.errorSQLCache.Get(md5) 371 if !ok { 372 return "", false 373 } 374 return string(v.(cache.CachedString)), true 375 } 376 377 // GetErrorSQLFingerprints return all error sql fingerprints 378 func (n *Namespace) GetErrorSQLFingerprints() map[string]string { 379 ret := make(map[string]string) 380 items := n.errorSQLCache.Items() 381 for _, item := range items { 382 ret[item.Key] = string(item.Value.(cache.CachedString)) 383 } 384 return ret 385 } 386 387 // ClearErrorSQLFingerprints clear all error sql fingerprints 388 func (n *Namespace) ClearErrorSQLFingerprints() { 389 n.errorSQLCache.Clear() 390 } 391 392 // SetBackendSlowSQLFingerprint store backend slow sql fingerprint 393 func (n *Namespace) SetBackendSlowSQLFingerprint(md5, fingerprint string) { 394 n.backendSlowSQLCache.Set(md5, cache.CachedString(fingerprint)) 395 } 396 397 // GetBackendSlowSQLFingerprint return backend slow sql fingerprint 398 func (n *Namespace) GetBackendSlowSQLFingerprint(md5 string) (string, bool) { 399 v, ok := n.backendSlowSQLCache.Get(md5) 400 if !ok { 401 return "", false 402 } 403 return string(v.(cache.CachedString)), true 404 } 405 406 // GetBackendSlowSQLFingerprints return all backend slow sql fingerprints 407 func (n *Namespace) GetBackendSlowSQLFingerprints() map[string]string { 408 ret := make(map[string]string) 409 items := n.backendSlowSQLCache.Items() 410 for _, item := range items { 411 ret[item.Key] = string(item.Value.(cache.CachedString)) 412 } 413 return ret 414 } 415 416 // ClearBackendSlowSQLFingerprints clear all backend slow sql fingerprints 417 func (n *Namespace) ClearBackendSlowSQLFingerprints() { 418 n.backendSlowSQLCache.Clear() 419 } 420 421 // SetBackendErrorSQLFingerprint store backend error sql fingerprint 422 func (n *Namespace) SetBackendErrorSQLFingerprint(md5, fingerprint string) { 423 n.backendErrorSQLCache.Set(md5, cache.CachedString(fingerprint)) 424 } 425 426 // GetBackendErrorSQLFingerprint return backedn error sql fingerprint 427 func (n *Namespace) GetBackendErrorSQLFingerprint(md5 string) (string, bool) { 428 v, ok := n.backendErrorSQLCache.Get(md5) 429 if !ok { 430 return "", false 431 } 432 return string(v.(cache.CachedString)), true 433 } 434 435 // GetBackendErrorSQLFingerprints return all backend error sql fingerprints 436 func (n *Namespace) GetBackendErrorSQLFingerprints() map[string]string { 437 ret := make(map[string]string) 438 items := n.backendErrorSQLCache.Items() 439 for _, item := range items { 440 ret[item.Key] = string(item.Value.(cache.CachedString)) 441 } 442 return ret 443 } 444 445 // ClearBackendErrorSQLFingerprints clear all backend error sql fingerprints 446 func (n *Namespace) ClearBackendErrorSQLFingerprints() { 447 n.backendErrorSQLCache.Clear() 448 } 449 450 // Close recycle resources of namespace 451 func (n *Namespace) Close(delay bool) { 452 var err error 453 // delay close time 454 if delay { 455 time.Sleep(time.Second * namespaceDelayClose) 456 } 457 for k := range n.slices { 458 err = n.slices[k].Close() 459 if err != nil { 460 log.Warn("delay close slice: %s failed, err: %v", k, err) 461 continue 462 } 463 } 464 n.slowSQLCache.Clear() 465 n.errorSQLCache.Clear() 466 n.backendSlowSQLCache.Clear() 467 n.backendErrorSQLCache.Clear() 468 } 469 470 func parseSlice(cfg *models.Slice, charset string, collationID mysql.CollationID) (*backend.Slice, error) { 471 var err error 472 s := new(backend.Slice) 473 s.Cfg = *cfg 474 s.SetCharsetInfo(charset, collationID) 475 476 // parse master 477 err = s.ParseMaster(cfg.Master) 478 if err != nil { 479 return nil, err 480 } 481 482 // parse slaves 483 err = s.ParseSlave(cfg.Slaves) 484 if err != nil { 485 return nil, err 486 } 487 488 // parse statistic slaves 489 err = s.ParseStatisticSlave(cfg.StatisticSlaves) 490 if err != nil { 491 return nil, err 492 } 493 494 return s, nil 495 } 496 497 func parseSlices(cfgSlices []*models.Slice, charset string, collationID mysql.CollationID) (map[string]*backend.Slice, error) { 498 slices := make(map[string]*backend.Slice, len(cfgSlices)) 499 for _, v := range cfgSlices { 500 v.Name = strings.TrimSpace(v.Name) // modify origin slice name, trim space 501 if _, ok := slices[v.Name]; ok { 502 return nil, fmt.Errorf("duplicate slice [%s]", v.Name) 503 } 504 505 s, err := parseSlice(v, charset, collationID) 506 if err != nil { 507 return nil, err 508 } 509 510 slices[v.Name] = s 511 } 512 513 return slices, nil 514 } 515 516 func parseAllowIps(allowedIP []string) ([]util.IPInfo, error) { 517 var allowips []util.IPInfo 518 for _, ipStr := range allowedIP { 519 ipStr = strings.TrimSpace(ipStr) 520 if len(ipStr) == 0 { 521 continue 522 } 523 ipInfo, err := util.ParseIPInfo(ipStr) 524 if err != nil { 525 return nil, err 526 } 527 allowips = append(allowips, ipInfo) 528 } 529 return allowips, nil 530 } 531 532 func parseBlackSqls(sqls []string) map[string]string { 533 sqlMap := make(map[string]string, 10) 534 for _, sql := range sqls { 535 sql = strings.TrimSpace(sql) 536 if len(sql) == 0 { 537 continue 538 } 539 fingerprint := mysql.GetFingerprint(sql) 540 md5 := mysql.GetMd5(fingerprint) 541 sqlMap[md5] = fingerprint 542 } 543 return sqlMap 544 } 545 546 func parseSlowSQLTime(str string) (int64, error) { 547 if str == "" { 548 return defaultSlowSQLTime, nil 549 } 550 t, err := strconv.ParseInt(str, 10, 64) 551 if err != nil { 552 return 0, err 553 } 554 if t < 0 { 555 return 0, fmt.Errorf("less than zero") 556 } 557 558 return t, nil 559 } 560 561 func parseCharset(charset, collation string) (string, mysql.CollationID, error) { 562 if charset == "" && collation == "" { 563 return mysql.DefaultCharset, mysql.DefaultCollationID, nil 564 } 565 566 if collation == "" { 567 collationID, ok := mysql.CharsetIds[charset] 568 if !ok { 569 return "", 0, errors.New("invalid charset") 570 } 571 return charset, collationID, nil 572 } 573 574 if err := mysql.VerifyCharset(charset, collation); err != nil { 575 return "", 0, err 576 } 577 collationID, ok := mysql.CollationNames[collation] 578 if !ok { 579 return "", 0, errors.New("invalid collation") 580 } 581 582 return charset, collationID, nil 583 } 584 585 func parseDefaultPhyDB(defaultPhyDBs map[string]string, allowedDBs map[string]bool) (map[string]string, error) { 586 // no logic database mode 587 if len(defaultPhyDBs) == 0 { 588 result := make(map[string]string, len(allowedDBs)) 589 for db := range allowedDBs { 590 result[db] = db 591 } 592 return result, nil 593 } 594 595 // logic database mode 596 for db := range allowedDBs { 597 if _, ok := defaultPhyDBs[db]; !ok { 598 return nil, fmt.Errorf("db %s have no phy db", db) 599 } 600 } 601 return defaultPhyDBs, nil 602 }