github.com/XiaoMi/Gaea@v1.2.5/proxy/router/shard_mycat.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 router
    16  
    17  import (
    18  	"bytes"
    19  	"errors"
    20  	"fmt"
    21  	"strconv"
    22  	"strings"
    23  
    24  	"github.com/emirpasic/gods/maps/treemap"
    25  	"github.com/XiaoMi/Gaea/util"
    26  	"github.com/XiaoMi/Gaea/util/hack"
    27  )
    28  
    29  // MycatPartitionModShard mycat route algorithm: PartitionByMod
    30  // take care: in Mycat, the key is parsed to a BigInteger, not int64.
    31  type MycatPartitionModShard struct {
    32  	ShardNum int
    33  }
    34  
    35  // NewMycatPartitionModShard constructor of MycatPartitionModShard
    36  func NewMycatPartitionModShard(shardNum int) *MycatPartitionModShard {
    37  	return &MycatPartitionModShard{
    38  		ShardNum: shardNum,
    39  	}
    40  }
    41  
    42  // FindForKey return result of calculated key
    43  func (m *MycatPartitionModShard) FindForKey(key interface{}) (int, error) {
    44  	h := hack.Abs(NumValue(key))
    45  	return int(h % int64(m.ShardNum)), nil
    46  }
    47  
    48  const (
    49  	// PartitionLength length of partition
    50  	PartitionLength = 1024
    51  	andValue        = PartitionLength - 1
    52  )
    53  
    54  // MycatPartitionLongShard mycat route algorithm: PartitionByLong
    55  type MycatPartitionLongShard struct {
    56  	shardNum  int
    57  	countStr  string
    58  	lengthStr string
    59  	count     []int
    60  	length    []int
    61  	segment   []int
    62  }
    63  
    64  // NewMycatPartitionLongShard constructor of MycatPartitionLongShard
    65  func NewMycatPartitionLongShard(shardNum int, partitionCount, partitionLength string) *MycatPartitionLongShard {
    66  	return &MycatPartitionLongShard{
    67  		shardNum:  shardNum,
    68  		countStr:  partitionCount,
    69  		lengthStr: partitionLength,
    70  		count:     make([]int, 0, 10),
    71  		length:    make([]int, 0, 10),
    72  		segment:   make([]int, 0, PartitionLength),
    73  	}
    74  }
    75  
    76  // Init init MycatPartitionLongShard
    77  func (m *MycatPartitionLongShard) Init() error {
    78  	countList, err := toIntArray(m.countStr)
    79  	if err != nil {
    80  		return err
    81  	}
    82  
    83  	lengthList, err := toIntArray(m.lengthStr)
    84  	if err != nil {
    85  		return err
    86  	}
    87  
    88  	countSize := len(countList)
    89  	lengthSize := len(lengthList)
    90  	if countSize != lengthSize {
    91  		return errors.New("error, check your scope & scopeLength definition")
    92  	}
    93  
    94  	segmentLength := 0
    95  	for i := 0; i < countSize; i++ {
    96  		segmentLength += countList[i]
    97  	}
    98  
    99  	if segmentLength != m.shardNum {
   100  		return errors.New("segmentLength is not equal to shardNum")
   101  	}
   102  
   103  	ai := make([]int, segmentLength+1)
   104  
   105  	index := 0
   106  	for i := 0; i < countSize; i++ {
   107  		for j := 0; j < countList[i]; j++ {
   108  			ai[index+1] = ai[index] + lengthList[i]
   109  			index++
   110  		}
   111  	}
   112  	if ai[len(ai)-1] != PartitionLength {
   113  		return errors.New("error, check your partitionScope definition")
   114  	}
   115  
   116  	segment := make([]int, PartitionLength)
   117  	for i := 1; i < len(ai); i++ {
   118  		for j := ai[i-1]; j < ai[i]; j++ {
   119  			segment[j] = i - 1
   120  		}
   121  	}
   122  
   123  	m.count = countList
   124  	m.length = lengthList
   125  	m.segment = segment
   126  	return nil
   127  }
   128  
   129  func toIntArray(str string) ([]int, error) {
   130  	str = strings.Replace(str, " ", "", -1)
   131  	strList := strings.Split(str, ",")
   132  	ret := make([]int, 0, len(strList))
   133  	for _, s := range strList {
   134  		num, err := strconv.Atoi(s)
   135  		if err != nil {
   136  			return ret, err
   137  		}
   138  		ret = append(ret, num)
   139  	}
   140  	return ret, nil
   141  }
   142  
   143  // FindForKey return MycatPartitionLongShard calculated result
   144  func (m *MycatPartitionLongShard) FindForKey(key interface{}) (int, error) {
   145  	h := NumValue(key)
   146  	return m.segment[int(h)&andValue], nil
   147  }
   148  
   149  // MycatPartitionStringShard mycat route algorithm: PartitionByString
   150  type MycatPartitionStringShard struct {
   151  	*MycatPartitionLongShard
   152  	hashSliceStr   string
   153  	hashSliceStart int
   154  	hashSliceEnd   int
   155  }
   156  
   157  // NewMycatPartitionStringShard constructor of MycatPartitionStringShard
   158  func NewMycatPartitionStringShard(shardNum int, partitionCount, partitionLength string, hashSliceStr string) *MycatPartitionStringShard {
   159  	longShard := NewMycatPartitionLongShard(shardNum, partitionCount, partitionLength)
   160  	stringShard := &MycatPartitionStringShard{
   161  		MycatPartitionLongShard: longShard,
   162  		hashSliceStr:            hashSliceStr,
   163  	}
   164  	return stringShard
   165  }
   166  
   167  // Init init NewMycatPartitionStringShard
   168  func (m *MycatPartitionStringShard) Init() error {
   169  	if err := m.MycatPartitionLongShard.Init(); err != nil {
   170  		return err
   171  	}
   172  
   173  	start, end, err := parseHashSliceStartEnd(m.hashSliceStr)
   174  	if err != nil {
   175  		return err
   176  	}
   177  	m.hashSliceStart = start
   178  	m.hashSliceEnd = end
   179  	return nil
   180  }
   181  
   182  /**
   183   * "2" -&gt; (0,2)<br/>
   184   * "1:2" -&gt; (1,2)<br/>
   185   * "1:" -&gt; (1,0)<br/>
   186   * "-1:" -&gt; (-1,0)<br/>
   187   * ":-1" -&gt; (0,-1)<br/>
   188   * ":" -&gt; (0,0)<br/>
   189   */
   190  // copied from mycat
   191  func parseHashSliceStartEnd(hashSliceStr string) (int, int, error) {
   192  	hashSliceStr = strings.TrimSpace(hashSliceStr)
   193  	strs := strings.Split(hashSliceStr, ":")
   194  
   195  	if len(strs) == 1 {
   196  		v, err := strconv.Atoi(strs[0])
   197  		if err != nil {
   198  			return -1, -1, err
   199  		}
   200  		if v >= 0 {
   201  			return 0, v, nil
   202  		}
   203  		return v, 0, nil
   204  	}
   205  
   206  	if len(strs) == 2 {
   207  		start, err := parseHashSliceValue(strs[0])
   208  		if err != nil {
   209  			return -1, -1, fmt.Errorf("parse hash slice start error: %v", err)
   210  		}
   211  		end, err := parseHashSliceValue(strs[1])
   212  		if err != nil {
   213  			return -1, -1, fmt.Errorf("parse hash slice end error: %v", err)
   214  		}
   215  		return start, end, nil
   216  	}
   217  
   218  	return -1, -1, fmt.Errorf("invalid hash slice str")
   219  }
   220  
   221  // 如果是空字符串, 则返回0
   222  // 如果是非数字, 则返回err
   223  // 如果是数字, 则正常返回
   224  func parseHashSliceValue(str string) (int, error) {
   225  	if str == "" {
   226  		return 0, nil
   227  	}
   228  	v, err := strconv.Atoi(str)
   229  	return v, err
   230  }
   231  
   232  // FindForKey return MycatPartitionStringShard calculated result
   233  func (m *MycatPartitionStringShard) FindForKey(key interface{}) (int, error) {
   234  	keyStr := GetString(key)
   235  	var start int
   236  	if m.hashSliceStart >= 0 {
   237  		start = m.hashSliceStart
   238  	} else {
   239  		start = len(keyStr) + m.hashSliceStart
   240  	}
   241  
   242  	var end int
   243  	if m.hashSliceEnd > 0 {
   244  		end = m.hashSliceEnd
   245  	} else {
   246  		end = len(keyStr) + m.hashSliceEnd
   247  	}
   248  	h := stringHash(keyStr, start, end)
   249  	return m.segment[int(h)&andValue], nil
   250  }
   251  
   252  // copied from mycat
   253  func stringHash(s string, start, end int) int64 {
   254  	input := []rune(s)
   255  	if start < 0 {
   256  		start = 0
   257  	}
   258  
   259  	if end > len(input) {
   260  		end = len(input)
   261  	}
   262  
   263  	var h int64
   264  	for i := start; i < end; i++ {
   265  		h = (h << 5) - h + int64(input[i])
   266  	}
   267  
   268  	return h
   269  }
   270  
   271  const (
   272  	defaultWeight = 1
   273  )
   274  
   275  // MycatPartitionMurmurHashShard mycat route algorithm: PartitionByMurmurHash
   276  type MycatPartitionMurmurHashShard struct {
   277  	seed               int
   278  	count              int
   279  	virtualBucketTimes int
   280  	weightMap          map[int]int
   281  	bucketMap          *treemap.Map
   282  	hashFunction       *util.MurmurHash
   283  }
   284  
   285  // NewMycatPartitionMurmurHashShard constructor of MycatPartitionMurmurHashShard
   286  func NewMycatPartitionMurmurHashShard(seedStr, virtualBucketTimesStr string, count int) (*MycatPartitionMurmurHashShard, error) {
   287  	seed, err := strconv.Atoi(seedStr)
   288  	if err != nil {
   289  		return nil, err
   290  	}
   291  	if virtualBucketTimesStr == "" {
   292  		virtualBucketTimesStr = "160"
   293  	}
   294  	virtualBucketTimes, err := strconv.Atoi(virtualBucketTimesStr)
   295  	if err != nil {
   296  		return nil, err
   297  	}
   298  
   299  	return &MycatPartitionMurmurHashShard{
   300  		seed:               seed,
   301  		count:              count,
   302  		virtualBucketTimes: virtualBucketTimes,
   303  	}, nil
   304  }
   305  
   306  // Init init MycatPartitionMurmurHashShard
   307  func (m *MycatPartitionMurmurHashShard) Init() error {
   308  	m.bucketMap = treemap.NewWithIntComparator()
   309  
   310  	m.hashFunction = util.NewMurmurHash(m.seed) //计算一致性哈希的对象
   311  	if err := m.generateBucketMap(); err != nil {
   312  		return fmt.Errorf("murmur hash error, %v", err)
   313  	}
   314  
   315  	m.weightMap = make(map[int]int)
   316  	return nil
   317  }
   318  
   319  // FindForKey return MycatPartitionMurmurHashShard calculated result
   320  func (m *MycatPartitionMurmurHashShard) FindForKey(key interface{}) (int, error) {
   321  	keyStr := GetString(key)
   322  	hashKey := m.hashFunction.HashUnencodedChars(keyStr)
   323  	_, ret := m.bucketMap.Ceiling(int(hashKey))
   324  	if ret != nil {
   325  		return ret.(int), nil
   326  	}
   327  	k, v := m.bucketMap.Min()
   328  	if k == nil {
   329  		return 0, errors.New("bucket map is empty")
   330  	}
   331  	return v.(int), nil
   332  }
   333  
   334  // SetWeightMapFromFile init weight
   335  func (m *MycatPartitionMurmurHashShard) SetWeightMapFromFile(weightMapPath string) error {
   336  	weightMap, err := parseWeightMapFile(weightMapPath)
   337  	if err != nil {
   338  		return err
   339  	}
   340  	m.weightMap = weightMap
   341  	return nil
   342  }
   343  
   344  /**
   345   * 节点的权重,没有指定权重的节点默认是1。以properties文件的格式填写,以从0开始到count-1的整数值也就是节点索引为key,以节点权重值为值。
   346   * 所有权重值必须是正整数,否则以1代替
   347   */
   348  // TODO: not implemented
   349  func parseWeightMapFile(fileName string) (map[int]int, error) {
   350  	return nil, errors.New("not implemented")
   351  }
   352  
   353  /**
   354   * 得到桶的权重,桶就是实际存储数据的DB实例
   355   * 从0开始的桶编号为key,权重为值,权重默认为1。
   356   * 键值必须都是整数
   357   *
   358   * @param bucket
   359   * @return
   360   */
   361  func (m *MycatPartitionMurmurHashShard) getWeight(bucket int) int {
   362  	w, ok := m.weightMap[bucket]
   363  	if !ok {
   364  		return defaultWeight
   365  	}
   366  	return w
   367  }
   368  
   369  func (m *MycatPartitionMurmurHashShard) generateBucketMap() error {
   370  	hash := m.hashFunction
   371  	for i := 0; i < m.count; i++ { //构造一致性哈希环,用TreeMap表示
   372  		buf := bytes.NewBufferString("SHARD-" + strconv.Itoa(i))
   373  		shard := m.virtualBucketTimes * m.getWeight(i)
   374  		for n := 0; n < shard; n++ {
   375  			// the hashKey is same with mycat, but maybe ugly, like:
   376  			// SHARD-0-NODE-0, SHARD-0-NODE-0-NODE-1, SHARD-0-NODE-0-NODE-1-NODE-2,
   377  			// not SHARD-0-NODE-0, SHARD-0-NODE-1, SHARD-0-NODE-2,
   378  			// take care!
   379  			buf.WriteString("-NODE-" + strconv.Itoa(n))
   380  			hashKey := hash.HashUnencodedChars(buf.String())
   381  			m.bucketMap.Put(hashKey, i)
   382  		}
   383  	}
   384  	return nil
   385  }
   386  
   387  // mod padding
   388  const (
   389  	PaddingModLeftEnd  = 0
   390  	PaddingModRightEnd = 1
   391  
   392  	PaddingModDefaultPadFrom   = PaddingModRightEnd
   393  	PaddingModDefaultPadLength = 18
   394  	PaddingModDefaultModBegin  = 10
   395  	PaddingModDefaultModEnd    = 16
   396  	PaddingModDefaultMod       = 2
   397  )
   398  
   399  // MycatPartitionPaddingModShard mcyat partition padding mod
   400  type MycatPartitionPaddingModShard struct {
   401  	padFrom   int
   402  	padLength int
   403  	modBegin  int
   404  	modEnd    int
   405  	mod       int
   406  }
   407  
   408  func newDefaultMycatPartitionPaddingModShard() *MycatPartitionPaddingModShard {
   409  	return &MycatPartitionPaddingModShard{
   410  		padFrom:   PaddingModDefaultPadFrom,
   411  		padLength: PaddingModDefaultPadLength,
   412  		modBegin:  PaddingModDefaultModBegin,
   413  		modEnd:    PaddingModDefaultModEnd,
   414  		mod:       PaddingModDefaultMod,
   415  	}
   416  }
   417  
   418  // GetMycatPartitionPaddingModShard wrapper newDefaultMycatPartitionPaddingModShard
   419  func GetMycatPartitionPaddingModShard(padFromStr, padLengthStr, modBeginStr, modEndStr string, mod int) (shard *MycatPartitionPaddingModShard, err error) {
   420  	padFrom, err := strconv.Atoi(padFromStr)
   421  	if err != nil {
   422  		return nil, err
   423  	}
   424  
   425  	padLength, err := strconv.Atoi(padLengthStr)
   426  	if err != nil {
   427  		return nil, err
   428  	}
   429  
   430  	modBegin, err := strconv.Atoi(modBeginStr)
   431  	if err != nil {
   432  		return nil, err
   433  	}
   434  
   435  	modEnd, err := strconv.Atoi(modEndStr)
   436  	if err != nil {
   437  		return nil, err
   438  	}
   439  
   440  	return newMycatPartitionPaddingModShard(padFrom, padLength, modBegin, modEnd, mod), nil
   441  }
   442  
   443  func newMycatPartitionPaddingModShard(padFrom, padLength, modBegin, modEnd, mod int) *MycatPartitionPaddingModShard {
   444  	return &MycatPartitionPaddingModShard{
   445  		padFrom:   padFrom,
   446  		padLength: padLength,
   447  		modBegin:  modBegin,
   448  		modEnd:    modEnd,
   449  		mod:       mod,
   450  	}
   451  }
   452  
   453  // Init init MycatPartitionPaddingModShard and check params
   454  func (m *MycatPartitionPaddingModShard) Init() error {
   455  	return m.checkParam()
   456  }
   457  
   458  func (m *MycatPartitionPaddingModShard) checkParam() error {
   459  	if m.padFrom != PaddingModLeftEnd && m.padFrom != PaddingModRightEnd {
   460  		return fmt.Errorf("invalid padding mod mode: %d", m.padFrom)
   461  	}
   462  
   463  	if m.mod < PaddingModDefaultMod {
   464  		return fmt.Errorf("invalid padding mod number: %d", m.mod)
   465  	}
   466  
   467  	if m.modBegin < 0 || m.modBegin >= m.modEnd {
   468  		return fmt.Errorf("invalid padding modBegin or modEnd: %d, %d", m.modBegin, m.modEnd)
   469  	}
   470  
   471  	if m.padLength <= 0 {
   472  		return fmt.Errorf("invalid padding mod padLength: %d", m.padLength)
   473  	}
   474  
   475  	if m.padLength < (m.modEnd - m.modBegin) {
   476  		return fmt.Errorf("invalid padding mod, padLength is less than modBegin - modEnd: %d, %d, %d", m.padLength, m.modBegin, m.modEnd)
   477  	}
   478  
   479  	return nil
   480  }
   481  
   482  // FindForKey return MycatPartitionPaddingModShard calculated result
   483  func (m *MycatPartitionPaddingModShard) FindForKey(key interface{}) (int, error) {
   484  	h := NumValue(key) // assert the key is number
   485  	keyStr := strconv.FormatInt(h, 10)
   486  
   487  	var paddingKey string
   488  	if len(keyStr) > m.padLength {
   489  		paddingKey = keyStr[0:m.padLength]
   490  	} else {
   491  		if m.padFrom == PaddingModLeftEnd {
   492  			if h < 0 { // Compatible with xiaomi mycat version
   493  				return -1, errors.New("padding left to a negative is not allowed")
   494  			}
   495  			paddingKey = util.Left(keyStr, m.padLength, "0")
   496  		} else {
   497  			paddingKey = util.Right(keyStr, m.padLength, "0")
   498  		}
   499  	}
   500  
   501  	modSegment := paddingKey[m.modBegin:m.modEnd]
   502  	bigNum, err := strconv.ParseInt(modSegment, 10, 64) // in mycat, this is a BigInteger, but here is int64
   503  	if err != nil {
   504  		return -1, err
   505  	}
   506  	bigNumAbs := hack.Abs(bigNum)
   507  	return int(bigNumAbs % int64(m.mod)), nil
   508  }