github.com/keysonzzz/kmg@v0.0.0-20151121023212-05317bfd7d39/kmgRedis/db.go (about)

     1  package kmgRedis
     2  
     3  import (
     4  	"github.com/bronze1man/kmg/errors"
     5  	"gopkg.in/redis.v3"
     6  	"strconv"
     7  	"strings"
     8  	"time"
     9  )
    10  
    11  var gClient *redis.Client
    12  
    13  func DefaultInit() {
    14  	InitWithDbNum(0)
    15  }
    16  
    17  func TestInit() {
    18  	InitWithDbNum(1)
    19  
    20  }
    21  
    22  func InitWithConfig(opt *redis.Options) {
    23  	gClient = redis.NewClient(opt)
    24  }
    25  
    26  func InitWithDbNum(num int){
    27  	gClient = redis.NewClient(&redis.Options{
    28  		Network: "tcp",
    29  		Addr:    "127.0.0.1:6379",
    30  		DB:      int64(num),
    31  	})
    32  }
    33  
    34  // 向redis中插入数据,如果已经存在数据会返回 ErrKeyExist
    35  // 如果出现网络错误,会返回 一个网络错误的err
    36  // 没有其他错误的可能性了.
    37  func Insert(key string, value string) (err error) {
    38  	success, err := gClient.SetNX(key, value, 0).Result()
    39  	if err != nil {
    40  		return
    41  	}
    42  	if !success {
    43  		return ErrKeyExist
    44  	}
    45  	return nil
    46  }
    47  
    48  func MustInsert(key string, value string) {
    49  	err := Insert(key, value)
    50  	if err != nil {
    51  		panic(err)
    52  	}
    53  }
    54  
    55  // 数据不存在会返回 ErrKeyNotExist
    56  // 网络错误会返回 error
    57  func Update(key string, value string) (err error) {
    58  	cmd := redis.NewStatusCmd("SET", key, value, "XX")
    59  	gClient.Process(cmd)
    60  	_, err = cmd.Result()
    61  	if err == redis.Nil {
    62  		return ErrKeyNotExist
    63  	}
    64  	return err
    65  }
    66  
    67  func MustUpdate(key string, value string) {
    68  	err := Update(key, value)
    69  	if err != nil {
    70  		panic(err)
    71  	}
    72  }
    73  
    74  // key,不存在会insert,存在会update
    75  // 网络错误会返回 error
    76  // 注意redis类型错误时,也不会报错,只会把这个key设置成正确的value
    77  func Set(key string, value string) (err error) {
    78  	return gClient.Set(key, value, 0).Err()
    79  }
    80  
    81  func MustSet(key string, value string) {
    82  	err := Set(key, value)
    83  	if err != nil {
    84  		panic(err)
    85  	}
    86  }
    87  
    88  type KeyValuePair struct {
    89  	Key   string
    90  	Value string
    91  }
    92  
    93  // 经过实际测试发现一次只能写入1万个,太多会 broken pipe
    94  func MustMSet(pairList []KeyValuePair) {
    95  	outPair := make([]string, len(pairList)*2)
    96  	for i, pair := range pairList {
    97  		outPair[2*i] = pair.Key
    98  		outPair[2*i+1] = pair.Value
    99  	}
   100  	err := gClient.MSet(outPair...).Err()
   101  	if err != nil {
   102  		panic(err)
   103  	}
   104  }
   105  
   106  // 从redis的kvdb中获取一个key
   107  // 注意 value有可能是 "" 这个和数据不存在是两种情况.
   108  // 如果数据不存在,会返回ErrKeyNotExist
   109  // value在redis里面不是string类型,会返回 ErrStringWrongType
   110  // 网络错误也会返回 error
   111  func Get(key string) (value string, err error) {
   112  	value, err = gClient.Get(key).Result()
   113  	if err == nil {
   114  		return value, nil
   115  	}
   116  	if isRedisErrorWrongType(err) {
   117  		return "", ErrStringWrongType
   118  	}
   119  	if err == redis.Nil {
   120  		return "", ErrKeyNotExist
   121  	}
   122  	return "", err
   123  }
   124  
   125  func MustGet(key string) (value string) {
   126  	value, err := Get(key)
   127  	if err != nil {
   128  		panic(err)
   129  	}
   130  	return value
   131  }
   132  
   133  // 从redis的kvdb中获取一个key
   134  // 将这个key转换成int
   135  // 无法转换成int,会panic
   136  // key不存在,返回0
   137  // 网络错误会panic
   138  func MustGetIntIgnoreNotExist(key string) (valueI int) {
   139  	value, err := Get(key)
   140  	if err == ErrKeyNotExist {
   141  		return 0
   142  	}
   143  	if err != nil {
   144  		panic(err)
   145  	}
   146  	valueI, err = strconv.Atoi(value)
   147  	if err != nil {
   148  		panic(err)
   149  	}
   150  	return valueI
   151  }
   152  
   153  // 从redis的kvdb中获取一个key
   154  // 将这个key转换成float
   155  // 无法转换成float,会panic
   156  // key不存在,返回0
   157  // 网络错误会panic
   158  func MustGetFloatIgnoreNotExist(key string) float64 {
   159  	value, err := Get(key)
   160  	if err == ErrKeyNotExist {
   161  		return 0
   162  	}
   163  	if err != nil {
   164  		panic(err)
   165  	}
   166  	valueF, err := strconv.ParseFloat(value, 64)
   167  	if err != nil {
   168  		panic(err)
   169  	}
   170  	return valueF
   171  }
   172  
   173  // 只有网络问题会返回error
   174  func Del(key string) (err error) {
   175  	return gClient.Del(key).Err()
   176  }
   177  
   178  func MustDel(key string) {
   179  	err := gClient.Del(key).Err()
   180  	if err!=nil{
   181  		panic(err)
   182  	}
   183  	return
   184  }
   185  
   186  // 只有网络问题会返回error
   187  func FlushDbV2() (err error) {
   188  	_, err = gClient.FlushDb().Result()
   189  	return err
   190  }
   191  func MustFlushDbV2() {
   192  	err := FlushDbV2()
   193  	if err != nil {
   194  		panic(err)
   195  	}
   196  }
   197  
   198  // 使用 redis的表达式搜索key,返回搜索到的key的列表
   199  // 只有网络问题会返回error
   200  // ** 仅适用于整个数据库key数量比较少的数据库(<500k条数据),否则非常慢. **
   201  func Keys(searchKey string) (keyList []string, err error) {
   202  	return gClient.Keys(searchKey).Result()
   203  }
   204  
   205  func MustKeys(searchKey string)(kList []string){
   206  	kList,err:=Keys(searchKey)
   207  	if err!=nil{
   208  		panic(err)
   209  	}
   210  	return kList
   211  }
   212  
   213  
   214  // 某个key是否存在
   215  // 只有网络问题会返回error
   216  func Exists(key string) (exist bool, err error) {
   217  	return gClient.Exists(key).Result()
   218  }
   219  
   220  /*
   221  Insert all the specified values at the tail of the list stored at key. If key does not exist, it is created as empty list before performing the push operation. When key holds a value that is not a list, an error is returned.
   222  更改的key存在,会向这个数组类型的key,右边加入一个元素.
   223  更改的key不存在,会创建一个,并且写入第一个值.
   224  更改的key的类型不正确会返回 ErrListWrongType
   225  网络错误会返回error
   226  */
   227  func RPush(key string, value string) (err error) {
   228  	err = gClient.RPush(key, value).Err()
   229  	if err == nil {
   230  		return nil
   231  	}
   232  	if isRedisErrorWrongType(err) {
   233  		return ErrListWrongType
   234  	}
   235  	return err
   236  }
   237  
   238  /*
   239  返回一个redis数组里面所有的值.
   240  查询的key存在,并且类型正确,返回列表里面的数据
   241  查询的key不存在,返回空数组 TODO 好用?
   242  查询的key类型错误,返回 ErrListWrongType
   243  网络错误会返回error
   244  */
   245  func LRangeAll(key string) (out []string, err error) {
   246  	out, err = gClient.LRange(key, 0, -1).Result()
   247  	if err == nil {
   248  		return out, nil
   249  	}
   250  	if isRedisErrorWrongType(err) {
   251  		return nil, ErrListWrongType
   252  	}
   253  	return nil, err
   254  }
   255  
   256  
   257  
   258  /*
   259  一次操作,批量从redis里面返回大量key的值.
   260  没有传入数据,不报错
   261  如果查询的key全部存在,返回数据.
   262  如果存在某一个key不存在,或者类型错误,返回 ErrKeyNotExist ,value里面什么也没有 (和redis命令不一致)
   263  网络错误会返回error
   264  */
   265  func MGetNotExistCheck(keyList []string) (value []string, err error) {
   266  	if len(keyList) == 0 {
   267  		return nil, nil
   268  	}
   269  	outList, err := gClient.MGet(keyList...).Result()
   270  	if err != nil {
   271  		return nil, err
   272  	}
   273  	value = make([]string, len(outList))
   274  	for i, stringI := range outList {
   275  		s, ok := stringI.(string)
   276  		if !ok {
   277  			return nil, ErrKeyNotExist
   278  		}
   279  		value[i] = s
   280  	}
   281  	return value, nil
   282  }
   283  
   284  /*
   285  带超时的设置一条数据
   286  网络错误会返回error
   287  */
   288  func SetEx(key string, dur time.Duration, value string) (err error) {
   289  	return gClient.Set(key, value, dur).Err()
   290  }
   291  
   292  func isRedisErrorWrongType(err error) bool {
   293  	return strings.Contains(err.Error(), "WRONGTYPE")
   294  }
   295  
   296  /*
   297  改key的名字
   298  key不存在       ErrKeyNotExist
   299  key和newKey一样 ErrRenameSameName
   300  newKey存在      ErrKeyExist
   301  网络错误会返回error
   302  */
   303  func RenameNx(key string, newKey string) (err error) {
   304  	retB, err := gClient.RenameNX(key, newKey).Result()
   305  	if err == nil {
   306  		if retB == false {
   307  			return ErrKeyExist
   308  		}
   309  		return
   310  	}
   311  	errS := err.Error()
   312  	if strings.Contains(errS, "ERR source and destination objects are the same") {
   313  		return ErrRenameSameName
   314  	}
   315  	if strings.Contains(errS, "ERR no such key") {
   316  		return ErrKeyNotExist
   317  	}
   318  	return err
   319  }
   320  
   321  /*
   322  给某一个redis的key加一个整数
   323  key不存在,会先把这个key变成0,然后再进行增加
   324  key不能被解析成整数,会返回 ErrValueNotIntFormatOrOutOfRange
   325  value不是string类型,会返回 ErrStringWrongType
   326  网络错误会返回error
   327  */
   328  func IncrBy(key string, num int64) (err error) {
   329  	err = gClient.IncrBy(key, num).Err()
   330  	if err != nil {
   331  		if isRedisErrorWrongType(err) {
   332  			return ErrStringWrongType
   333  		}
   334  		if strings.Contains(err.Error(), "ERR value is not an integer or out of range") {
   335  			return ErrValueNotIntFormatOrOutOfRange
   336  		}
   337  		return
   338  	}
   339  	return nil
   340  }
   341  
   342  /*
   343  给某一个redis的key加一个浮点
   344  不要传入大于1e200的浮点,会挂掉. TODO 解决这个问题?
   345  key不存在,会先把这个key变成0,然后再进行增加
   346  key不能被解析成整数,会返回 ErrValueNotFloatFormatOrOutOfRange
   347  value不是string类型,会返回 ErrStringWrongType
   348  网络错误会返回error
   349  */
   350  func IncrByFloat(key string, num float64) (err error) {
   351  	err = gClient.IncrByFloat(key, num).Err()
   352  	if err != nil {
   353  		if isRedisErrorWrongType(err) {
   354  			return ErrStringWrongType
   355  		}
   356  		if strings.Contains(err.Error(), "ERR value is not a valid float") {
   357  			return ErrValueNotFloatFormat
   358  		}
   359  		return
   360  	}
   361  	return nil
   362  }
   363  
   364  // 1万个key扫描大概需要花费 10ms时间,如果这个东西性能是瓶颈.请尝试降低这个值.
   365  var scanSize = 10000
   366  
   367  // 扫描redis里面所有的key.
   368  // 目前按照10000个一次的速度进行出来.
   369  func ScanCallback(patten string, cb func(key string) error) (err error) {
   370  	var cursor int64
   371  	var keyList []string
   372  	for {
   373  		cursor, keyList, err = gClient.Scan(cursor, patten, int64(scanSize)).Result()
   374  		if err != nil {
   375  			return err
   376  		}
   377  		for _, key := range keyList {
   378  			err = cb(key)
   379  			if err != nil {
   380  				return err
   381  			}
   382  		}
   383  		if cursor == 0 {
   384  			return nil
   385  		}
   386  	}
   387  }
   388  
   389  var scanWithOutputLimitEOFError = errors.New("scanWithOutputLimitEOFError")
   390  
   391  // 保证只会返回小于等于limit个数据.
   392  func ScanWithOutputLimit(pattern string, limit int) (sList []string, err error) {
   393  	if limit == 0 {
   394  		return nil, nil
   395  	}
   396  	sList = make([]string, 0, limit)
   397  	err = ScanCallback(pattern, func(key string) error {
   398  		sList = append(sList, key)
   399  		if len(sList) >= limit {
   400  			return scanWithOutputLimitEOFError
   401  		} else {
   402  			return nil
   403  		}
   404  	})
   405  	if err == scanWithOutputLimitEOFError {
   406  		return sList, nil
   407  	}
   408  	return sList, err
   409  }