初始化项目
This commit is contained in:
131
trunk/goutil/redisUtil/pubsub.go
Normal file
131
trunk/goutil/redisUtil/pubsub.go
Normal file
@@ -0,0 +1,131 @@
|
||||
package redisUtil
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/gomodule/redigo/redis"
|
||||
)
|
||||
|
||||
// 订阅回调函数
|
||||
type SubscribeCallback func() error
|
||||
|
||||
// Subscriber
|
||||
// @description: 订阅者
|
||||
type Subscriber struct {
|
||||
// pool 订阅者连接池
|
||||
pool *RedisPool
|
||||
// callBack 订阅者回调函数
|
||||
callBack SubscribeCallback
|
||||
}
|
||||
|
||||
// NewSubscriber
|
||||
// @description: 构建一个订阅者
|
||||
// parameter:
|
||||
// @pool:
|
||||
// @callBack:
|
||||
// return:
|
||||
// @*Subscriber:
|
||||
func NewSubscriber(pool *RedisPool, callBack SubscribeCallback) *Subscriber {
|
||||
return &Subscriber{pool: pool, callBack: callBack}
|
||||
}
|
||||
|
||||
// Promulgator
|
||||
// @description: 发布者
|
||||
type Promulgator struct {
|
||||
// pool 发布者连接池
|
||||
pool *RedisPool
|
||||
}
|
||||
|
||||
// NewPromulgator
|
||||
// @description: 构建一个发布者
|
||||
// parameter:
|
||||
// @pool:
|
||||
// return:
|
||||
// @*Promulgator:
|
||||
func NewPromulgator(pool *RedisPool) *Promulgator {
|
||||
return &Promulgator{pool: pool}
|
||||
}
|
||||
|
||||
// Publish
|
||||
// @description: 发布消息
|
||||
// parameter:
|
||||
// @receiver s:
|
||||
// @channel:
|
||||
// @message:
|
||||
// return:
|
||||
// @error:
|
||||
func (s *Promulgator) Publish(channel, message string) error {
|
||||
c := s.pool.GetConnection()
|
||||
defer c.Close()
|
||||
|
||||
_, err := c.Do("PUBLISH", channel, message)
|
||||
if err != nil {
|
||||
return fmt.Errorf("redis publish %s %s, err: %v", channel, message, err)
|
||||
}
|
||||
//n, err := s.pool.Int(result)
|
||||
//if err != nil {
|
||||
// return fmt.Errorf("redis publish %s %s, err: %v", channel, message, err)
|
||||
//}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Subscribe
|
||||
// @description: 订阅者订阅消息
|
||||
// parameter:
|
||||
// @receiver s:
|
||||
// @ctx:
|
||||
// @channel: 频道
|
||||
// return:
|
||||
// @error:
|
||||
func (s *Subscriber) Subscribe(ctx context.Context, channel ...string) error {
|
||||
sub := redis.PubSubConn{Conn: s.pool.GetConnection()}
|
||||
if err := sub.Subscribe(redis.Args{}.AddFlat(channel)...); err != nil {
|
||||
return err
|
||||
}
|
||||
done := make(chan error, 1)
|
||||
|
||||
// 启动一个新协程去持续订阅消息
|
||||
go func() {
|
||||
defer sub.Close()
|
||||
for {
|
||||
switch msg := sub.Receive().(type) {
|
||||
case error:
|
||||
done <- fmt.Errorf("redis pubsub receive err: %v", msg)
|
||||
return
|
||||
case redis.Message:
|
||||
if err := s.callBack(); err != nil {
|
||||
done <- err
|
||||
return
|
||||
}
|
||||
case redis.Subscription:
|
||||
if msg.Count == 0 {
|
||||
// 所有的订阅者都退出
|
||||
done <- nil
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// health check
|
||||
tick := time.NewTicker(time.Minute)
|
||||
defer tick.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
if err := sub.Unsubscribe(); err != nil {
|
||||
return fmt.Errorf("redis pubsub unsubscribe err: %v", err)
|
||||
}
|
||||
return nil
|
||||
case err := <-done:
|
||||
return err
|
||||
case <-tick.C:
|
||||
if err := sub.Ping(""); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
123
trunk/goutil/redisUtil/redisConfig.go
Normal file
123
trunk/goutil/redisUtil/redisConfig.go
Normal file
@@ -0,0 +1,123 @@
|
||||
package redisUtil
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Redis配置对象
|
||||
type RedisConfig struct {
|
||||
// 连接字符串
|
||||
ConnectionString string
|
||||
|
||||
// 密码
|
||||
Password string
|
||||
|
||||
// 数据库编号
|
||||
Database int
|
||||
|
||||
// 最大活跃连接数
|
||||
MaxActive int
|
||||
|
||||
// 最大空闲连接数
|
||||
MaxIdle int
|
||||
|
||||
// 空闲超时
|
||||
IdleTimeout time.Duration
|
||||
|
||||
// 连接超时
|
||||
DialConnectTimeout time.Duration
|
||||
}
|
||||
|
||||
// 将redis连接字符串转化为redis config对象
|
||||
// 格式:ConnectionString=10.1.0.21:6379;Password=redis_pwd;Database=3;MaxActive=50;MaxIdle=20;IdleTimeout=300;DialConnectTimeout=10;
|
||||
// redisConfigStr:redis连接字符串
|
||||
// 返回值:
|
||||
// redis config对象
|
||||
// 错误对象
|
||||
func NewRedisConfig(redisConfigStr string) (redisConfig *RedisConfig, err error) {
|
||||
var connectionString string
|
||||
var password string
|
||||
var database int
|
||||
var maxActive int
|
||||
var maxIdle int
|
||||
var idleTimeout time.Duration
|
||||
var dialConectTimeout time.Duration
|
||||
var count int = 7
|
||||
var subCount int = 2
|
||||
|
||||
itemList := strings.Split(redisConfigStr, ";")
|
||||
// 去掉最后的空数据
|
||||
if itemList[len(itemList)-1] == "" {
|
||||
itemList = itemList[0 : len(itemList)-1]
|
||||
}
|
||||
if len(itemList) != count {
|
||||
err = fmt.Errorf("%s格式不正确,需要包含%d个部分,现在有%d个部分", redisConfigStr, count, len(itemList))
|
||||
return
|
||||
}
|
||||
|
||||
for _, item := range itemList {
|
||||
subItemList := strings.Split(item, "=")
|
||||
if len(subItemList) != subCount {
|
||||
err = fmt.Errorf("%s格式不正确,需要包含%d个部分", item, subCount)
|
||||
return
|
||||
}
|
||||
|
||||
// 分别进行判断
|
||||
switch strings.ToLower(subItemList[0]) {
|
||||
case strings.ToLower("ConnectionString"):
|
||||
connectionString = subItemList[1]
|
||||
case strings.ToLower("Password"):
|
||||
password = subItemList[1]
|
||||
case strings.ToLower("Database"):
|
||||
if database, err = strconv.Atoi(subItemList[1]); err != nil {
|
||||
err = fmt.Errorf("%s转化为int型失败", subItemList[1])
|
||||
return
|
||||
}
|
||||
case strings.ToLower("MaxActive"):
|
||||
if maxActive, err = strconv.Atoi(subItemList[1]); err != nil {
|
||||
err = fmt.Errorf("%s转化为int型失败", subItemList[1])
|
||||
return
|
||||
}
|
||||
case strings.ToLower("MaxIdle"):
|
||||
if maxIdle, err = strconv.Atoi(subItemList[1]); err != nil {
|
||||
err = fmt.Errorf("%s转化为int型失败", subItemList[1])
|
||||
return
|
||||
}
|
||||
case strings.ToLower("IdleTimeout"):
|
||||
if idleTimeout_int, err1 := strconv.Atoi(subItemList[1]); err1 != nil {
|
||||
err = fmt.Errorf("%s转化为int型失败", subItemList[1])
|
||||
return
|
||||
} else {
|
||||
idleTimeout = time.Duration(idleTimeout_int) * time.Second
|
||||
}
|
||||
case strings.ToLower("DialConnectTimeout"):
|
||||
if dialConectTimeout_int, err1 := strconv.Atoi(subItemList[1]); err1 != nil {
|
||||
err = fmt.Errorf("%s转化为int型失败", subItemList[1])
|
||||
return
|
||||
} else {
|
||||
dialConectTimeout = time.Duration(dialConectTimeout_int) * time.Second
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
redisConfig = NewRedisConfig2(connectionString, password, database, maxActive, maxIdle, idleTimeout, dialConectTimeout)
|
||||
return
|
||||
}
|
||||
|
||||
func NewRedisConfig2(connectionString, password string,
|
||||
database, maxActive, maxIdle int,
|
||||
idleTimeout, dialConnectTimeout time.Duration) *RedisConfig {
|
||||
|
||||
return &RedisConfig{
|
||||
ConnectionString: connectionString,
|
||||
Password: password,
|
||||
Database: database,
|
||||
MaxActive: maxActive,
|
||||
MaxIdle: maxIdle,
|
||||
IdleTimeout: idleTimeout,
|
||||
DialConnectTimeout: dialConnectTimeout,
|
||||
}
|
||||
}
|
||||
88
trunk/goutil/redisUtil/redisConfig_test.go
Normal file
88
trunk/goutil/redisUtil/redisConfig_test.go
Normal file
@@ -0,0 +1,88 @@
|
||||
package redisUtil
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestNewRedisConfig(t *testing.T) {
|
||||
redisConfigStr := "ConnectionString=10.1.0.21:6379;Password=redis_pwd;Database=3;MaxActive=50;MaxIdle=20;IdleTimeout=300;DialConnectTimeout=10;"
|
||||
redisConfig, err := NewRedisConfig(redisConfigStr)
|
||||
if err != nil {
|
||||
t.Errorf("there should be no err, but now has:%s", err)
|
||||
}
|
||||
|
||||
connectionString := "10.1.0.21:6379"
|
||||
password := "redis_pwd"
|
||||
database := 3
|
||||
maxActive := 50
|
||||
maxIdle := 20
|
||||
idleTimeout := 300 * time.Second
|
||||
dialConnectTimeout := 10 * time.Second
|
||||
if redisConfig.ConnectionString != connectionString {
|
||||
t.Errorf("expected %s, but now got %s", connectionString, redisConfig.ConnectionString)
|
||||
}
|
||||
|
||||
if redisConfig.Password != password {
|
||||
t.Errorf("expected %s, but now got %s", password, redisConfig.Password)
|
||||
}
|
||||
|
||||
if redisConfig.Database != database {
|
||||
t.Errorf("expected %d, but now got %d", database, redisConfig.Database)
|
||||
}
|
||||
|
||||
if redisConfig.MaxActive != maxActive {
|
||||
t.Errorf("expected %d, but now got %d", maxActive, redisConfig.MaxActive)
|
||||
}
|
||||
|
||||
if redisConfig.MaxIdle != maxIdle {
|
||||
t.Errorf("expected %d, but now got %d", maxIdle, redisConfig.MaxIdle)
|
||||
}
|
||||
|
||||
if redisConfig.IdleTimeout != idleTimeout {
|
||||
t.Errorf("expected %d, but now got %d", idleTimeout, redisConfig.IdleTimeout)
|
||||
}
|
||||
|
||||
if redisConfig.DialConnectTimeout != dialConnectTimeout {
|
||||
t.Errorf("expected %d, but now got %d", dialConnectTimeout, redisConfig.DialConnectTimeout)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewRedisConfig2(t *testing.T) {
|
||||
connectionString := "10.1.0.21:6379"
|
||||
password := "redis_pwd"
|
||||
database := 3
|
||||
maxActive := 50
|
||||
maxIdle := 20
|
||||
idleTimeout := 300 * time.Second
|
||||
dialConnectTimeout := 10 * time.Second
|
||||
|
||||
redisConfig := NewRedisConfig2(connectionString, password, database, maxActive, maxIdle, idleTimeout, dialConnectTimeout)
|
||||
if redisConfig.ConnectionString != connectionString {
|
||||
t.Errorf("expected %s, but now got %s", connectionString, redisConfig.ConnectionString)
|
||||
}
|
||||
|
||||
if redisConfig.Password != password {
|
||||
t.Errorf("expected %s, but now got %s", password, redisConfig.Password)
|
||||
}
|
||||
|
||||
if redisConfig.Database != database {
|
||||
t.Errorf("expected %d, but now got %d", database, redisConfig.Database)
|
||||
}
|
||||
|
||||
if redisConfig.MaxActive != maxActive {
|
||||
t.Errorf("expected %d, but now got %d", maxActive, redisConfig.MaxActive)
|
||||
}
|
||||
|
||||
if redisConfig.MaxIdle != maxIdle {
|
||||
t.Errorf("expected %d, but now got %d", maxIdle, redisConfig.MaxIdle)
|
||||
}
|
||||
|
||||
if redisConfig.IdleTimeout != idleTimeout {
|
||||
t.Errorf("expected %d, but now got %d", idleTimeout, redisConfig.IdleTimeout)
|
||||
}
|
||||
|
||||
if redisConfig.DialConnectTimeout != dialConnectTimeout {
|
||||
t.Errorf("expected %d, but now got %d", dialConnectTimeout, redisConfig.DialConnectTimeout)
|
||||
}
|
||||
}
|
||||
119
trunk/goutil/redisUtil/redisPool.go
Normal file
119
trunk/goutil/redisUtil/redisPool.go
Normal file
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
redisUtil对Redis的连接池进行了一定程度的封装
|
||||
将常用的方法进行了内部封装,对于不常见的方法,有两种处理方式:
|
||||
1、向作者提出请求,由作者添加到代码中
|
||||
2、调用GetConnection方法,然后自己实现逻辑
|
||||
在代码中,统一将conn.Do的结果和redis.Int,redis.String等类型转换合并处理
|
||||
|
||||
redis的命令请参考:https://redis.readthedocs.io/en/2.6/index.html
|
||||
*/
|
||||
package redisUtil
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/gomodule/redigo/redis"
|
||||
)
|
||||
|
||||
// 自定义Redis连接池对象
|
||||
type RedisPool struct {
|
||||
name string
|
||||
address string
|
||||
pool *redis.Pool
|
||||
}
|
||||
|
||||
// 获取自定义Redis连接池对象的名称
|
||||
// 返回值:
|
||||
// 自定义Redis连接池对象的名称
|
||||
func (this *RedisPool) GetName() string {
|
||||
return this.name
|
||||
}
|
||||
|
||||
// 获取自定义Redis连接池对象的目标地址
|
||||
// 返回值:
|
||||
// 自定义Redis连接池对象的目标地址
|
||||
func (this *RedisPool) GetAddress() string {
|
||||
return this.address
|
||||
}
|
||||
|
||||
// 从自定义连接池中获取连接,在使用后需要调用Close方法
|
||||
// 返回值:
|
||||
// 连接对象
|
||||
func (this *RedisPool) GetConnection() redis.Conn {
|
||||
return this.pool.Get()
|
||||
}
|
||||
|
||||
// 关闭自定义连接池
|
||||
func (this *RedisPool) Close() {
|
||||
this.pool.Close()
|
||||
}
|
||||
|
||||
// 测试连接情况
|
||||
// 返回值:
|
||||
// 错误对象
|
||||
func (this *RedisPool) TestConnection() error {
|
||||
conn := this.GetConnection()
|
||||
defer conn.Close()
|
||||
|
||||
_, err := conn.Do("PING")
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// 创建新的Redis连接池对象(obsolete,建议使用NewRedisPool2)
|
||||
// name:连接池对象名称
|
||||
// connectionString:Redis服务器连接地址
|
||||
// password:Redis服务器连接密码
|
||||
// database:Redis服务器选择的数据库
|
||||
// maxActive:Redis连接池允许的最大活跃连接数量
|
||||
// maxIdle:Redis连接池允许的最大空闲数量
|
||||
// idleTimeout:连接被回收前的空闲时间
|
||||
// dialConnectTimeout:连接Redis服务器超时时间
|
||||
// 返回值:
|
||||
// Redis连接池对象
|
||||
func NewRedisPool(name, connectionString, password string, database, maxActive, maxIdle int, idleTimeout, dialConnectTimeout time.Duration) *RedisPool {
|
||||
redisConfig := NewRedisConfig2(connectionString, password, database, maxActive, maxIdle, idleTimeout, dialConnectTimeout)
|
||||
return NewRedisPool2(name, redisConfig)
|
||||
}
|
||||
|
||||
// 创建新的Redis连接池对象
|
||||
// name:连接池对象名称
|
||||
// redisConfig:Redis配置对象
|
||||
// 返回值:
|
||||
// Redis连接池对象
|
||||
func NewRedisPool2(name string, redisConfig *RedisConfig) *RedisPool {
|
||||
poolObj := &redis.Pool{
|
||||
MaxActive: redisConfig.MaxActive,
|
||||
MaxIdle: redisConfig.MaxIdle,
|
||||
IdleTimeout: redisConfig.IdleTimeout,
|
||||
Dial: func() (redis.Conn, error) {
|
||||
options := make([]redis.DialOption, 0, 4)
|
||||
options = append(options, redis.DialConnectTimeout(redisConfig.DialConnectTimeout))
|
||||
options = append(options, redis.DialDatabase(redisConfig.Database))
|
||||
if redisConfig.Password != "" {
|
||||
options = append(options, redis.DialPassword(redisConfig.Password))
|
||||
}
|
||||
|
||||
conn, err := redis.Dial("tcp", redisConfig.ConnectionString, options...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Dial failed, err:%s", err)
|
||||
}
|
||||
|
||||
return conn, err
|
||||
},
|
||||
TestOnBorrow: func(conn redis.Conn, t time.Time) error {
|
||||
if time.Since(t) < time.Minute {
|
||||
return nil
|
||||
}
|
||||
_, err := conn.Do("PING")
|
||||
return err
|
||||
},
|
||||
}
|
||||
|
||||
return &RedisPool{
|
||||
name: name,
|
||||
address: redisConfig.ConnectionString,
|
||||
pool: poolObj,
|
||||
}
|
||||
}
|
||||
221
trunk/goutil/redisUtil/redisPool_db.go
Normal file
221
trunk/goutil/redisUtil/redisPool_db.go
Normal file
@@ -0,0 +1,221 @@
|
||||
/*
|
||||
未实现的哈希表方法:
|
||||
MOVE、SCAN、SORT、FLUSHDB、FLUSHALL、SELECT、SWAPDB
|
||||
*/
|
||||
package redisUtil
|
||||
|
||||
import (
|
||||
"github.com/gomodule/redigo/redis"
|
||||
)
|
||||
|
||||
/*
|
||||
EXISTS key
|
||||
可用版本: >= 1.0.0
|
||||
时间复杂度: O(1)
|
||||
检查给定 key 是否存在。
|
||||
|
||||
返回值
|
||||
若 key 存在,返回 1 ,否则返回 0 。
|
||||
*/
|
||||
func (this *RedisPool) Exists(key string) (exist bool, err error) {
|
||||
conn := this.GetConnection()
|
||||
defer conn.Close()
|
||||
|
||||
var result int
|
||||
result, err = redis.Int(conn.Do("EXISTS", key))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if result == 1 {
|
||||
exist = true
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
TYPE key
|
||||
可用版本: >= 1.0.0
|
||||
时间复杂度: O(1)
|
||||
返回 key 所储存的值的类型。
|
||||
|
||||
返回值
|
||||
none (key不存在)
|
||||
|
||||
string (字符串)
|
||||
|
||||
list (列表)
|
||||
|
||||
set (集合)
|
||||
|
||||
zset (有序集)
|
||||
|
||||
hash (哈希表)
|
||||
|
||||
stream (流)
|
||||
*/
|
||||
func (this *RedisPool) Type(key string) (_type string, err error) {
|
||||
conn := this.GetConnection()
|
||||
defer conn.Close()
|
||||
|
||||
_type, err = redis.String(conn.Do("TYPE", key))
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
RENAME key newkey
|
||||
|
||||
将 key 改名为 newkey 。
|
||||
|
||||
当 key 和 newkey 相同,或者 key 不存在时,返回一个错误。
|
||||
|
||||
当 newkey 已经存在时, RENAME 命令将覆盖旧值。
|
||||
|
||||
可用版本:
|
||||
>= 1.0.0
|
||||
时间复杂度:
|
||||
O(1)
|
||||
返回值:
|
||||
改名成功时提示 OK ,失败时候返回一个错误。
|
||||
*/
|
||||
func (this *RedisPool) Rename(key, newkey string) (err error) {
|
||||
conn := this.GetConnection()
|
||||
defer conn.Close()
|
||||
|
||||
_, err = conn.Do("RENAME", key, newkey)
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
RENAMENX key newkey
|
||||
|
||||
当且仅当 newkey 不存在时,将 key 改名为 newkey 。
|
||||
|
||||
当 key 不存在时,返回一个错误。
|
||||
|
||||
可用版本:
|
||||
>= 1.0.0
|
||||
时间复杂度:
|
||||
O(1)
|
||||
返回值:
|
||||
修改成功时,返回 1 。
|
||||
如果 newkey 已经存在,返回 0 。
|
||||
*/
|
||||
func (this *RedisPool) RenameNX(key, newkey string) (successful bool, err error) {
|
||||
conn := this.GetConnection()
|
||||
defer conn.Close()
|
||||
|
||||
var result int
|
||||
result, err = redis.Int(conn.Do("RENAMENX", key, newkey))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if result == 1 {
|
||||
successful = true
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
DEL key [key ...]
|
||||
|
||||
删除给定的一个或多个 key 。
|
||||
|
||||
不存在的 key 会被忽略。
|
||||
|
||||
可用版本:
|
||||
>= 1.0.0
|
||||
时间复杂度:
|
||||
O(N), N 为被删除的 key 的数量。
|
||||
删除单个字符串类型的 key ,时间复杂度为O(1)。
|
||||
删除单个列表、集合、有序集合或哈希表类型的 key ,时间复杂度为O(M), M 为以上数据结构内的元素数量。
|
||||
返回值:
|
||||
被删除 key 的数量。
|
||||
*/
|
||||
func (this *RedisPool) Del(keys ...string) (count int, err error) {
|
||||
conn := this.GetConnection()
|
||||
defer conn.Close()
|
||||
|
||||
count, err = redis.Int(conn.Do("DEL", redis.Args{}.AddFlat(keys)...))
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
从当前数据库中随机返回(不删除)一个 key 。
|
||||
|
||||
可用版本:
|
||||
>= 1.0.0
|
||||
时间复杂度:
|
||||
O(1)
|
||||
返回值:
|
||||
当数据库不为空时,返回一个 key 。
|
||||
当数据库为空时,返回 nil 。
|
||||
*/
|
||||
func (this *RedisPool) RandomKey() (key string, exist bool, err error) {
|
||||
conn := this.GetConnection()
|
||||
defer conn.Close()
|
||||
|
||||
var reply interface{}
|
||||
reply, err = conn.Do("RANDOMKEY")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if reply == nil {
|
||||
return
|
||||
}
|
||||
|
||||
key, err = redis.String(reply, err)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
exist = true
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
DBSIZE
|
||||
可用版本: >= 1.0.0
|
||||
时间复杂度: O(1)
|
||||
返回当前数据库的 key 的数量。
|
||||
|
||||
返回值
|
||||
当前数据库的 key 的数量。
|
||||
*/
|
||||
func (this *RedisPool) DBSize() (keyCount int, err error) {
|
||||
conn := this.GetConnection()
|
||||
defer conn.Close()
|
||||
|
||||
keyCount, err = redis.Int(conn.Do("DBSIZE"))
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
KEYS pattern
|
||||
|
||||
查找所有符合给定模式 pattern 的 key 。
|
||||
|
||||
KEYS * 匹配数据库中所有 key 。
|
||||
KEYS h?llo 匹配 hello , hallo 和 hxllo 等。
|
||||
KEYS h*llo 匹配 hllo 和 heeeeello 等。
|
||||
KEYS h[ae]llo 匹配 hello 和 hallo ,但不匹配 hillo 。
|
||||
特殊符号用 \ 隔开
|
||||
|
||||
KEYS 的速度非常快,但在一个大的数据库中使用它仍然可能造成性能问题,如果你需要从一个数据集中查找特定的 key ,你最好还是用 Redis 的集合结构(set)来代替。
|
||||
可用版本:
|
||||
>= 1.0.0
|
||||
时间复杂度:
|
||||
O(N), N 为数据库中 key 的数量。
|
||||
返回值:
|
||||
符合给定模式的 key 列表。
|
||||
*/
|
||||
func (this *RedisPool) Keys(pattern string) (keyList []string, err error) {
|
||||
conn := this.GetConnection()
|
||||
defer conn.Close()
|
||||
|
||||
keyList, err = redis.Strings(conn.Do("KEYS", pattern))
|
||||
return
|
||||
}
|
||||
860
trunk/goutil/redisUtil/redisPool_db_test.go
Normal file
860
trunk/goutil/redisUtil/redisPool_db_test.go
Normal file
@@ -0,0 +1,860 @@
|
||||
package redisUtil
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
redisPoolObj_db *RedisPool
|
||||
)
|
||||
|
||||
func init() {
|
||||
redisPoolObj_db = NewRedisPool("testPool", "10.1.0.21:6379", "redis_pwd", 5, 500, 200, 10*time.Second, 5*time.Second)
|
||||
}
|
||||
|
||||
func TestExists(t *testing.T) {
|
||||
/*
|
||||
redis> SET db "redis"
|
||||
OK
|
||||
*/
|
||||
key := "db"
|
||||
value := "redis"
|
||||
successful, err := redisPoolObj_db.Set(key, value, "", 0, "")
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
if !successful {
|
||||
t.Errorf("Set the key:%s should be successful, but now it's not.", key)
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
redis> EXISTS db
|
||||
(integer) 1
|
||||
*/
|
||||
exist, err := redisPoolObj_db.Exists(key)
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
if !exist {
|
||||
t.Errorf("Set the key:%s should exist, but now it doesn't.", key)
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
redis> DEL db
|
||||
(integer) 1
|
||||
*/
|
||||
expected := 1
|
||||
got, err := redisPoolObj_db.Del(key)
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
if got != expected {
|
||||
t.Errorf("Expected to get %d, but now got %d", expected, got)
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
redis> EXISTS db
|
||||
(integer) 0
|
||||
*/
|
||||
exist, err = redisPoolObj_db.Exists(key)
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
if exist {
|
||||
t.Errorf("Set the key:%s should not exist, but now it does.", key)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func TestType(t *testing.T) {
|
||||
deleteKeys := make([]string, 0, 8)
|
||||
defer func() {
|
||||
// Delete the test keys
|
||||
distinctKeyList := getDistinctKeyList(deleteKeys)
|
||||
count, err := redisPoolObj_string.Del(distinctKeyList...)
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
if count != len(distinctKeyList) {
|
||||
t.Errorf("Expected to get %d, but now got %d", len(distinctKeyList), count)
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
||||
/*
|
||||
# 字符串
|
||||
|
||||
redis> SET weather "sunny"
|
||||
OK
|
||||
|
||||
redis> TYPE weather
|
||||
string
|
||||
*/
|
||||
key := "weather"
|
||||
value := "sunny"
|
||||
|
||||
successful, err := redisPoolObj_db.Set(key, value, "", 0, "")
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
if !successful {
|
||||
t.Errorf("Set the key:%s should be successful, but now it's not.", key)
|
||||
return
|
||||
}
|
||||
|
||||
expected := "string"
|
||||
got, err := redisPoolObj_db.Type(key)
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
if got != expected {
|
||||
t.Errorf("Expected to get %s, but now got %s", expected, got)
|
||||
return
|
||||
}
|
||||
|
||||
deleteKeys = append(deleteKeys, key)
|
||||
|
||||
/*
|
||||
# 列表
|
||||
|
||||
redis> LPUSH book_list "programming in scala"
|
||||
(integer) 1
|
||||
|
||||
redis> TYPE book_list
|
||||
list
|
||||
*/
|
||||
key = "book_list"
|
||||
value = "programming in scala"
|
||||
expected2 := 1
|
||||
got2, err := redisPoolObj_db.LPush(key, value)
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
if got2 != expected2 {
|
||||
t.Errorf("Expected to get %d, but now got %d", expected2, got2)
|
||||
return
|
||||
}
|
||||
|
||||
expected = "list"
|
||||
got, err = redisPoolObj_db.Type(key)
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
if got != expected {
|
||||
t.Errorf("Expected to get %s, but now got %s", expected, got)
|
||||
return
|
||||
}
|
||||
|
||||
deleteKeys = append(deleteKeys, key)
|
||||
|
||||
/*
|
||||
# 集合
|
||||
|
||||
redis> SADD pat "dog"
|
||||
(integer) 1
|
||||
|
||||
redis> TYPE pat
|
||||
set
|
||||
*/
|
||||
|
||||
key = "pat"
|
||||
value = "dog"
|
||||
expected3 := 1
|
||||
got3, err := redisPoolObj_db.SAdd(key, value)
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
if got3 != expected3 {
|
||||
t.Errorf("Expected to get %d, but now got %d", expected3, got3)
|
||||
return
|
||||
}
|
||||
|
||||
expected = "set"
|
||||
got, err = redisPoolObj_db.Type(key)
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
if got != expected {
|
||||
t.Errorf("Expected to get %s, but now got %s", expected, got)
|
||||
return
|
||||
}
|
||||
|
||||
deleteKeys = append(deleteKeys, key)
|
||||
}
|
||||
|
||||
func TestRename(t *testing.T) {
|
||||
deleteKeys := make([]string, 0, 8)
|
||||
defer func() {
|
||||
// Delete the test keys
|
||||
distinctKeyList := getDistinctKeyList(deleteKeys)
|
||||
count, err := redisPoolObj_string.Del(distinctKeyList...)
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
if count != len(distinctKeyList) {
|
||||
t.Errorf("Expected to get %d, but now got %d", len(distinctKeyList), count)
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
||||
/*
|
||||
# key 存在且 newkey 不存在
|
||||
|
||||
redis> SET message "hello world"
|
||||
OK
|
||||
|
||||
redis> RENAME message greeting
|
||||
OK
|
||||
|
||||
redis> EXISTS message # message 不复存在
|
||||
(integer) 0
|
||||
|
||||
redis> EXISTS greeting # greeting 取而代之
|
||||
(integer) 1
|
||||
*/
|
||||
key := "message"
|
||||
value := "hello world"
|
||||
expected := true
|
||||
got, err := redisPoolObj_db.Set(key, value, "", 0, "")
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
if got != expected {
|
||||
t.Errorf("Expected to get %t, but now got %t", expected, got)
|
||||
return
|
||||
}
|
||||
|
||||
newkey := "greeting"
|
||||
err = redisPoolObj_db.Rename(key, newkey)
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
expected = false
|
||||
got, err = redisPoolObj_db.Exists(key)
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
if got != expected {
|
||||
t.Errorf("Expected to get %t, but now got %t", expected, got)
|
||||
return
|
||||
}
|
||||
|
||||
expected = true
|
||||
got, err = redisPoolObj_db.Exists(newkey)
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
if got != expected {
|
||||
t.Errorf("Expected to get %t, but now got %t", expected, got)
|
||||
return
|
||||
}
|
||||
|
||||
deleteKeys = append(deleteKeys, newkey)
|
||||
|
||||
/*
|
||||
# 当 key 不存在时,返回错误
|
||||
|
||||
redis> RENAME fake_key never_exists
|
||||
(error) ERR no such key
|
||||
*/
|
||||
key = "fake_key"
|
||||
newkey = "never_exists"
|
||||
err = redisPoolObj_db.Rename(key, newkey)
|
||||
if err == nil {
|
||||
t.Errorf("There should be one error, but now there isn't.")
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
# newkey 已存在时, RENAME 会覆盖旧 newkey
|
||||
|
||||
redis> SET pc "lenovo"
|
||||
OK
|
||||
|
||||
redis> SET personal_computer "dell"
|
||||
OK
|
||||
|
||||
redis> RENAME pc personal_computer
|
||||
OK
|
||||
|
||||
redis> GET pc
|
||||
(nil)
|
||||
|
||||
redis:1> GET personal_computer # 原来的值 dell 被覆盖了
|
||||
"lenovo"
|
||||
*/
|
||||
key = "pc"
|
||||
value = "lenovo"
|
||||
expected = true
|
||||
got, err = redisPoolObj_db.Set(key, value, "", 0, "")
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
if got != expected {
|
||||
t.Errorf("Expected to get %t, but now got %t", expected, got)
|
||||
return
|
||||
}
|
||||
|
||||
key = "personal_computer"
|
||||
value = "dell"
|
||||
expected = true
|
||||
got, err = redisPoolObj_db.Set(key, value, "", 0, "")
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
if got != expected {
|
||||
t.Errorf("Expected to get %t, but now got %t", expected, got)
|
||||
return
|
||||
}
|
||||
|
||||
key = "pc"
|
||||
newkey = "personal_computer"
|
||||
err = redisPoolObj_db.Rename(key, newkey)
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
expected = false
|
||||
got, err = redisPoolObj_db.Exists(key)
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
if got != expected {
|
||||
t.Errorf("Expected to get %t, but now got %t", expected, got)
|
||||
return
|
||||
}
|
||||
|
||||
expected = true
|
||||
got, err = redisPoolObj_db.Exists(newkey)
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
if got != expected {
|
||||
t.Errorf("Expected to get %t, but now got %t", expected, got)
|
||||
return
|
||||
}
|
||||
|
||||
deleteKeys = append(deleteKeys, newkey)
|
||||
}
|
||||
|
||||
func TestRenameNX(t *testing.T) {
|
||||
deleteKeys := make([]string, 0, 8)
|
||||
defer func() {
|
||||
// Delete the test keys
|
||||
distinctKeyList := getDistinctKeyList(deleteKeys)
|
||||
count, err := redisPoolObj_string.Del(distinctKeyList...)
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
if count != len(distinctKeyList) {
|
||||
t.Errorf("Expected to get %d, but now got %d", len(distinctKeyList), count)
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
||||
/*
|
||||
# newkey 不存在,改名成功
|
||||
|
||||
redis> SET player "MPlyaer"
|
||||
OK
|
||||
|
||||
redis> EXISTS best_player
|
||||
(integer) 0
|
||||
|
||||
redis> RENAMENX player best_player
|
||||
(integer) 1
|
||||
*/
|
||||
key := "player"
|
||||
value := "MPlayer"
|
||||
expected := true
|
||||
got, err := redisPoolObj_db.Set(key, value, "", 0, "")
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
if got != expected {
|
||||
t.Errorf("Expected to get %t, but now got %t", expected, got)
|
||||
return
|
||||
}
|
||||
|
||||
newkey := "best_player"
|
||||
expected = false
|
||||
got, err = redisPoolObj_db.Exists(newkey)
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
if got != expected {
|
||||
t.Errorf("Expected to get %t, but now got %t", expected, got)
|
||||
return
|
||||
}
|
||||
|
||||
expected = true
|
||||
got, err = redisPoolObj_db.RenameNX(key, newkey)
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
if got != expected {
|
||||
t.Errorf("Expected to get %t, but now got %t", expected, got)
|
||||
return
|
||||
}
|
||||
|
||||
deleteKeys = append(deleteKeys, newkey)
|
||||
|
||||
/*
|
||||
# newkey存在时,失败
|
||||
|
||||
redis> SET animal "bear"
|
||||
OK
|
||||
|
||||
redis> SET favorite_animal "butterfly"
|
||||
OK
|
||||
|
||||
redis> RENAMENX animal favorite_animal
|
||||
(integer) 0
|
||||
|
||||
redis> get animal
|
||||
"bear"
|
||||
|
||||
redis> get favorite_animal
|
||||
"butterfly"
|
||||
*/
|
||||
key = "animal"
|
||||
value = "bear"
|
||||
expected = true
|
||||
got, err = redisPoolObj_db.Set(key, value, "", 0, "")
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
if got != expected {
|
||||
t.Errorf("Expected to get %t, but now got %t", expected, got)
|
||||
return
|
||||
}
|
||||
deleteKeys = append(deleteKeys, key)
|
||||
|
||||
key = "favorite_animal"
|
||||
value = "butterfly"
|
||||
expected = true
|
||||
got, err = redisPoolObj_db.Set(key, value, "", 0, "")
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
if got != expected {
|
||||
t.Errorf("Expected to get %t, but now got %t", expected, got)
|
||||
return
|
||||
}
|
||||
deleteKeys = append(deleteKeys, key)
|
||||
|
||||
expected = false
|
||||
got, err = redisPoolObj_db.RenameNX(key, newkey)
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
if got != expected {
|
||||
t.Errorf("Expected to get %t, but now got %t", expected, got)
|
||||
return
|
||||
}
|
||||
|
||||
key2 := "animal"
|
||||
expected2 := "bear"
|
||||
got2_interface, exist2, err := redisPoolObj_db.Get(key2)
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
if !exist2 {
|
||||
t.Errorf("The key:%s should exist, but now it doesn't.", key2)
|
||||
return
|
||||
}
|
||||
got2, err := redisPoolObj_db.String(got2_interface)
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
if got2 != expected2 {
|
||||
t.Errorf("Expected to get %s, but now got %s", expected2, got2)
|
||||
return
|
||||
}
|
||||
|
||||
key3 := "animal"
|
||||
expected3 := "bear"
|
||||
got3_interface, exist3, err := redisPoolObj_db.Get(key3)
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
if !exist3 {
|
||||
t.Errorf("The key:%s should exist, but now it doesn't.", key3)
|
||||
return
|
||||
}
|
||||
got3, err := redisPoolObj_db.String(got3_interface)
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
if got3 != expected3 {
|
||||
t.Errorf("Expected to get %s, but now got %s", expected3, got3)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func TestDel(t *testing.T) {
|
||||
/*
|
||||
# 删除单个 key
|
||||
|
||||
redis> SET name huangz
|
||||
OK
|
||||
|
||||
redis> DEL name
|
||||
(integer) 1
|
||||
*/
|
||||
key := "name"
|
||||
value := "huangz"
|
||||
expected := true
|
||||
got, err := redisPoolObj_db.Set(key, value, "", 0, "")
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
if got != expected {
|
||||
t.Errorf("Expected to get %t, but now got %t", expected, got)
|
||||
return
|
||||
}
|
||||
|
||||
expected2 := 1
|
||||
got2, err := redisPoolObj_db.Del(key)
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
if got2 != expected2 {
|
||||
t.Errorf("Expected to get %d, but now got %d", expected2, got2)
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
# 删除一个不存在的 key
|
||||
|
||||
redis> EXISTS phone
|
||||
(integer) 0
|
||||
|
||||
redis> DEL phone # 失败,没有 key 被删除
|
||||
(integer) 0
|
||||
*/
|
||||
key = "phone"
|
||||
expected = false
|
||||
got, err = redisPoolObj_db.Exists(key)
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
if got != expected {
|
||||
t.Errorf("Expected to get %t, but now got %t", expected, got)
|
||||
return
|
||||
}
|
||||
|
||||
expected3 := 0
|
||||
got3, err := redisPoolObj_db.Del(key)
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
if got3 != expected3 {
|
||||
t.Errorf("Expected to get %d, but now got %d", expected3, got3)
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
# 同时删除多个 key
|
||||
|
||||
redis> SET name "redis"
|
||||
OK
|
||||
|
||||
redis> SET type "key-value store"
|
||||
OK
|
||||
|
||||
redis> SET website "redis.com"
|
||||
OK
|
||||
|
||||
redis> DEL name type website
|
||||
(integer) 3
|
||||
*/
|
||||
key1 := "name"
|
||||
value1 := "redis"
|
||||
expected = true
|
||||
got, err = redisPoolObj_db.Set(key1, value1, "", 0, "")
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
if got != expected {
|
||||
t.Errorf("Expected to get %t, but now got %t", expected, got)
|
||||
return
|
||||
}
|
||||
key2 := "type"
|
||||
value2 := "key-value store"
|
||||
expected = true
|
||||
got, err = redisPoolObj_db.Set(key2, value2, "", 0, "")
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
if got != expected {
|
||||
t.Errorf("Expected to get %t, but now got %t", expected, got)
|
||||
return
|
||||
}
|
||||
key3 := "website"
|
||||
value3 := "redis.com"
|
||||
expected = true
|
||||
got, err = redisPoolObj_db.Set(key3, value3, "", 0, "")
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
if got != expected {
|
||||
t.Errorf("Expected to get %t, but now got %t", expected, got)
|
||||
return
|
||||
}
|
||||
|
||||
expected4 := 3
|
||||
got4, err := redisPoolObj_db.Del(key1, key2, key3)
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
if got4 != expected4 {
|
||||
t.Errorf("Expected to get %d, but now got %d", expected4, got4)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func TestRandomKey(t *testing.T) {
|
||||
deleteKeys := make([]string, 0, 8)
|
||||
defer func() {
|
||||
// Delete the test keys
|
||||
distinctKeyList := getDistinctKeyList(deleteKeys)
|
||||
count, err := redisPoolObj_list.Del(distinctKeyList...)
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
if count != len(distinctKeyList) {
|
||||
t.Errorf("Expected to get %d, but now got %d", len(distinctKeyList), count)
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
||||
/*
|
||||
# 数据库为空
|
||||
|
||||
redis> RANDOMKEY
|
||||
(nil)
|
||||
*/
|
||||
expected := ""
|
||||
got, exist, err := redisPoolObj_db.RandomKey()
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
if exist {
|
||||
t.Errorf("RandomKey doesn't exist, but now it does.")
|
||||
return
|
||||
}
|
||||
if got != expected {
|
||||
t.Errorf("Expected to get %s, but now got %s", expected, got)
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
# 数据库不为空
|
||||
|
||||
redis> MSET fruit "apple" drink "beer" food "cookies" # 设置多个 key
|
||||
OK
|
||||
|
||||
redis> RANDOMKEY
|
||||
"fruit"
|
||||
|
||||
redis> RANDOMKEY
|
||||
"food"
|
||||
|
||||
redis> KEYS * # 查看数据库内所有key,证明 RANDOMKEY 并不删除 key
|
||||
1) "food"
|
||||
2) "drink"
|
||||
3) "fruit"
|
||||
*/
|
||||
key_value_map := make(map[string]interface{})
|
||||
key_value_map["fruit"] = "apple"
|
||||
key_value_map["drink"] = "beer"
|
||||
key_value_map["food"] = "cookies"
|
||||
err = redisPoolObj_db.MSet(key_value_map)
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
got, exist, err = redisPoolObj_db.RandomKey()
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
if !exist {
|
||||
t.Errorf("RandomKey should exist, but now it doesn't.")
|
||||
return
|
||||
}
|
||||
if _, exist = key_value_map[got]; !exist {
|
||||
t.Errorf("RandomKey should exist, but now it doesn't.")
|
||||
return
|
||||
}
|
||||
|
||||
expected2 := make([]string, 0, len(key_value_map))
|
||||
for k := range key_value_map {
|
||||
expected2 = append(expected2, k)
|
||||
deleteKeys = append(deleteKeys, k)
|
||||
}
|
||||
got2, err := redisPoolObj_db.Keys("*")
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
if isTwoUnorderedSliceEqual(expected2, got2) == false {
|
||||
t.Errorf("Expected to get %v, but got %v\n", expected2, got2)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func TestDBSize(t *testing.T) {
|
||||
deleteKeys := make([]string, 0, 8)
|
||||
defer func() {
|
||||
// Delete the test keys
|
||||
distinctKeyList := getDistinctKeyList(deleteKeys)
|
||||
count, err := redisPoolObj_list.Del(distinctKeyList...)
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
if count != len(distinctKeyList) {
|
||||
t.Errorf("Expected to get %d, but now got %d", len(distinctKeyList), count)
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
||||
/*
|
||||
redis> DBSIZE
|
||||
(integer) 0
|
||||
|
||||
redis> SET new_key "hello_moto" # 增加一个 key 试试
|
||||
OK
|
||||
|
||||
redis> DBSIZE
|
||||
(integer) 1
|
||||
*/
|
||||
expected := 0
|
||||
got, err := redisPoolObj_db.DBSize()
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
if got != expected {
|
||||
t.Errorf("Expected to get %d, but now got %d", expected, got)
|
||||
return
|
||||
}
|
||||
|
||||
key := "new_key"
|
||||
value := "hello_moto"
|
||||
redisPoolObj_db.Set(key, value, "", 0, "")
|
||||
|
||||
deleteKeys = append(deleteKeys, key)
|
||||
|
||||
expected = 1
|
||||
got, err = redisPoolObj_db.DBSize()
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
if got != expected {
|
||||
t.Errorf("Expected to get %d, but now got %d", expected, got)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func TestKeys(t *testing.T) {
|
||||
deleteKeys := make([]string, 0, 8)
|
||||
defer func() {
|
||||
// Delete the test keys
|
||||
distinctKeyList := getDistinctKeyList(deleteKeys)
|
||||
count, err := redisPoolObj_list.Del(distinctKeyList...)
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
if count != len(distinctKeyList) {
|
||||
t.Errorf("Expected to get %d, but now got %d", len(distinctKeyList), count)
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
||||
/*
|
||||
redis> MSET one 1 two 2 three 3 four 4 # 一次设置 4 个 key
|
||||
OK
|
||||
*/
|
||||
key_value_map := make(map[string]interface{})
|
||||
key_value_map["one"] = "1"
|
||||
key_value_map["two"] = "2"
|
||||
key_value_map["three"] = "3"
|
||||
key_value_map["four"] = "4"
|
||||
err := redisPoolObj_db.MSet(key_value_map)
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
for k := range key_value_map {
|
||||
deleteKeys = append(deleteKeys, k)
|
||||
}
|
||||
|
||||
/*
|
||||
redis> KEYS *o*
|
||||
1) "four"
|
||||
2) "two"
|
||||
3) "one"
|
||||
*/
|
||||
pattern := "*o*"
|
||||
expected := []string{"four", "two", "one"}
|
||||
got, err := redisPoolObj_db.Keys(pattern)
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
if isTwoUnorderedSliceEqual(expected, got) == false {
|
||||
t.Errorf("Expected to get %v, but got %v\n", expected, got)
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
redis> KEYS t??
|
||||
1) "two"
|
||||
*/
|
||||
pattern = "t??"
|
||||
expected = []string{"two"}
|
||||
got, err = redisPoolObj_db.Keys(pattern)
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
if isTwoUnorderedSliceEqual(expected, got) == false {
|
||||
t.Errorf("Expected to get %v, but got %v\n", expected, got)
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
redis> KEYS t[w]*
|
||||
1) "two"
|
||||
*/
|
||||
pattern = "t[w]*"
|
||||
expected = []string{"two"}
|
||||
got, err = redisPoolObj_db.Keys(pattern)
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
if isTwoUnorderedSliceEqual(expected, got) == false {
|
||||
t.Errorf("Expected to get %v, but got %v\n", expected, got)
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
redis> KEYS * # 匹配数据库内所有 key
|
||||
1) "four"
|
||||
2) "three"
|
||||
3) "two"
|
||||
4) "one"
|
||||
*/
|
||||
pattern = "*"
|
||||
expected = []string{"two", "one", "three", "four"}
|
||||
got, err = redisPoolObj_db.Keys(pattern)
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
if isTwoUnorderedSliceEqual(expected, got) == false {
|
||||
t.Errorf("Expected to get %v, but got %v\n", expected, got)
|
||||
return
|
||||
}
|
||||
}
|
||||
233
trunk/goutil/redisUtil/redisPool_expire.go
Normal file
233
trunk/goutil/redisUtil/redisPool_expire.go
Normal file
@@ -0,0 +1,233 @@
|
||||
/*
|
||||
未实现的哈希表方法:
|
||||
MOVE、SCAN、SORT、FLUSHDB、FLUSHALL、SELECT、SWAPDB
|
||||
*/
|
||||
package redisUtil
|
||||
|
||||
import (
|
||||
"github.com/gomodule/redigo/redis"
|
||||
)
|
||||
|
||||
/*
|
||||
EXPIRE key seconds
|
||||
可用版本: >= 1.0.0
|
||||
时间复杂度: O(1)
|
||||
为给定 key 设置生存时间,当 key 过期时(生存时间为 0 ),它会被自动删除。
|
||||
|
||||
在 Redis 中,带有生存时间的 key 被称为『易失的』(volatile)。
|
||||
|
||||
生存时间可以通过使用 DEL 命令来删除整个 key 来移除,或者被 SET 和 GETSET 命令覆写(overwrite),这意味着,如果一个命令只是修改(alter)一个带生存时间的 key 的值而不是用一个新的 key 值来代替(replace)它的话,那么生存时间不会被改变。
|
||||
|
||||
比如说,对一个 key 执行 INCR 命令,对一个列表进行 LPUSH 命令,或者对一个哈希表执行 HSET 命令,这类操作都不会修改 key 本身的生存时间。
|
||||
|
||||
另一方面,如果使用 RENAME 对一个 key 进行改名,那么改名后的 key 的生存时间和改名前一样。
|
||||
|
||||
RENAME 命令的另一种可能是,尝试将一个带生存时间的 key 改名成另一个带生存时间的 another_key ,这时旧的 another_key (以及它的生存时间)会被删除,然后旧的 key 会改名为 another_key ,因此,新的 another_key 的生存时间也和原本的 key 一样。
|
||||
|
||||
使用 PERSIST 命令可以在不删除 key 的情况下,移除 key 的生存时间,让 key 重新成为一个『持久的』(persistent) key 。
|
||||
|
||||
更新生存时间
|
||||
可以对一个已经带有生存时间的 key 执行 EXPIRE 命令,新指定的生存时间会取代旧的生存时间。
|
||||
|
||||
过期时间的精确度
|
||||
在 Redis 2.4 版本中,过期时间的延迟在 1 秒钟之内 —— 也即是,就算 key 已经过期,但它还是可能在过期之后一秒钟之内被访问到,而在新的 Redis 2.6 版本中,延迟被降低到 1 毫秒之内。
|
||||
|
||||
Redis 2.1.3 之前的不同之处
|
||||
在 Redis 2.1.3 之前的版本中,修改一个带有生存时间的 key 会导致整个 key 被删除,这一行为是受当时复制(replication)层的限制而作出的,现在这一限制已经被修复。
|
||||
|
||||
返回值
|
||||
设置成功返回 1 。 当 key 不存在或者不能为 key 设置生存时间时(比如在低于 2.1.3 版本的 Redis 中你尝试更新 key 的生存时间),返回 0 。
|
||||
*/
|
||||
func (this *RedisPool) Expire(key string, seconds int64) (successful bool, err error) {
|
||||
conn := this.GetConnection()
|
||||
defer conn.Close()
|
||||
|
||||
var result int
|
||||
if result, err = redis.Int(conn.Do("EXPIRE", key, seconds)); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if result == 1 {
|
||||
successful = true
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
EXPIREAT key timestamp
|
||||
可用版本: >= 1.2.0
|
||||
时间复杂度: O(1)
|
||||
EXPIREAT 的作用和 EXPIRE 类似,都用于为 key 设置生存时间。
|
||||
|
||||
不同在于 EXPIREAT 命令接受的时间参数是 UNIX 时间戳(unix timestamp)。
|
||||
|
||||
返回值
|
||||
如果生存时间设置成功,返回 1 ; 当 key 不存在或没办法设置生存时间,返回 0 。
|
||||
*/
|
||||
func (this *RedisPool) ExpireAt(key string, timestamp int64) (successful bool, err error) {
|
||||
conn := this.GetConnection()
|
||||
defer conn.Close()
|
||||
|
||||
var result int
|
||||
if result, err = redis.Int(conn.Do("EXPIREAT", key, timestamp)); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if result == 1 {
|
||||
successful = true
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
TTL key
|
||||
可用版本: >= 1.0.0
|
||||
时间复杂度: O(1)
|
||||
以秒为单位,返回给定 key 的剩余生存时间(TTL, time to live)。
|
||||
|
||||
返回值
|
||||
当 key 不存在时,返回 -2 。 当 key 存在但没有设置剩余生存时间时,返回 -1 。 否则,以秒为单位,返回 key 的剩余生存时间。
|
||||
|
||||
Note
|
||||
|
||||
在 Redis 2.8 以前,当 key 不存在,或者 key 没有设置剩余生存时间时,命令都返回 -1 。
|
||||
*/
|
||||
func (this *RedisPool) TTL(key string) (ttl int64, exist, persisted bool, err error) {
|
||||
conn := this.GetConnection()
|
||||
defer conn.Close()
|
||||
|
||||
ttl, err = redis.Int64(conn.Do("TTL", key))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if ttl == -2 {
|
||||
exist = false
|
||||
persisted = false
|
||||
} else if ttl == -1 {
|
||||
exist = true
|
||||
persisted = true
|
||||
} else {
|
||||
exist = true
|
||||
persisted = false
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
PERSIST key
|
||||
可用版本: >= 2.2.0
|
||||
时间复杂度: O(1)
|
||||
移除给定 key 的生存时间,将这个 key 从“易失的”(带生存时间 key )转换成“持久的”(一个不带生存时间、永不过期的 key )。
|
||||
|
||||
返回值
|
||||
当生存时间移除成功时,返回 1 . 如果 key 不存在或 key 没有设置生存时间,返回 0 。
|
||||
*/
|
||||
func (this *RedisPool) Persist(key string) (successful bool, err error) {
|
||||
conn := this.GetConnection()
|
||||
defer conn.Close()
|
||||
|
||||
var result int
|
||||
if result, err = redis.Int(conn.Do("PERSIST", key)); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if result == 1 {
|
||||
successful = true
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
PEXPIRE key milliseconds
|
||||
可用版本: >= 2.6.0
|
||||
时间复杂度: O(1)
|
||||
这个命令和 EXPIRE 命令的作用类似,但是它以毫秒为单位设置 key 的生存时间,而不像 EXPIRE 命令那样,以秒为单位。
|
||||
|
||||
返回值
|
||||
设置成功,返回 1 key 不存在或设置失败,返回 0
|
||||
*/
|
||||
func (this *RedisPool) PExpire(key string, milliseconds int64) (successful bool, err error) {
|
||||
conn := this.GetConnection()
|
||||
defer conn.Close()
|
||||
|
||||
var result int
|
||||
if result, err = redis.Int(conn.Do("PEXPIRE", key, milliseconds)); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if result == 1 {
|
||||
successful = true
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
PEXPIREAT key milliseconds-timestamp
|
||||
可用版本: >= 2.6.0
|
||||
时间复杂度: O(1)
|
||||
这个命令和 expireat 命令类似,但它以毫秒为单位设置 key 的过期 unix 时间戳,而不是像 expireat 那样,以秒为单位。
|
||||
|
||||
返回值
|
||||
如果生存时间设置成功,返回 1 。 当 key 不存在或没办法设置生存时间时,返回 0 。(查看 EXPIRE key seconds 命令获取更多信息)
|
||||
*/
|
||||
func (this *RedisPool) PExpireAt(key string, milliseconds_timestamp int64) (successful bool, err error) {
|
||||
conn := this.GetConnection()
|
||||
defer conn.Close()
|
||||
|
||||
var result int
|
||||
if result, err = redis.Int(conn.Do("PEXPIREAT", key, milliseconds_timestamp)); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if result == 1 {
|
||||
successful = true
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
PTTL key
|
||||
可用版本: >= 2.6.0
|
||||
复杂度: O(1)
|
||||
这个命令类似于 TTL 命令,但它以毫秒为单位返回 key 的剩余生存时间,而不是像 TTL 命令那样,以秒为单位。
|
||||
|
||||
返回值
|
||||
当 key 不存在时,返回 -2 。
|
||||
|
||||
当 key 存在但没有设置剩余生存时间时,返回 -1 。
|
||||
|
||||
否则,以毫秒为单位,返回 key 的剩余生存时间。
|
||||
|
||||
Note
|
||||
|
||||
在 Redis 2.8 以前,当 key 不存在,或者 key 没有设置剩余生存时间时,命令都返回 -1 。
|
||||
*/
|
||||
func (this *RedisPool) PTTL(key string) (pttl int64, exist bool, persisted bool, err error) {
|
||||
conn := this.GetConnection()
|
||||
defer conn.Close()
|
||||
|
||||
pttl, err = redis.Int64(conn.Do("PTTL", key))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if pttl == -2 {
|
||||
exist = false
|
||||
persisted = false
|
||||
} else if pttl == -1 {
|
||||
exist = true
|
||||
persisted = true
|
||||
} else {
|
||||
exist = true
|
||||
persisted = false
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
592
trunk/goutil/redisUtil/redisPool_expire_test.go
Normal file
592
trunk/goutil/redisUtil/redisPool_expire_test.go
Normal file
@@ -0,0 +1,592 @@
|
||||
package redisUtil
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
redisPoolObj_expire *RedisPool
|
||||
)
|
||||
|
||||
func init() {
|
||||
redisPoolObj_expire = NewRedisPool("testPool", "10.1.0.21:6379", "redis_pwd", 5, 500, 200, 10*time.Second, 5*time.Second)
|
||||
}
|
||||
|
||||
func TestExpire(t *testing.T) {
|
||||
deleteKeys := make([]string, 0, 8)
|
||||
defer func() {
|
||||
// Delete the test keys
|
||||
distinctKeyList := getDistinctKeyList(deleteKeys)
|
||||
count, err := redisPoolObj_string.Del(distinctKeyList...)
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
if count != len(distinctKeyList) {
|
||||
t.Errorf("Expected to get %d, but now got %d", len(distinctKeyList), count)
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
||||
/*
|
||||
redis> SET cache_page "www.google.com"
|
||||
OK
|
||||
|
||||
redis> TTL cache_page
|
||||
(integer) -1
|
||||
|
||||
redis> EXPIRE cache_page 30 # 设置过期时间为 30 秒
|
||||
(integer) 1
|
||||
|
||||
redis> TTL cache_page # 查看剩余生存时间
|
||||
(integer) 23
|
||||
|
||||
redis> EXPIRE cache_page 30000 # 更新过期时间
|
||||
(integer) 1
|
||||
|
||||
redis> TTL cache_page
|
||||
(integer) 29996
|
||||
*/
|
||||
key := "cache_page"
|
||||
value := "www.google.com"
|
||||
expected := true
|
||||
got, err := redisPoolObj_expire.Set(key, value, "", 0, "")
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
if got != expected {
|
||||
t.Errorf("Expected to get %t, but got %t", expected, got)
|
||||
return
|
||||
}
|
||||
deleteKeys = append(deleteKeys, key)
|
||||
|
||||
expected = true
|
||||
_, _, got, err = redisPoolObj_expire.TTL(key)
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
if got != expected {
|
||||
t.Errorf("Expected to get %t, but got %t", expected, got)
|
||||
return
|
||||
}
|
||||
|
||||
seconds := int64(30)
|
||||
got, err = redisPoolObj_expire.Expire(key, seconds)
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
if got != expected {
|
||||
t.Errorf("Expected to get %t, but got %t", expected, got)
|
||||
return
|
||||
}
|
||||
|
||||
expected2 := seconds - 5
|
||||
got2, exist, _, err := redisPoolObj_expire.TTL(key)
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
if !exist {
|
||||
t.Errorf("The key:%s should exist, but now it doesn't.", key)
|
||||
return
|
||||
}
|
||||
if got2 <= expected2 {
|
||||
t.Errorf("Expected to get a number bigger than %d, but now get %d.", expected2, got2)
|
||||
return
|
||||
}
|
||||
|
||||
seconds = int64(3000)
|
||||
got, err = redisPoolObj_expire.Expire(key, seconds)
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
if got != expected {
|
||||
t.Errorf("Expected to get %t, but got %t", expected, got)
|
||||
return
|
||||
}
|
||||
|
||||
expected2 = seconds - 5
|
||||
got2, exist, _, err = redisPoolObj_expire.TTL(key)
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
if !exist {
|
||||
t.Errorf("The key:%s should exist, but now it doesn't.", key)
|
||||
return
|
||||
}
|
||||
if got2 <= expected2 {
|
||||
t.Errorf("Expected to get a number bigger than %d, but now get %d.", expected2, got2)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func TestExpireAt(t *testing.T) {
|
||||
deleteKeys := make([]string, 0, 8)
|
||||
defer func() {
|
||||
// Delete the test keys
|
||||
distinctKeyList := getDistinctKeyList(deleteKeys)
|
||||
count, err := redisPoolObj_string.Del(distinctKeyList...)
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
if count != len(distinctKeyList) {
|
||||
t.Errorf("Expected to get %d, but now got %d", len(distinctKeyList), count)
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
||||
/*
|
||||
redis> SET cache www.google.com
|
||||
OK
|
||||
|
||||
redis> EXPIREAT cache '1609403601' # 这个 key 将在 Now()+1day 过期
|
||||
(integer) 1
|
||||
|
||||
redis> TTL cache
|
||||
(integer) 45081860
|
||||
*/
|
||||
key := "cache_page"
|
||||
value := "www.google.com"
|
||||
expected := true
|
||||
got, err := redisPoolObj_expire.Set(key, value, "", 0, "")
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
if got != expected {
|
||||
t.Errorf("Expected to get %t, but got %t", expected, got)
|
||||
return
|
||||
}
|
||||
deleteKeys = append(deleteKeys, key)
|
||||
|
||||
timestamp := time.Now().AddDate(0, 0, 1).Unix()
|
||||
expected = true
|
||||
got, err = redisPoolObj_expire.ExpireAt(key, timestamp)
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
if got != expected {
|
||||
t.Errorf("Expected to get %t, but got %t", expected, got)
|
||||
return
|
||||
}
|
||||
|
||||
expected2 := int64(86400 - 5)
|
||||
got2, exist, _, err := redisPoolObj_expire.TTL(key)
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
if !exist {
|
||||
t.Errorf("The key:%s should exist, but now it doesn't.", key)
|
||||
return
|
||||
}
|
||||
if got2 <= expected2 {
|
||||
t.Errorf("Expected to get a number bigger than %d, but now get %d.", expected2, got2)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func TestTTL(t *testing.T) {
|
||||
deleteKeys := make([]string, 0, 8)
|
||||
defer func() {
|
||||
// Delete the test keys
|
||||
distinctKeyList := getDistinctKeyList(deleteKeys)
|
||||
count, err := redisPoolObj_string.Del(distinctKeyList...)
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
if count != len(distinctKeyList) {
|
||||
t.Errorf("Expected to get %d, but now got %d", len(distinctKeyList), count)
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
||||
/*
|
||||
# 不存在的 key
|
||||
|
||||
redis> TTL key
|
||||
(integer) -2
|
||||
*/
|
||||
key := "key"
|
||||
expected := false
|
||||
_, got, _, err := redisPoolObj_expire.TTL(key)
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
if got != expected {
|
||||
t.Errorf("Expected to get %t, but got %t", expected, got)
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
# key 存在,但没有设置剩余生存时间
|
||||
|
||||
redis> SET key value
|
||||
OK
|
||||
|
||||
redis> TTL key
|
||||
(integer) -1
|
||||
*/
|
||||
value := "value"
|
||||
redisPoolObj_expire.Set(key, value, "", 0, "")
|
||||
deleteKeys = append(deleteKeys, key)
|
||||
|
||||
expected2 := int64(-1)
|
||||
got2, exist, _, err := redisPoolObj_expire.TTL(key)
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
if !exist {
|
||||
t.Errorf("The key:%s should exist, but now it doesn't.", key)
|
||||
return
|
||||
}
|
||||
if got2 != expected2 {
|
||||
t.Errorf("Expected to get %d, but got %d", expected2, got2)
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
# 有剩余生存时间的 key
|
||||
|
||||
redis> EXPIRE key 10086
|
||||
(integer) 1
|
||||
|
||||
redis> TTL key
|
||||
(integer) 10084
|
||||
*/
|
||||
seconds := int64(10086)
|
||||
expected = true
|
||||
got, err = redisPoolObj_expire.Expire(key, seconds)
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
if got != expected {
|
||||
t.Errorf("Expected to get %t, but got %t", expected, got)
|
||||
return
|
||||
}
|
||||
|
||||
expected2 = seconds - 5
|
||||
got2, exist, _, err = redisPoolObj_expire.TTL(key)
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
if !exist {
|
||||
t.Errorf("The key:%s should exist, but now it doesn't.", key)
|
||||
return
|
||||
}
|
||||
if got2 <= expected2 {
|
||||
t.Errorf("Expected to get a number bigger than %d, but now get %d.", expected2, got2)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func TestPersist(t *testing.T) {
|
||||
deleteKeys := make([]string, 0, 8)
|
||||
defer func() {
|
||||
// Delete the test keys
|
||||
distinctKeyList := getDistinctKeyList(deleteKeys)
|
||||
count, err := redisPoolObj_string.Del(distinctKeyList...)
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
if count != len(distinctKeyList) {
|
||||
t.Errorf("Expected to get %d, but now got %d", len(distinctKeyList), count)
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
||||
/*
|
||||
redis> SET mykey "Hello"
|
||||
OK
|
||||
*/
|
||||
key := "mykey"
|
||||
value := "Hello"
|
||||
redisPoolObj_expire.Set(key, value, "", 0, "")
|
||||
|
||||
/*
|
||||
redis> EXPIRE mykey 10 # 为 key 设置生存时间
|
||||
(integer) 1
|
||||
*/
|
||||
seconds := int64(10)
|
||||
expected := true
|
||||
got, err := redisPoolObj_expire.Expire(key, seconds)
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
if got != expected {
|
||||
t.Errorf("Expected to get %t, but got %t", expected, got)
|
||||
return
|
||||
}
|
||||
deleteKeys = append(deleteKeys, key)
|
||||
|
||||
/*
|
||||
redis> TTL mykey
|
||||
(integer) 10
|
||||
*/
|
||||
expected2 := seconds - 5
|
||||
got2, _, _, err := redisPoolObj_expire.TTL(key)
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
if got2 <= expected2 {
|
||||
t.Errorf("Expected to get %d, but got %d", expected2, got2)
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
redis> PERSIST mykey # 移除 key 的生存时间
|
||||
(integer) 1
|
||||
*/
|
||||
redisPoolObj_expire.Persist(key)
|
||||
|
||||
/*
|
||||
redis> TTL mykey
|
||||
(integer) -1
|
||||
*/
|
||||
expected = true
|
||||
_, _, got, err = redisPoolObj_expire.TTL(key)
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
if got != expected {
|
||||
t.Errorf("Expected to get %t, but got %t", expected, got)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func TestPExpire(t *testing.T) {
|
||||
deleteKeys := make([]string, 0, 8)
|
||||
defer func() {
|
||||
// Delete the test keys
|
||||
distinctKeyList := getDistinctKeyList(deleteKeys)
|
||||
count, err := redisPoolObj_string.Del(distinctKeyList...)
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
if count != len(distinctKeyList) {
|
||||
t.Errorf("Expected to get %d, but now got %d", len(distinctKeyList), count)
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
||||
/*
|
||||
redis> SET mykey "Hello"
|
||||
OK
|
||||
*/
|
||||
key := "mykey"
|
||||
value := "Hello"
|
||||
expected := true
|
||||
got, err := redisPoolObj_expire.Set(key, value, "", 0, "")
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
if got != expected {
|
||||
t.Errorf("Expected to get %t, but got %t", expected, got)
|
||||
return
|
||||
}
|
||||
deleteKeys = append(deleteKeys, key)
|
||||
|
||||
/*
|
||||
redis> PEXPIRE mykey 1500
|
||||
(integer) 1
|
||||
*/
|
||||
milliseconds := int64(1500)
|
||||
expected = true
|
||||
got, err = redisPoolObj_expire.PExpire(key, milliseconds)
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
if got != expected {
|
||||
t.Errorf("Expected to get %t, but got %t", expected, got)
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
redis> TTL mykey # TTL 的返回值以秒为单位
|
||||
(integer) 2
|
||||
|
||||
redis> PTTL mykey # PTTL 可以给出准确的毫秒数
|
||||
(integer) 1499
|
||||
*/
|
||||
expected2 := int64(1)
|
||||
got2, _, _, err := redisPoolObj_expire.TTL(key)
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
if got2 < expected2 {
|
||||
t.Errorf("TTL %d should be no less than %d", got2, expected2)
|
||||
return
|
||||
}
|
||||
|
||||
expected2 = int64(1000)
|
||||
got2, _, _, err = redisPoolObj_expire.PTTL(key)
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
if got2 < expected2 {
|
||||
t.Errorf("TTL %d should be no less than %d", got2, expected2)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func TestPExpireAt(t *testing.T) {
|
||||
deleteKeys := make([]string, 0, 8)
|
||||
defer func() {
|
||||
// Delete the test keys
|
||||
distinctKeyList := getDistinctKeyList(deleteKeys)
|
||||
count, err := redisPoolObj_string.Del(distinctKeyList...)
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
if count != len(distinctKeyList) {
|
||||
t.Errorf("Expected to get %d, but now got %d", len(distinctKeyList), count)
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
||||
/*
|
||||
redis> SET mykey "Hello"
|
||||
OK
|
||||
*/
|
||||
key := "mykey"
|
||||
value := "Hello"
|
||||
expected := true
|
||||
got, err := redisPoolObj_expire.Set(key, value, "", 0, "")
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
if got != expected {
|
||||
t.Errorf("Expected to get %t, but got %t", expected, got)
|
||||
return
|
||||
}
|
||||
deleteKeys = append(deleteKeys, key)
|
||||
|
||||
/*
|
||||
redis> PEXPIREAT mykey 1609403601005
|
||||
(integer) 1
|
||||
*/
|
||||
milliseconds_timestamp := time.Now().AddDate(0, 0, 1).Unix() * 1000
|
||||
redisPoolObj_expire.PExpireAt(key, milliseconds_timestamp)
|
||||
|
||||
/*
|
||||
redis> TTL mykey # TTL 返回秒
|
||||
(integer) 223157079
|
||||
|
||||
redis> PTTL mykey # PTTL 返回毫秒
|
||||
(integer) 223157079318
|
||||
*/
|
||||
expected2 := int64(86400 - 5)
|
||||
got2, exist, _, err := redisPoolObj_expire.TTL(key)
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
if !exist {
|
||||
t.Errorf("The key:%s should exist, but now it doesn't.", key)
|
||||
return
|
||||
}
|
||||
if got2 <= expected2 {
|
||||
t.Errorf("Expected to get a number bigger than %d, but now get %d.", expected2, got2)
|
||||
return
|
||||
}
|
||||
|
||||
expected2 = int64(86400000 - 5)
|
||||
got2, exist, _, err = redisPoolObj_expire.PTTL(key)
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
if !exist {
|
||||
t.Errorf("The key:%s should exist, but now it doesn't.", key)
|
||||
return
|
||||
}
|
||||
if got2 <= expected2 {
|
||||
t.Errorf("Expected to get a number bigger than %d, but now get %d.", expected2, got2)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func TestPTTL(t *testing.T) {
|
||||
deleteKeys := make([]string, 0, 8)
|
||||
defer func() {
|
||||
// Delete the test keys
|
||||
distinctKeyList := getDistinctKeyList(deleteKeys)
|
||||
count, err := redisPoolObj_string.Del(distinctKeyList...)
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
if count != len(distinctKeyList) {
|
||||
t.Errorf("Expected to get %d, but now got %d", len(distinctKeyList), count)
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
||||
/*
|
||||
# 不存在的 key
|
||||
|
||||
redis> PTTL key
|
||||
(integer) -2
|
||||
*/
|
||||
key := "key"
|
||||
expected := false
|
||||
_, got, _, err := redisPoolObj_expire.PTTL(key)
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
if got != expected {
|
||||
t.Errorf("Expected to get %t, but got %t", expected, got)
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
# key 存在,但没有设置剩余生存时间
|
||||
|
||||
redis> SET key value
|
||||
OK
|
||||
|
||||
redis> PTTL key
|
||||
(integer) -1
|
||||
*/
|
||||
value := "value"
|
||||
redisPoolObj_expire.Set(key, value, "", 0, "")
|
||||
deleteKeys = append(deleteKeys, key)
|
||||
|
||||
expected = true
|
||||
_, _, got, err = redisPoolObj_expire.TTL(key)
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
if got != expected {
|
||||
t.Errorf("Expected to get %t, but got %t", expected, got)
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
# 有剩余生存时间的 key
|
||||
|
||||
redis> PEXPIRE key 10086
|
||||
(integer) 1
|
||||
|
||||
redis> PTTL key
|
||||
(integer) 6179
|
||||
*/
|
||||
milliseconds := int64(10086)
|
||||
expected = true
|
||||
got, err = redisPoolObj_expire.PExpire(key, milliseconds)
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
if got != expected {
|
||||
t.Errorf("Expected to get %t, but got %t", expected, got)
|
||||
return
|
||||
}
|
||||
|
||||
expected2 := milliseconds - 500
|
||||
got2, exist, _, err := redisPoolObj_expire.PTTL(key)
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
if !exist {
|
||||
t.Errorf("The key:%s should exist, but now it doesn't.", key)
|
||||
return
|
||||
}
|
||||
if got2 <= expected2 {
|
||||
t.Errorf("Expected to get a number bigger than %d, but now get %d.", expected2, got2)
|
||||
return
|
||||
}
|
||||
}
|
||||
412
trunk/goutil/redisUtil/redisPool_hash.go
Normal file
412
trunk/goutil/redisUtil/redisPool_hash.go
Normal file
@@ -0,0 +1,412 @@
|
||||
/*
|
||||
未实现的哈希表方法:
|
||||
HSCAN
|
||||
*/
|
||||
package redisUtil
|
||||
|
||||
import (
|
||||
"github.com/gomodule/redigo/redis"
|
||||
)
|
||||
|
||||
/*
|
||||
HSET hash field value
|
||||
可用版本: >= 2.0.0
|
||||
时间复杂度: O(1)
|
||||
将哈希表 hash 中域 field 的值设置为 value 。
|
||||
|
||||
如果给定的哈希表并不存在, 那么一个新的哈希表将被创建并执行 HSET 操作。
|
||||
|
||||
如果域 field 已经存在于哈希表中, 那么它的旧值将被新值 value 覆盖。
|
||||
|
||||
返回值
|
||||
当 HSET 命令在哈希表中新创建 field 域并成功为它设置值时, 命令返回 1 ; 如果域 field 已经存在于哈希表, 并且 HSET 命令成功使用新值覆盖了它的旧值, 那么命令返回 0 。
|
||||
*/
|
||||
func (this *RedisPool) HSet(key, field string, value interface{}) (result int, err error) {
|
||||
conn := this.GetConnection()
|
||||
defer conn.Close()
|
||||
|
||||
result, err = redis.Int(conn.Do("HSET", key, field, value))
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
HSETNX hash field value
|
||||
可用版本: >= 2.0.0
|
||||
时间复杂度: O(1)
|
||||
当且仅当域 field 尚未存在于哈希表的情况下, 将它的值设置为 value 。
|
||||
|
||||
如果给定域已经存在于哈希表当中, 那么命令将放弃执行设置操作。
|
||||
|
||||
如果哈希表 hash 不存在, 那么一个新的哈希表将被创建并执行 HSETNX 命令。
|
||||
|
||||
返回值
|
||||
HSETNX 命令在设置成功时返回 1 , 在给定域已经存在而放弃执行设置操作时返回 0 。
|
||||
*/
|
||||
func (this *RedisPool) HSetNX(key, field string, value interface{}) (successful bool, err error) {
|
||||
conn := this.GetConnection()
|
||||
defer conn.Close()
|
||||
|
||||
var result int
|
||||
result, err = redis.Int(conn.Do("HSETNX", key, field, value))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if result == 1 {
|
||||
successful = true
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
HGET hash field
|
||||
可用版本: >= 2.0.0
|
||||
时间复杂度: O(1)
|
||||
返回哈希表中给定域的值。
|
||||
|
||||
返回值
|
||||
HGET 命令在默认情况下返回给定域的值。
|
||||
|
||||
如果给定域不存在于哈希表中, 又或者给定的哈希表并不存在, 那么命令返回 nil 。
|
||||
*/
|
||||
func (this *RedisPool) HGet(key, field string) (value interface{}, exist bool, err error) {
|
||||
conn := this.GetConnection()
|
||||
defer conn.Close()
|
||||
|
||||
value, err = conn.Do("HGET", key, field)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if value == nil {
|
||||
return
|
||||
}
|
||||
|
||||
exist = true
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
HEXISTS hash field
|
||||
可用版本: >= 2.0.0
|
||||
时间复杂度: O(1)
|
||||
检查给定域 field 是否存在于哈希表 hash 当中。
|
||||
|
||||
返回值
|
||||
HEXISTS 命令在给定域存在时返回 1 , 在给定域不存在时返回 0 。
|
||||
*/
|
||||
func (this *RedisPool) HExists(key, field string) (exist bool, err error) {
|
||||
conn := this.GetConnection()
|
||||
defer conn.Close()
|
||||
|
||||
var result int
|
||||
result, err = redis.Int(conn.Do("HEXISTS", key, field))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if result == 1 {
|
||||
exist = true
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
HDEL
|
||||
HDEL key field [field …]
|
||||
|
||||
删除哈希表 key 中的一个或多个指定域,不存在的域将被忽略。
|
||||
|
||||
Note
|
||||
|
||||
在Redis2.4以下的版本里, HDEL 每次只能删除单个域,如果你需要在一个原子时间内删除多个域,请将命令包含在 MULTI / EXEC 块内。
|
||||
|
||||
可用版本:
|
||||
>= 2.0.0
|
||||
|
||||
时间复杂度:
|
||||
O(N), N 为要删除的域的数量。
|
||||
|
||||
返回值:
|
||||
被成功移除的域的数量,不包括被忽略的域。
|
||||
*/
|
||||
func (this *RedisPool) HDel(key string, field ...string) (succeedCount int, err error) {
|
||||
conn := this.GetConnection()
|
||||
defer conn.Close()
|
||||
|
||||
succeedCount, err = redis.Int(conn.Do("HDEL", redis.Args{}.Add(key).AddFlat(field)...))
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
HLEN
|
||||
HLEN key
|
||||
|
||||
返回哈希表 key 中域的数量。
|
||||
|
||||
时间复杂度:
|
||||
O(1)
|
||||
|
||||
返回值:
|
||||
哈希表中域的数量。
|
||||
当 key 不存在时,返回 0 。
|
||||
*/
|
||||
func (this *RedisPool) HLen(key string) (count int, err error) {
|
||||
conn := this.GetConnection()
|
||||
defer conn.Close()
|
||||
|
||||
count, err = redis.Int(conn.Do("HLEN", key))
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
HSTRLEN
|
||||
HSTRLEN key field
|
||||
|
||||
返回哈希表 key 中, 与给定域 field 相关联的值的字符串长度(string length)。
|
||||
|
||||
如果给定的键或者域不存在, 那么命令返回 0 。
|
||||
|
||||
可用版本:
|
||||
>= 3.2.0
|
||||
|
||||
时间复杂度:
|
||||
O(1)
|
||||
|
||||
返回值:
|
||||
一个整数。
|
||||
*/
|
||||
func (this *RedisPool) HStrlen(key, field string) (length int, err error) {
|
||||
conn := this.GetConnection()
|
||||
defer conn.Close()
|
||||
|
||||
length, err = redis.Int(conn.Do("HSTRLEN", key, field))
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
HINCRBY
|
||||
HINCRBY key field increment
|
||||
|
||||
为哈希表 key 中的域 field 的值加上增量 increment 。
|
||||
|
||||
增量也可以为负数,相当于对给定域进行减法操作。
|
||||
|
||||
如果 key 不存在,一个新的哈希表被创建并执行 HINCRBY 命令。
|
||||
|
||||
如果域 field 不存在,那么在执行命令前,域的值被初始化为 0 。
|
||||
|
||||
对一个储存字符串值的域 field 执行 HINCRBY 命令将造成一个错误。
|
||||
|
||||
本操作的值被限制在 64 位(bit)有符号数字表示之内。
|
||||
|
||||
可用版本:
|
||||
>= 2.0.0
|
||||
|
||||
时间复杂度:
|
||||
O(1)
|
||||
|
||||
返回值:
|
||||
执行 HINCRBY 命令之后,哈希表 key 中域 field 的值。
|
||||
*/
|
||||
func (this *RedisPool) HIncrBy(key, field string, increment int64) (newValue int64, err error) {
|
||||
conn := this.GetConnection()
|
||||
defer conn.Close()
|
||||
|
||||
newValue, err = redis.Int64(conn.Do("HINCRBY", key, field, increment))
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
HINCRBYFLOAT
|
||||
HINCRBYFLOAT key field increment
|
||||
|
||||
为哈希表 key 中的域 field 加上浮点数增量 increment 。
|
||||
|
||||
如果哈希表中没有域 field ,那么 HINCRBYFLOAT 会先将域 field 的值设为 0 ,然后再执行加法操作。
|
||||
|
||||
如果键 key 不存在,那么 HINCRBYFLOAT 会先创建一个哈希表,再创建域 field ,最后再执行加法操作。
|
||||
|
||||
当以下任意一个条件发生时,返回一个错误:
|
||||
|
||||
域 field 的值不是字符串类型(因为 redis 中的数字和浮点数都以字符串的形式保存,所以它们都属于字符串类型)
|
||||
|
||||
域 field 当前的值或给定的增量 increment 不能解释(parse)为双精度浮点数(double precision floating point number)
|
||||
|
||||
HINCRBYFLOAT 命令的详细功能和 INCRBYFLOAT key increment 命令类似,请查看 INCRBYFLOAT key increment 命令获取更多相关信息。
|
||||
|
||||
可用版本:
|
||||
>= 2.6.0
|
||||
|
||||
时间复杂度:
|
||||
O(1)
|
||||
|
||||
返回值:
|
||||
执行加法操作之后 field 域的值。
|
||||
*/
|
||||
func (this *RedisPool) HIncrByFloat(key, field string, increment float64) (newValue float64, err error) {
|
||||
conn := this.GetConnection()
|
||||
defer conn.Close()
|
||||
|
||||
newValue, err = redis.Float64(conn.Do("HINCRBYFLOAT", key, field, increment))
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
HMSET
|
||||
HMSET key field value [field value …]
|
||||
|
||||
同时将多个 field-value (域-值)对设置到哈希表 key 中。
|
||||
|
||||
此命令会覆盖哈希表中已存在的域。
|
||||
|
||||
如果 key 不存在,一个空哈希表被创建并执行 HMSET 操作。
|
||||
|
||||
可用版本:
|
||||
>= 2.0.0
|
||||
|
||||
时间复杂度:
|
||||
O(N), N 为 field-value 对的数量。
|
||||
|
||||
返回值:
|
||||
如果命令执行成功,返回 OK 。
|
||||
当 key 不是哈希表(hash)类型时,返回一个错误。
|
||||
*/
|
||||
func (this *RedisPool) HMSet(key string, value interface{}) (err error) {
|
||||
conn := this.GetConnection()
|
||||
defer conn.Close()
|
||||
|
||||
_, err = conn.Do("HMSET", redis.Args{}.Add(key).AddFlat(value)...)
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
HMGET
|
||||
HMGET key field [field …]
|
||||
|
||||
返回哈希表 key 中,一个或多个给定域的值。
|
||||
|
||||
如果给定的域不存在于哈希表,那么返回一个 nil 值。
|
||||
|
||||
因为不存在的 key 被当作一个空哈希表来处理,所以对一个不存在的 key 进行 HMGET 操作将返回一个只带有 nil 值的表。
|
||||
|
||||
可用版本:
|
||||
>= 2.0.0
|
||||
|
||||
时间复杂度:
|
||||
O(N), N 为给定域的数量。
|
||||
|
||||
返回值:
|
||||
一个包含多个给定域的关联值的表,表值的排列顺序和给定域参数的请求顺序一样。
|
||||
*/
|
||||
func (this *RedisPool) HMGet(key string, fields ...string) (reply interface{}, exist bool, err error) {
|
||||
conn := this.GetConnection()
|
||||
defer conn.Close()
|
||||
|
||||
reply, err = conn.Do("HMGET", redis.Args{}.Add(key).AddFlat(fields)...)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if reply == nil {
|
||||
return
|
||||
}
|
||||
|
||||
exist = true
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
HKEYS
|
||||
HKEYS key
|
||||
|
||||
返回哈希表 key 中的所有域。
|
||||
|
||||
可用版本:
|
||||
>= 2.0.0
|
||||
|
||||
时间复杂度:
|
||||
O(N), N 为哈希表的大小。
|
||||
|
||||
返回值:
|
||||
一个包含哈希表中所有域的表。
|
||||
当 key 不存在时,返回一个空表。
|
||||
*/
|
||||
func (this *RedisPool) HKeys(key string) (fieldList []string, err error) {
|
||||
conn := this.GetConnection()
|
||||
defer conn.Close()
|
||||
|
||||
fieldList, err = redis.Strings(conn.Do("HKEYS", key))
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
VALS
|
||||
HVALS key
|
||||
|
||||
返回哈希表 key 中所有域的值。
|
||||
|
||||
可用版本:
|
||||
>= 2.0.0
|
||||
|
||||
时间复杂度:
|
||||
O(N), N 为哈希表的大小。
|
||||
|
||||
返回值:
|
||||
一个包含哈希表中所有值的表。
|
||||
当 key 不存在时,返回一个空表。
|
||||
*/
|
||||
func (this *RedisPool) HVals(key string) (reply interface{}, err error) {
|
||||
conn := this.GetConnection()
|
||||
defer conn.Close()
|
||||
|
||||
reply, err = conn.Do("HVALS", key)
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
HGETALL
|
||||
HGETALL key
|
||||
|
||||
返回哈希表 key 中,所有的域和值。
|
||||
|
||||
在返回值里,紧跟每个域名(field name)之后是域的值(value),所以返回值的长度是哈希表大小的两倍。
|
||||
|
||||
可用版本:
|
||||
>= 2.0.0
|
||||
|
||||
时间复杂度:
|
||||
O(N), N 为哈希表的大小。
|
||||
|
||||
返回值:
|
||||
以列表形式返回哈希表的域和域的值。
|
||||
若 key 不存在,返回空列表。
|
||||
*/
|
||||
func (this *RedisPool) HGetAll(key string) (reply interface{}, err error) {
|
||||
conn := this.GetConnection()
|
||||
defer conn.Close()
|
||||
|
||||
reply, err = conn.Do("HGETALL", key)
|
||||
return
|
||||
}
|
||||
|
||||
func (this *RedisPool) HGetAll_Struct(key string, obj interface{}) (exist bool, err error) {
|
||||
conn := this.GetConnection()
|
||||
defer conn.Close()
|
||||
|
||||
values, err := redis.Values(conn.Do("HGETALL", key))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if len(values) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
err = redis.ScanStruct(values, obj)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
exist = true
|
||||
return
|
||||
}
|
||||
1501
trunk/goutil/redisUtil/redisPool_hash_test.go
Normal file
1501
trunk/goutil/redisUtil/redisPool_hash_test.go
Normal file
File diff suppressed because it is too large
Load Diff
378
trunk/goutil/redisUtil/redisPool_list.go
Normal file
378
trunk/goutil/redisUtil/redisPool_list.go
Normal file
@@ -0,0 +1,378 @@
|
||||
/*
|
||||
未实现的列表方法:
|
||||
BLPOP、BRPOP、BRPOPLPUSH
|
||||
*/
|
||||
package redisUtil
|
||||
|
||||
import (
|
||||
"github.com/gomodule/redigo/redis"
|
||||
)
|
||||
|
||||
/*
|
||||
LPUSH key value [value …]
|
||||
可用版本: >= 1.0.0
|
||||
时间复杂度: O(1)
|
||||
将一个或多个值 value 插入到列表 key 的表头
|
||||
|
||||
如果有多个 value 值,那么各个 value 值按从左到右的顺序依次插入到表头: 比如说,对空列表 mylist 执行命令 LPUSH mylist a b c ,列表的值将是 c b a ,这等同于原子性地执行 LPUSH mylist a 、 LPUSH mylist b 和 LPUSH mylist c 三个命令。
|
||||
|
||||
如果 key 不存在,一个空列表会被创建并执行 LPUSH 操作。
|
||||
|
||||
当 key 存在但不是列表类型时,返回一个错误。
|
||||
|
||||
Note
|
||||
|
||||
在Redis 2.4版本以前的 LPUSH 命令,都只接受单个 value 值。
|
||||
|
||||
返回值
|
||||
执行 LPUSH 命令后,列表的长度。
|
||||
*/
|
||||
func (this *RedisPool) LPush(key string, values ...interface{}) (length int, err error) {
|
||||
conn := this.GetConnection()
|
||||
defer conn.Close()
|
||||
|
||||
length, err = redis.Int(conn.Do("LPUSH", redis.Args{}.Add(key).AddFlat(values)...))
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
LPUSHX key value
|
||||
可用版本: >= 2.2.0
|
||||
时间复杂度: O(1)
|
||||
将值 value 插入到列表 key 的表头,当且仅当 key 存在并且是一个列表。
|
||||
|
||||
和 LPUSH key value [value …] 命令相反,当 key 不存在时, LPUSHX 命令什么也不做。
|
||||
|
||||
返回值
|
||||
LPUSHX 命令执行之后,表的长度。
|
||||
*/
|
||||
func (this *RedisPool) LPushX(key string, values ...interface{}) (length int, err error) {
|
||||
conn := this.GetConnection()
|
||||
defer conn.Close()
|
||||
|
||||
length, err = redis.Int(conn.Do("LPUSHX", redis.Args{}.Add(key).AddFlat(values)...))
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
RPUSH key value [value …]
|
||||
可用版本: >= 1.0.0
|
||||
时间复杂度: O(1)
|
||||
将一个或多个值 value 插入到列表 key 的表尾(最右边)。
|
||||
|
||||
如果有多个 value 值,那么各个 value 值按从左到右的顺序依次插入到表尾:比如对一个空列表 mylist 执行 RPUSH mylist a b c ,得出的结果列表为 a b c ,等同于执行命令 RPUSH mylist a 、 RPUSH mylist b 、 RPUSH mylist c 。
|
||||
|
||||
如果 key 不存在,一个空列表会被创建并执行 RPUSH 操作。
|
||||
|
||||
当 key 存在但不是列表类型时,返回一个错误。
|
||||
|
||||
Note
|
||||
|
||||
在 Redis 2.4 版本以前的 RPUSH 命令,都只接受单个 value 值。
|
||||
|
||||
返回值
|
||||
执行 RPUSH 操作后,表的长度。
|
||||
*/
|
||||
func (this *RedisPool) RPush(key string, values ...interface{}) (length int, err error) {
|
||||
conn := this.GetConnection()
|
||||
defer conn.Close()
|
||||
|
||||
length, err = redis.Int(conn.Do("RPUSH", redis.Args{}.Add(key).AddFlat(values)...))
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
RPUSHX key value
|
||||
可用版本: >= 2.2.0
|
||||
时间复杂度: O(1)
|
||||
将值 value 插入到列表 key 的表尾,当且仅当 key 存在并且是一个列表。
|
||||
|
||||
和 RPUSH key value [value …] 命令相反,当 key 不存在时, RPUSHX 命令什么也不做。
|
||||
|
||||
返回值
|
||||
RPUSHX 命令执行之后,表的长度。
|
||||
*/
|
||||
func (this *RedisPool) RPushX(key string, values ...interface{}) (length int, err error) {
|
||||
conn := this.GetConnection()
|
||||
defer conn.Close()
|
||||
|
||||
length, err = redis.Int(conn.Do("RPUSHX", redis.Args{}.Add(key).AddFlat(values)...))
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
LPOP key
|
||||
可用版本: >= 1.0.0
|
||||
时间复杂度: O(1)
|
||||
移除并返回列表 key 的头元素。
|
||||
|
||||
返回值
|
||||
列表的头元素。 当 key 不存在时,返回 nil 。
|
||||
*/
|
||||
func (this *RedisPool) LPop(key string) (item interface{}, exist bool, err error) {
|
||||
conn := this.GetConnection()
|
||||
defer conn.Close()
|
||||
|
||||
item, err = conn.Do("LPOP", key)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if item == nil {
|
||||
return
|
||||
}
|
||||
|
||||
exist = true
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
RPOP key
|
||||
可用版本: >= 1.0.0
|
||||
时间复杂度: O(1)
|
||||
移除并返回列表 key 的尾元素。
|
||||
|
||||
返回值
|
||||
列表的尾元素。 当 key 不存在时,返回 nil 。
|
||||
*/
|
||||
func (this *RedisPool) RPop(key string) (item interface{}, exist bool, err error) {
|
||||
conn := this.GetConnection()
|
||||
defer conn.Close()
|
||||
|
||||
item, err = conn.Do("RPOP", key)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if item == nil {
|
||||
return
|
||||
}
|
||||
|
||||
exist = true
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
RPOPLPUSH source destination
|
||||
可用版本: >= 1.2.0
|
||||
时间复杂度: O(1)
|
||||
命令 RPOPLPUSH 在一个原子时间内,执行以下两个动作:
|
||||
|
||||
将列表 source 中的最后一个元素(尾元素)弹出,并返回给客户端。
|
||||
|
||||
将 source 弹出的元素插入到列表 destination ,作为 destination 列表的的头元素。
|
||||
|
||||
举个例子,你有两个列表 source 和 destination , source 列表有元素 a, b, c , destination 列表有元素 x, y, z ,执行 RPOPLPUSH source destination 之后, source 列表包含元素 a, b , destination 列表包含元素 c, x, y, z ,并且元素 c 会被返回给客户端。
|
||||
|
||||
如果 source 不存在,值 nil 被返回,并且不执行其他动作。
|
||||
|
||||
如果 source 和 destination 相同,则列表中的表尾元素被移动到表头,并返回该元素,可以把这种特殊情况视作列表的旋转(rotation)操作。
|
||||
|
||||
返回值
|
||||
被弹出的元素。
|
||||
*/
|
||||
func (this *RedisPool) RPopLPush(source, destination string) (item interface{}, err error) {
|
||||
conn := this.GetConnection()
|
||||
defer conn.Close()
|
||||
|
||||
item, err = conn.Do("RPOPLPUSH", source, destination)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
LREM key count value
|
||||
可用版本: >= 1.0.0
|
||||
时间复杂度: O(N), N 为列表的长度。
|
||||
根据参数 count 的值,移除列表中与参数 value 相等的元素。
|
||||
|
||||
count 的值可以是以下几种:
|
||||
|
||||
count > 0 : 从表头开始向表尾搜索,移除与 value 相等的元素,数量为 count 。
|
||||
|
||||
count < 0 : 从表尾开始向表头搜索,移除与 value 相等的元素,数量为 count 的绝对值。
|
||||
|
||||
count = 0 : 移除表中所有与 value 相等的值。
|
||||
|
||||
返回值
|
||||
被移除元素的数量。 因为不存在的 key 被视作空表(empty list),所以当 key 不存在时, LREM 命令总是返回 0 。
|
||||
*/
|
||||
// 错误对象
|
||||
func (this *RedisPool) LRem(key string, count int, value string) (removeCount int, err error) {
|
||||
conn := this.GetConnection()
|
||||
defer conn.Close()
|
||||
|
||||
removeCount, err = redis.Int(conn.Do("LREM", key, count, value))
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
LLEN key
|
||||
可用版本: >= 1.0.0
|
||||
时间复杂度: O(1)
|
||||
返回列表 key 的长度。
|
||||
|
||||
如果 key 不存在,则 key 被解释为一个空列表,返回 0 .
|
||||
|
||||
如果 key 不是列表类型,返回一个错误。
|
||||
|
||||
返回值
|
||||
列表 key 的长度。
|
||||
*/
|
||||
func (this *RedisPool) LLen(key string) (length int, err error) {
|
||||
conn := this.GetConnection()
|
||||
defer conn.Close()
|
||||
|
||||
length, err = redis.Int(conn.Do("LLEN", key))
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
LINDEX key index
|
||||
可用版本: >= 1.0.0
|
||||
时间复杂度:O(N), N 为到达下标 index 过程中经过的元素数量。因此,对列表的头元素和尾元素执行 LINDEX 命令,复杂度为O(1)。
|
||||
返回列表 key 中,下标为 index 的元素。
|
||||
|
||||
下标(index)参数 start 和 stop 都以 0 为底,也就是说,以 0 表示列表的第一个元素,以 1 表示列表的第二个元素,以此类推。
|
||||
|
||||
你也可以使用负数下标,以 -1 表示列表的最后一个元素, -2 表示列表的倒数第二个元素,以此类推。
|
||||
|
||||
如果 key 不是列表类型,返回一个错误。
|
||||
|
||||
返回值
|
||||
列表中下标为 index 的元素。 如果 index 参数的值不在列表的区间范围内(out of range),返回 nil 。
|
||||
*/
|
||||
func (this *RedisPool) LIndex(key string, index int) (item interface{}, exist bool, err error) {
|
||||
conn := this.GetConnection()
|
||||
defer conn.Close()
|
||||
|
||||
item, err = conn.Do("LINDEX", key, index)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if item == nil {
|
||||
return
|
||||
}
|
||||
|
||||
exist = true
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
LINSERT key BEFORE|AFTER pivot value
|
||||
可用版本: >= 2.2.0
|
||||
时间复杂度: O(N), N 为寻找 pivot 过程中经过的元素数量。
|
||||
将值 value 插入到列表 key 当中,位于值 pivot 之前或之后。
|
||||
|
||||
当 pivot 不存在于列表 key 时,不执行任何操作。
|
||||
|
||||
当 key 不存在时, key 被视为空列表,不执行任何操作。
|
||||
|
||||
如果 key 不是列表类型,返回一个错误。
|
||||
|
||||
返回值
|
||||
如果命令执行成功,返回插入操作完成之后,列表的长度。 如果没有找到 pivot ,返回 -1 。 如果 key 不存在或为空列表,返回 0 。
|
||||
*/
|
||||
func (this *RedisPool) LInsert(key, beforeOrAfter string, pivot, value interface{}) (length int, err error) {
|
||||
conn := this.GetConnection()
|
||||
defer conn.Close()
|
||||
|
||||
length, err = redis.Int(conn.Do("LINSERT", key, beforeOrAfter, pivot, value))
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
LSET key index value
|
||||
可用版本: >= 1.0.0
|
||||
时间复杂度:对头元素或尾元素进行 LSET 操作,复杂度为 O(1)。其他情况下,为 O(N), N 为列表的长度。
|
||||
将列表 key 下标为 index 的元素的值设置为 value 。
|
||||
|
||||
当 index 参数超出范围,或对一个空列表( key 不存在)进行 LSET 时,返回一个错误。
|
||||
|
||||
关于列表下标的更多信息,请参考 LINDEX key index 命令。
|
||||
|
||||
返回值
|
||||
操作成功返回 ok ,否则返回错误信息。
|
||||
*/
|
||||
func (this *RedisPool) LSet(key string, index int, value interface{}) (err error) {
|
||||
conn := this.GetConnection()
|
||||
defer conn.Close()
|
||||
|
||||
_, err = conn.Do("LSet", key, index, value)
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
LRANGE key start stop
|
||||
可用版本: >= 1.0.0
|
||||
时间复杂度: O(S+N), S 为偏移量 start , N 为指定区间内元素的数量。
|
||||
返回列表 key 中指定区间内的元素,区间以偏移量 start 和 stop 指定。
|
||||
|
||||
下标(index)参数 start 和 stop 都以 0 为底,也就是说,以 0 表示列表的第一个元素,以 1 表示列表的第二个元素,以此类推。
|
||||
|
||||
你也可以使用负数下标,以 -1 表示列表的最后一个元素, -2 表示列表的倒数第二个元素,以此类推。
|
||||
|
||||
注意LRANGE命令和编程语言区间函数的区别
|
||||
假如你有一个包含一百个元素的列表,对该列表执行 LRANGE list 0 10 ,结果是一个包含11个元素的列表,这表明 stop 下标也在 LRANGE 命令的取值范围之内(闭区间),这和某些语言的区间函数可能不一致,比如Ruby的 Range.new 、 Array#slice 和Python的 range() 函数。
|
||||
|
||||
超出范围的下标
|
||||
超出范围的下标值不会引起错误。
|
||||
|
||||
如果 start 下标比列表的最大下标 end ( LLEN list 减去 1 )还要大,那么 LRANGE 返回一个空列表。
|
||||
|
||||
如果 stop 下标比 end 下标还要大,Redis将 stop 的值设置为 end 。
|
||||
|
||||
返回值
|
||||
一个列表,包含指定区间内的元素。
|
||||
*/
|
||||
func (this *RedisPool) LRange(key string, start, stop int) (reply interface{}, err error) {
|
||||
conn := this.GetConnection()
|
||||
defer conn.Close()
|
||||
|
||||
reply, err = conn.Do("LRANGE", key, start, stop)
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
LTRIM key start stop
|
||||
可用版本: >= 1.0.0
|
||||
时间复杂度: O(N), N 为被移除的元素的数量。
|
||||
对一个列表进行修剪(trim),就是说,让列表只保留指定区间内的元素,不在指定区间之内的元素都将被删除。
|
||||
|
||||
举个例子,执行命令 LTRIM list 0 2 ,表示只保留列表 list 的前三个元素,其余元素全部删除。
|
||||
|
||||
下标(index)参数 start 和 stop 都以 0 为底,也就是说,以 0 表示列表的第一个元素,以 1 表示列表的第二个元素,以此类推。
|
||||
|
||||
你也可以使用负数下标,以 -1 表示列表的最后一个元素, -2 表示列表的倒数第二个元素,以此类推。
|
||||
|
||||
当 key 不是列表类型时,返回一个错误。
|
||||
|
||||
LTRIM 命令通常和 LPUSH key value [value …] 命令或 RPUSH key value [value …] 命令配合使用,举个例子:
|
||||
|
||||
LPUSH log newest_log
|
||||
LTRIM log 0 99
|
||||
这个例子模拟了一个日志程序,每次将最新日志 newest_log 放到 log 列表中,并且只保留最新的 100 项。注意当这样使用 LTRIM 命令时,时间复杂度是O(1),因为平均情况下,每次只有一个元素被移除。
|
||||
|
||||
注意LTRIM命令和编程语言区间函数的区别
|
||||
假如你有一个包含一百个元素的列表 list ,对该列表执行 LTRIM list 0 10 ,结果是一个包含11个元素的列表,这表明 stop 下标也在 LTRIM 命令的取值范围之内(闭区间),这和某些语言的区间函数可能不一致,比如Ruby的 Range.new 、 Array#slice 和Python的 range() 函数。
|
||||
|
||||
超出范围的下标
|
||||
超出范围的下标值不会引起错误。
|
||||
|
||||
如果 start 下标比列表的最大下标 end ( LLEN list 减去 1 )还要大,或者 start > stop , LTRIM 返回一个空列表(因为 LTRIM 已经将整个列表清空)。
|
||||
|
||||
如果 stop 下标比 end 下标还要大,Redis将 stop 的值设置为 end 。
|
||||
|
||||
返回值
|
||||
命令执行成功时,返回 ok 。
|
||||
*/
|
||||
func (this *RedisPool) LTrim(key string, start, stop int) (err error) {
|
||||
conn := this.GetConnection()
|
||||
defer conn.Close()
|
||||
|
||||
_, err = conn.Do("LTRIM", key, start, stop)
|
||||
return
|
||||
}
|
||||
1888
trunk/goutil/redisUtil/redisPool_list_test.go
Normal file
1888
trunk/goutil/redisUtil/redisPool_list_test.go
Normal file
File diff suppressed because it is too large
Load Diff
309
trunk/goutil/redisUtil/redisPool_set.go
Normal file
309
trunk/goutil/redisUtil/redisPool_set.go
Normal file
@@ -0,0 +1,309 @@
|
||||
/*
|
||||
未实现的列表方法:
|
||||
SSCAN
|
||||
*/
|
||||
package redisUtil
|
||||
|
||||
import (
|
||||
"github.com/gomodule/redigo/redis"
|
||||
)
|
||||
|
||||
/*
|
||||
SADD key member [member …]
|
||||
可用版本: >= 1.0.0
|
||||
时间复杂度: O(N), N 是被添加的元素的数量。
|
||||
将一个或多个 member 元素加入到集合 key 当中,已经存在于集合的 member 元素将被忽略。
|
||||
|
||||
假如 key 不存在,则创建一个只包含 member 元素作成员的集合。
|
||||
|
||||
当 key 不是集合类型时,返回一个错误。
|
||||
|
||||
Note
|
||||
|
||||
在Redis2.4版本以前, SADD 只接受单个 member 值。
|
||||
|
||||
返回值
|
||||
被添加到集合中的新元素的数量,不包括被忽略的元素。
|
||||
*/
|
||||
func (this *RedisPool) SAdd(key string, values ...interface{}) (newCount int, err error) {
|
||||
conn := this.GetConnection()
|
||||
defer conn.Close()
|
||||
|
||||
newCount, err = redis.Int(conn.Do("SADD", redis.Args{}.Add(key).AddFlat(values)...))
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
SISMEMBER key member
|
||||
可用版本: >= 1.0.0
|
||||
时间复杂度: O(1)
|
||||
判断 member 元素是否集合 key 的成员。
|
||||
|
||||
返回值
|
||||
如果 member 元素是集合的成员,返回 1 。 如果 member 元素不是集合的成员,或 key 不存在,返回 0 。
|
||||
*/
|
||||
func (this *RedisPool) SIsMember(key string, value interface{}) (isMember bool, err error) {
|
||||
conn := this.GetConnection()
|
||||
defer conn.Close()
|
||||
|
||||
isMember, err = redis.Bool(conn.Do("SISMEMBER", key, value))
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
SPOP key
|
||||
可用版本: >= 1.0.0
|
||||
时间复杂度: O(1)
|
||||
移除并返回集合中的一个随机元素。
|
||||
|
||||
如果只想获取一个随机元素,但不想该元素从集合中被移除的话,可以使用 SRANDMEMBER key [count] 命令。
|
||||
|
||||
返回值
|
||||
被移除的随机元素。 当 key 不存在或 key 是空集时,返回 nil 。
|
||||
*/
|
||||
func (this *RedisPool) SPop(key string) (value interface{}, err error) {
|
||||
conn := this.GetConnection()
|
||||
defer conn.Close()
|
||||
|
||||
value, err = conn.Do("SPOP", key)
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
SRANDMEMBER key [count]
|
||||
可用版本: >= 1.0.0
|
||||
时间复杂度: 只提供 key 参数时为 O(1) 。如果提供了 count 参数,那么为 O(N) ,N 为返回数组的元素个数。
|
||||
如果命令执行时,只提供了 key 参数,那么返回集合中的一个随机元素。
|
||||
|
||||
从 Redis 2.6 版本开始, SRANDMEMBER 命令接受可选的 count 参数:
|
||||
|
||||
如果 count 为正数,且小于集合基数,那么命令返回一个包含 count 个元素的数组,数组中的元素各不相同。如果 count 大于等于集合基数,那么返回整个集合。
|
||||
|
||||
如果 count 为负数,那么命令返回一个数组,数组中的元素可能会重复出现多次,而数组的长度为 count 的绝对值。
|
||||
|
||||
该操作和 SPOP key 相似,但 SPOP key 将随机元素从集合中移除并返回,而 SRANDMEMBER 则仅仅返回随机元素,而不对集合进行任何改动。
|
||||
|
||||
返回值
|
||||
只提供 key 参数时,返回一个元素;如果集合为空,返回 nil 。 如果提供了 count 参数,那么返回一个数组;如果集合为空,返回空数组。
|
||||
*/
|
||||
func (this *RedisPool) SRandMember(key string, count int) (reply interface{}, err error) {
|
||||
conn := this.GetConnection()
|
||||
defer conn.Close()
|
||||
|
||||
reply, err = conn.Do("SRANDMEMBER", key, count)
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
SREM key member [member …]
|
||||
可用版本: >= 1.0.0
|
||||
时间复杂度: O(N), N 为给定 member 元素的数量。
|
||||
移除集合 key 中的一个或多个 member 元素,不存在的 member 元素会被忽略。
|
||||
|
||||
当 key 不是集合类型,返回一个错误。
|
||||
|
||||
Note
|
||||
|
||||
在 Redis 2.4 版本以前, SREM 只接受单个 member 值。
|
||||
|
||||
返回值
|
||||
被成功移除的元素的数量,不包括被忽略的元素。
|
||||
*/
|
||||
func (this *RedisPool) SRem(key string, values ...interface{}) (delCount int, err error) {
|
||||
conn := this.GetConnection()
|
||||
defer conn.Close()
|
||||
|
||||
delCount, err = redis.Int(conn.Do("SREM", redis.Args{}.Add(key).AddFlat(values)...))
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
SMOVE source destination member
|
||||
可用版本: >= 1.0.0
|
||||
时间复杂度: O(1)
|
||||
将 member 元素从 source 集合移动到 destination 集合。
|
||||
|
||||
SMOVE 是原子性操作。
|
||||
|
||||
如果 source 集合不存在或不包含指定的 member 元素,则 SMOVE 命令不执行任何操作,仅返回 0 。否则, member 元素从 source 集合中被移除,并添加到 destination 集合中去。
|
||||
|
||||
当 destination 集合已经包含 member 元素时, SMOVE 命令只是简单地将 source 集合中的 member 元素删除。
|
||||
|
||||
当 source 或 destination 不是集合类型时,返回一个错误。
|
||||
|
||||
返回值
|
||||
如果 member 元素被成功移除,返回 1 。 如果 member 元素不是 source 集合的成员,并且没有任何操作对 destination 集合执行,那么返回 0 。
|
||||
*/
|
||||
func (this *RedisPool) SMove(source, destination string, member interface{}) (successful bool, err error) {
|
||||
conn := this.GetConnection()
|
||||
defer conn.Close()
|
||||
|
||||
var result int
|
||||
result, err = redis.Int(conn.Do("SMOVE", source, destination, member))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if result == 1 {
|
||||
successful = true
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
SCARD key
|
||||
可用版本: >= 1.0.0
|
||||
时间复杂度: O(1)
|
||||
返回集合 key 的基数(集合中元素的数量)。
|
||||
|
||||
返回值
|
||||
集合的基数。 当 key 不存在时,返回 0 。
|
||||
*/
|
||||
func (this *RedisPool) SCard(key string) (nowCount int, err error) {
|
||||
conn := this.GetConnection()
|
||||
defer conn.Close()
|
||||
|
||||
nowCount, err = redis.Int(conn.Do("SCARD", key))
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
SMEMBERS key
|
||||
可用版本: >= 1.0.0
|
||||
时间复杂度: O(N), N 为集合的基数。
|
||||
返回集合 key 中的所有成员。
|
||||
|
||||
不存在的 key 被视为空集合。
|
||||
|
||||
返回值
|
||||
集合中的所有成员。
|
||||
*/
|
||||
func (this *RedisPool) SMembers(key string) (reply interface{}, err error) {
|
||||
conn := this.GetConnection()
|
||||
defer conn.Close()
|
||||
|
||||
reply, err = conn.Do("SMEMBERS", key)
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
SINTER key [key …]
|
||||
可用版本: >= 1.0.0
|
||||
时间复杂度: O(N * M), N 为给定集合当中基数最小的集合, M 为给定集合的个数。
|
||||
返回一个集合的全部成员,该集合是所有给定集合的交集。
|
||||
|
||||
不存在的 key 被视为空集。
|
||||
|
||||
当给定集合当中有一个空集时,结果也为空集(根据集合运算定律)。
|
||||
|
||||
返回值
|
||||
交集成员的列表。
|
||||
*/
|
||||
func (this *RedisPool) SInter(key string, keyList ...string) (reply interface{}, err error) {
|
||||
conn := this.GetConnection()
|
||||
defer conn.Close()
|
||||
|
||||
reply, err = conn.Do("SINTER", redis.Args{}.Add(key).AddFlat(keyList)...)
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
SINTERSTORE destination key [key …]
|
||||
可用版本: >= 1.0.0
|
||||
时间复杂度: O(N * M), N 为给定集合当中基数最小的集合, M 为给定集合的个数。
|
||||
这个命令类似于 SINTER key [key …] 命令,但它将结果保存到 destination 集合,而不是简单地返回结果集。
|
||||
|
||||
如果 destination 集合已经存在,则将其覆盖。
|
||||
|
||||
destination 可以是 key 本身。
|
||||
|
||||
返回值
|
||||
结果集中的成员数量。
|
||||
*/
|
||||
func (this *RedisPool) SInterStore(destination string, key string, keyList ...string) (count int, err error) {
|
||||
conn := this.GetConnection()
|
||||
defer conn.Close()
|
||||
|
||||
count, err = redis.Int(conn.Do("SINTERSTORE", redis.Args{}.Add(destination).Add(key).AddFlat(keyList)...))
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
SUNION key [key …]
|
||||
可用版本: >= 1.0.0
|
||||
时间复杂度: O(N), N 是所有给定集合的成员数量之和。
|
||||
返回一个集合的全部成员,该集合是所有给定集合的并集。
|
||||
|
||||
不存在的 key 被视为空集。
|
||||
|
||||
返回值
|
||||
并集成员的列表。
|
||||
*/
|
||||
func (this *RedisPool) SUnion(key string, keyList ...string) (reply interface{}, err error) {
|
||||
conn := this.GetConnection()
|
||||
defer conn.Close()
|
||||
|
||||
reply, err = conn.Do("SUNION", redis.Args{}.Add(key).AddFlat(keyList)...)
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
SUNIONSTORE destination key [key …]
|
||||
可用版本: >= 1.0.0
|
||||
时间复杂度: O(N), N 是所有给定集合的成员数量之和。
|
||||
这个命令类似于 SUNION key [key …] 命令,但它将结果保存到 destination 集合,而不是简单地返回结果集。
|
||||
|
||||
如果 destination 已经存在,则将其覆盖。
|
||||
|
||||
destination 可以是 key 本身。
|
||||
|
||||
返回值
|
||||
结果集中的元素数量。
|
||||
*/
|
||||
func (this *RedisPool) SUnionStore(destination string, key string, keyList ...string) (count int, err error) {
|
||||
conn := this.GetConnection()
|
||||
defer conn.Close()
|
||||
|
||||
count, err = redis.Int(conn.Do("SUNIONSTORE", redis.Args{}.Add(destination).Add(key).AddFlat(keyList)...))
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
SDIFF key [key …]
|
||||
可用版本: >= 1.0.0
|
||||
时间复杂度: O(N), N 是所有给定集合的成员数量之和。
|
||||
返回一个集合的全部成员,该集合是所有给定集合之间的差集。
|
||||
|
||||
不存在的 key 被视为空集。
|
||||
|
||||
返回值
|
||||
一个包含差集成员的列表。
|
||||
*/
|
||||
func (this *RedisPool) SDiff(key string, keyList ...string) (reply interface{}, err error) {
|
||||
conn := this.GetConnection()
|
||||
defer conn.Close()
|
||||
|
||||
reply, err = conn.Do("SDIFF", redis.Args{}.Add(key).AddFlat(keyList)...)
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
SDIFFSTORE destination key [key …]
|
||||
可用版本: >= 1.0.0
|
||||
时间复杂度: O(N), N 是所有给定集合的成员数量之和。
|
||||
这个命令的作用和 SDIFF key [key …] 类似,但它将结果保存到 destination 集合,而不是简单地返回结果集。
|
||||
|
||||
如果 destination 集合已经存在,则将其覆盖。
|
||||
|
||||
destination 可以是 key 本身。
|
||||
|
||||
返回值
|
||||
结果集中的元素数量。
|
||||
*/
|
||||
func (this *RedisPool) SDiffStore(destination string, key string, keyList ...string) (count int, err error) {
|
||||
conn := this.GetConnection()
|
||||
defer conn.Close()
|
||||
|
||||
count, err = redis.Int(conn.Do("SDIFFSTORE", redis.Args{}.Add(destination).Add(key).AddFlat(keyList)...))
|
||||
return
|
||||
}
|
||||
1575
trunk/goutil/redisUtil/redisPool_set_test.go
Normal file
1575
trunk/goutil/redisUtil/redisPool_set_test.go
Normal file
File diff suppressed because it is too large
Load Diff
420
trunk/goutil/redisUtil/redisPool_string.go
Normal file
420
trunk/goutil/redisUtil/redisPool_string.go
Normal file
@@ -0,0 +1,420 @@
|
||||
package redisUtil
|
||||
|
||||
import (
|
||||
"github.com/gomodule/redigo/redis"
|
||||
)
|
||||
|
||||
/*
|
||||
SET key value [EX seconds] [PX milliseconds] [NX|XX]
|
||||
可用版本: >= 1.0.0
|
||||
时间复杂度: O(1)
|
||||
将字符串值 value 关联到 key 。
|
||||
|
||||
如果 key 已经持有其他值, SET 就覆写旧值, 无视类型。
|
||||
|
||||
当 SET 命令对一个带有生存时间(TTL)的键进行设置之后, 该键原有的 TTL 将被清除。
|
||||
|
||||
可选参数
|
||||
从 Redis 2.6.12 版本开始, SET 命令的行为可以通过一系列参数来修改:
|
||||
|
||||
EX seconds : 将键的过期时间设置为 seconds 秒。 执行 SET key value EX seconds 的效果等同于执行 SETEX key seconds value 。
|
||||
|
||||
PX milliseconds : 将键的过期时间设置为 milliseconds 毫秒。 执行 SET key value PX milliseconds 的效果等同于执行 PSETEX key milliseconds value 。
|
||||
|
||||
NX : 只在键不存在时, 才对键进行设置操作。 执行 SET key value NX 的效果等同于执行 SETNX key value 。
|
||||
|
||||
XX : 只在键已经存在时, 才对键进行设置操作。
|
||||
|
||||
Note
|
||||
|
||||
因为 SET 命令可以通过参数来实现 SETNX 、 SETEX 以及 PSETEX 命令的效果, 所以 Redis 将来的版本可能会移除并废弃 SETNX 、 SETEX 和 PSETEX 这三个命令。
|
||||
|
||||
返回值
|
||||
在 Redis 2.6.12 版本以前, SET 命令总是返回 OK 。
|
||||
|
||||
从 Redis 2.6.12 版本开始, SET 命令只在设置操作成功完成时才返回 OK ; 如果命令使用了 NX 或者 XX 选项, 但是因为条件没达到而造成设置操作未执行, 那么命令将返回空批量回复(NULL Bulk Reply)。
|
||||
*/
|
||||
/*
|
||||
expireType: "EX"|"PX"|""(参照上面的说明)
|
||||
expireTime: seconds|milliseconds|无(根据expireType的不同而不同,参照上面的说明)
|
||||
setType: "NX"|"XX"|""(参照上面的说明)
|
||||
*/
|
||||
/*
|
||||
返回值:
|
||||
successful: 是否成功
|
||||
err: 错误对象
|
||||
*/
|
||||
func (this *RedisPool) Set(key string, value interface{}, expireType string, expireTime int, setType string) (successful bool, err error) {
|
||||
conn := this.GetConnection()
|
||||
defer conn.Close()
|
||||
|
||||
args := redis.Args{}.Add(key).Add(value)
|
||||
if expireType != "" {
|
||||
args = args.Add(expireType).Add(expireTime)
|
||||
}
|
||||
if setType != "" {
|
||||
args = args.Add(setType)
|
||||
}
|
||||
|
||||
reply, err := conn.Do("SET", args...)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if reply == nil {
|
||||
return
|
||||
}
|
||||
|
||||
successful = true
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
GET key
|
||||
可用版本: >= 1.0.0
|
||||
时间复杂度: O(1)
|
||||
返回与键 key 相关联的字符串值。
|
||||
|
||||
返回值
|
||||
如果键 key 不存在, 那么返回特殊值 nil ; 否则, 返回键 key 的值。
|
||||
|
||||
如果键 key 的值并非字符串类型, 那么返回一个错误, 因为 GET 命令只能用于字符串值。
|
||||
*/
|
||||
func (this *RedisPool) Get(key string) (reply interface{}, exist bool, err error) {
|
||||
conn := this.GetConnection()
|
||||
defer conn.Close()
|
||||
|
||||
reply, err = conn.Do("GET", key)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if reply == nil {
|
||||
return
|
||||
}
|
||||
|
||||
exist = true
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
GETSET key value
|
||||
可用版本: >= 1.0.0
|
||||
时间复杂度: O(1)
|
||||
将键 key 的值设为 value , 并返回键 key 在被设置之前的旧值。
|
||||
|
||||
返回值
|
||||
返回给定键 key 的旧值。
|
||||
|
||||
如果键 key 没有旧值, 也即是说, 键 key 在被设置之前并不存在, 那么命令返回 nil 。
|
||||
|
||||
当键 key 存在但不是字符串类型时, 命令返回一个错误。
|
||||
*/
|
||||
func (this *RedisPool) GetSet(key, value interface{}) (oldValue interface{}, exist bool, err error) {
|
||||
conn := this.GetConnection()
|
||||
defer conn.Close()
|
||||
|
||||
oldValue, err = conn.Do("GetSet", key, value)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if oldValue == nil {
|
||||
return
|
||||
}
|
||||
|
||||
exist = true
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
STRLEN key
|
||||
可用版本: >= 2.2.0
|
||||
复杂度: O(1)
|
||||
返回键 key 储存的字符串值的长度。
|
||||
|
||||
返回值
|
||||
STRLEN 命令返回字符串值的长度。
|
||||
|
||||
当键 key 不存在时, 命令返回 0 。
|
||||
|
||||
当 key 储存的不是字符串值时, 返回一个错误。
|
||||
*/
|
||||
func (this *RedisPool) StrLen(key string) (length int, err error) {
|
||||
conn := this.GetConnection()
|
||||
defer conn.Close()
|
||||
|
||||
length, err = redis.Int(conn.Do("STRLEN", key))
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
APPEND key value
|
||||
可用版本: >= 2.0.0
|
||||
时间复杂度: 平摊O(1)
|
||||
如果键 key 已经存在并且它的值是一个字符串, APPEND 命令将把 value 追加到键 key 现有值的末尾。
|
||||
|
||||
如果 key 不存在, APPEND 就简单地将键 key 的值设为 value , 就像执行 SET key value 一样。
|
||||
|
||||
返回值
|
||||
追加 value 之后, 键 key 的值的长度。
|
||||
*/
|
||||
func (this *RedisPool) Append(key, value string) (length int, err error) {
|
||||
conn := this.GetConnection()
|
||||
defer conn.Close()
|
||||
|
||||
length, err = redis.Int(conn.Do("APPEND", key, value))
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
SETRANGE key offset value
|
||||
可用版本: >= 2.2.0
|
||||
时间复杂度:对于长度较短的字符串,命令的平摊复杂度O(1);对于长度较大的字符串,命令的复杂度为 O(M) ,其中 M 为 value 的长度。
|
||||
从偏移量 offset 开始, 用 value 参数覆写(overwrite)键 key 储存的字符串值。
|
||||
|
||||
不存在的键 key 当作空白字符串处理。
|
||||
|
||||
SETRANGE 命令会确保字符串足够长以便将 value 设置到指定的偏移量上, 如果键 key 原来储存的字符串长度比偏移量小(比如字符串只有 5 个字符长,但你设置的 offset 是 10 ), 那么原字符和偏移量之间的空白将用零字节(zerobytes, "\x00" )进行填充。
|
||||
|
||||
因为 Redis 字符串的大小被限制在 512 兆(megabytes)以内, 所以用户能够使用的最大偏移量为 2^29-1(536870911) , 如果你需要使用比这更大的空间, 请使用多个 key 。
|
||||
|
||||
Warning
|
||||
|
||||
当生成一个很长的字符串时, Redis 需要分配内存空间, 该操作有时候可能会造成服务器阻塞(block)。 在2010年出产的Macbook Pro上, 设置偏移量为 536870911(512MB 内存分配)将耗费约 300 毫秒, 设置偏移量为 134217728(128MB 内存分配)将耗费约 80 毫秒, 设置偏移量 33554432(32MB 内存分配)将耗费约 30 毫秒, 设置偏移量为 8388608(8MB 内存分配)将耗费约 8 毫秒。
|
||||
|
||||
返回值
|
||||
SETRANGE 命令会返回被修改之后, 字符串值的长度。
|
||||
*/
|
||||
func (this *RedisPool) SetRange(key string, offset int, value string) (length int, err error) {
|
||||
conn := this.GetConnection()
|
||||
defer conn.Close()
|
||||
|
||||
length, err = redis.Int(conn.Do("SETRANGE", key, offset, value))
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
GETRANGE key start end
|
||||
可用版本: >= 2.4.0
|
||||
时间复杂度: O(N),其中 N 为被返回的字符串的长度。
|
||||
返回键 key 储存的字符串值的指定部分, 字符串的截取范围由 start 和 end 两个偏移量决定 (包括 start 和 end 在内)。
|
||||
|
||||
负数偏移量表示从字符串的末尾开始计数, -1 表示最后一个字符, -2 表示倒数第二个字符, 以此类推。
|
||||
|
||||
GETRANGE 通过保证子字符串的值域(range)不超过实际字符串的值域来处理超出范围的值域请求。
|
||||
|
||||
Note
|
||||
|
||||
GETRANGE 命令在 Redis 2.0 之前的版本里面被称为 SUBSTR 命令。
|
||||
|
||||
返回值
|
||||
GETRANGE 命令会返回字符串值的指定部分。
|
||||
*/
|
||||
func (this *RedisPool) GetRange(key string, start, end int) (value string, err error) {
|
||||
conn := this.GetConnection()
|
||||
defer conn.Close()
|
||||
|
||||
value, err = redis.String(conn.Do("GETRANGE", key, start, end))
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
INCR key
|
||||
可用版本: >= 1.0.0
|
||||
时间复杂度: O(1)
|
||||
为键 key 储存的数字值加上一。
|
||||
|
||||
如果键 key 不存在, 那么它的值会先被初始化为 0 , 然后再执行 INCR 命令。
|
||||
|
||||
如果键 key 储存的值不能被解释为数字, 那么 INCR 命令将返回一个错误。
|
||||
|
||||
本操作的值限制在 64 位(bit)有符号数字表示之内。
|
||||
|
||||
Note
|
||||
|
||||
INCR 命令是一个针对字符串的操作。 因为 Redis 并没有专用的整数类型, 所以键 key 储存的值在执行 INCR 命令时会被解释为十进制 64 位有符号整数。
|
||||
|
||||
返回值
|
||||
INCR 命令会返回键 key 在执行加一操作之后的值。
|
||||
*/
|
||||
func (this *RedisPool) Incr(key string) (newValue int64, err error) {
|
||||
conn := this.GetConnection()
|
||||
defer conn.Close()
|
||||
|
||||
newValue, err = redis.Int64(conn.Do("INCR", key))
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
INCRBY key increment
|
||||
可用版本: >= 1.0.0
|
||||
时间复杂度: O(1)
|
||||
为键 key 储存的数字值加上增量 increment 。
|
||||
|
||||
如果键 key 不存在, 那么键 key 的值会先被初始化为 0 , 然后再执行 INCRBY 命令。
|
||||
|
||||
如果键 key 储存的值不能被解释为数字, 那么 INCRBY 命令将返回一个错误。
|
||||
|
||||
本操作的值限制在 64 位(bit)有符号数字表示之内。
|
||||
|
||||
关于递增(increment) / 递减(decrement)操作的更多信息, 请参见 INCR 命令的文档。
|
||||
|
||||
返回值
|
||||
在加上增量 increment 之后, 键 key 当前的值。
|
||||
*/
|
||||
func (this *RedisPool) IncrBy(key string, increment int64) (newValue int64, err error) {
|
||||
conn := this.GetConnection()
|
||||
defer conn.Close()
|
||||
|
||||
newValue, err = redis.Int64(conn.Do("INCRBY", key, increment))
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
INCRBYFLOAT key increment
|
||||
可用版本: >= 2.6.0
|
||||
时间复杂度: O(1)
|
||||
为键 key 储存的值加上浮点数增量 increment 。
|
||||
|
||||
如果键 key 不存在, 那么 INCRBYFLOAT 会先将键 key 的值设为 0 , 然后再执行加法操作。
|
||||
|
||||
如果命令执行成功, 那么键 key 的值会被更新为执行加法计算之后的新值, 并且新值会以字符串的形式返回给调用者。
|
||||
|
||||
无论是键 key 的值还是增量 increment , 都可以使用像 2.0e7 、 3e5 、 90e-2 那样的指数符号(exponential notation)来表示, 但是, 执行 INCRBYFLOAT 命令之后的值总是以同样的形式储存, 也即是, 它们总是由一个数字, 一个(可选的)小数点和一个任意长度的小数部分组成(比如 3.14 、 69.768 ,诸如此类), 小数部分尾随的 0 会被移除, 如果可能的话, 命令还会将浮点数转换为整数(比如 3.0 会被保存成 3 )。
|
||||
|
||||
此外, 无论加法计算所得的浮点数的实际精度有多长, INCRBYFLOAT 命令的计算结果最多只保留小数点的后十七位。
|
||||
|
||||
当以下任意一个条件发生时, 命令返回一个错误:
|
||||
|
||||
键 key 的值不是字符串类型(因为 Redis 中的数字和浮点数都以字符串的形式保存,所以它们都属于字符串类型);
|
||||
|
||||
键 key 当前的值或者给定的增量 increment 不能被解释(parse)为双精度浮点数。
|
||||
|
||||
返回值
|
||||
在加上增量 increment 之后, 键 key 的值。
|
||||
*/
|
||||
func (this *RedisPool) IncrByFloat(key string, increment float64) (newValue float64, err error) {
|
||||
conn := this.GetConnection()
|
||||
defer conn.Close()
|
||||
|
||||
newValue, err = redis.Float64(conn.Do("INCRBYFLOAT", key, increment))
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
DECR key
|
||||
可用版本: >= 1.0.0
|
||||
时间复杂度: O(1)
|
||||
为键 key 储存的数字值减去一。
|
||||
|
||||
如果键 key 不存在, 那么键 key 的值会先被初始化为 0 , 然后再执行 DECR 操作。
|
||||
|
||||
如果键 key 储存的值不能被解释为数字, 那么 DECR 命令将返回一个错误。
|
||||
|
||||
本操作的值限制在 64 位(bit)有符号数字表示之内。
|
||||
|
||||
关于递增(increment) / 递减(decrement)操作的更多信息, 请参见 INCR 命令的文档。
|
||||
|
||||
返回值
|
||||
DECR 命令会返回键 key 在执行减一操作之后的值。
|
||||
*/
|
||||
func (this *RedisPool) Decr(key string) (newValue int64, err error) {
|
||||
conn := this.GetConnection()
|
||||
defer conn.Close()
|
||||
|
||||
newValue, err = redis.Int64(conn.Do("DECR", key))
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
DECRBY key decrement
|
||||
可用版本: >= 1.0.0
|
||||
时间复杂度: O(1)
|
||||
将键 key 储存的整数值减去减量 decrement 。
|
||||
|
||||
如果键 key 不存在, 那么键 key 的值会先被初始化为 0 , 然后再执行 DECRBY 命令。
|
||||
|
||||
如果键 key 储存的值不能被解释为数字, 那么 DECRBY 命令将返回一个错误。
|
||||
|
||||
本操作的值限制在 64 位(bit)有符号数字表示之内。
|
||||
|
||||
关于更多递增(increment) / 递减(decrement)操作的更多信息, 请参见 INCR 命令的文档。
|
||||
|
||||
返回值
|
||||
DECRBY 命令会返回键在执行减法操作之后的值。
|
||||
*/
|
||||
func (this *RedisPool) DecrBy(key string, decrement int64) (newValue int64, err error) {
|
||||
conn := this.GetConnection()
|
||||
defer conn.Close()
|
||||
|
||||
newValue, err = redis.Int64(conn.Do("DECRBY", key, decrement))
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
MSET key value [key value …]
|
||||
可用版本: >= 1.0.1
|
||||
时间复杂度: O(N),其中 N 为被设置的键数量。
|
||||
同时为多个键设置值。
|
||||
|
||||
如果某个给定键已经存在, 那么 MSET 将使用新值去覆盖旧值, 如果这不是你所希望的效果, 请考虑使用 MSETNX 命令, 这个命令只会在所有给定键都不存在的情况下进行设置。
|
||||
|
||||
MSET 是一个原子性(atomic)操作, 所有给定键都会在同一时间内被设置, 不会出现某些键被设置了但是另一些键没有被设置的情况。
|
||||
|
||||
返回值
|
||||
MSET 命令总是返回 OK 。
|
||||
*/
|
||||
func (this *RedisPool) MSet(key_value_map map[string]interface{}) (err error) {
|
||||
conn := this.GetConnection()
|
||||
defer conn.Close()
|
||||
|
||||
_, err = conn.Do("MSET", redis.Args{}.AddFlat(key_value_map)...)
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
MSETNX key value [key value …]
|
||||
可用版本: >= 1.0.1
|
||||
时间复杂度: O(N), 其中 N 为被设置的键数量。
|
||||
当且仅当所有给定键都不存在时, 为所有给定键设置值。
|
||||
|
||||
即使只有一个给定键已经存在, MSETNX 命令也会拒绝执行对所有键的设置操作。
|
||||
|
||||
MSETNX 是一个原子性(atomic)操作, 所有给定键要么就全部都被设置, 要么就全部都不设置, 不可能出现第三种状态。
|
||||
|
||||
返回值
|
||||
当所有给定键都设置成功时, 命令返回 1 ; 如果因为某个给定键已经存在而导致设置未能成功执行, 那么命令返回 0 。
|
||||
*/
|
||||
func (this *RedisPool) MSetNX(key_value_map map[string]interface{}) (successful bool, err error) {
|
||||
conn := this.GetConnection()
|
||||
defer conn.Close()
|
||||
|
||||
var count int
|
||||
count, err = redis.Int(conn.Do("MSETNX", redis.Args{}.AddFlat(key_value_map)...))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if count == 1 {
|
||||
successful = true
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
MGET key [key …]
|
||||
可用版本: >= 1.0.0
|
||||
时间复杂度: O(N) ,其中 N 为给定键的数量。
|
||||
返回给定的一个或多个字符串键的值。
|
||||
|
||||
如果给定的字符串键里面, 有某个键不存在, 那么这个键的值将以特殊值 nil 表示。
|
||||
|
||||
返回值
|
||||
MGET 命令将返回一个列表, 列表中包含了所有给定键的值。
|
||||
*/
|
||||
func (this *RedisPool) MGet(keyList []string) (reply interface{}, err error) {
|
||||
conn := this.GetConnection()
|
||||
defer conn.Close()
|
||||
|
||||
// valueList, err = redis.Values(conn.Do("MGET", redis.Args{}.AddFlat(keyList)...))
|
||||
reply, err = conn.Do("MGET", redis.Args{}.AddFlat(keyList)...)
|
||||
return
|
||||
}
|
||||
1733
trunk/goutil/redisUtil/redisPool_string_test.go
Normal file
1733
trunk/goutil/redisUtil/redisPool_string_test.go
Normal file
File diff suppressed because it is too large
Load Diff
114
trunk/goutil/redisUtil/redisPool_test.go
Normal file
114
trunk/goutil/redisUtil/redisPool_test.go
Normal file
@@ -0,0 +1,114 @@
|
||||
package redisUtil
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
redisPoolObj *RedisPool
|
||||
)
|
||||
|
||||
func init() {
|
||||
redisPoolObj = NewRedisPool("testPool", "10.1.0.21:6379", "redis_pwd", 5, 500, 200, 10*time.Second, 5*time.Second)
|
||||
}
|
||||
|
||||
func TestGetName(t *testing.T) {
|
||||
expected := "testPool"
|
||||
got := redisPoolObj.GetName()
|
||||
if expected != got {
|
||||
t.Errorf("Expected to get %s, but got %s", expected, got)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetAddress(t *testing.T) {
|
||||
expected := "10.1.0.21:6379"
|
||||
got := redisPoolObj.GetAddress()
|
||||
if expected != got {
|
||||
t.Errorf("Expected to get %s, but got %s", expected, got)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func converInterfaceSliceToStringSlice(sourceList []interface{}) []string {
|
||||
targetList := make([]string, 0, len(sourceList))
|
||||
for _, item := range sourceList {
|
||||
if item == nil {
|
||||
targetList = append(targetList, "")
|
||||
} else if item_str, ok := item.(string); ok {
|
||||
targetList = append(targetList, item_str)
|
||||
} else if item_bytes, ok2 := item.([]byte); ok2 {
|
||||
targetList = append(targetList, string(item_bytes))
|
||||
}
|
||||
}
|
||||
|
||||
return targetList
|
||||
}
|
||||
|
||||
func isTwoOrderedSliceEqual(list1, list2 []string) bool {
|
||||
if list1 == nil && list2 == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
if list1 == nil || list2 == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if len(list1) != len(list2) {
|
||||
return false
|
||||
}
|
||||
|
||||
for i := 0; i < len(list1); i++ {
|
||||
if list1[i] != list2[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func isTwoUnorderedSliceEqual(list1, list2 []string) bool {
|
||||
if list1 == nil && list2 == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
if list1 == nil || list2 == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if len(list1) != len(list2) {
|
||||
return false
|
||||
}
|
||||
|
||||
map1 := make(map[string]struct{})
|
||||
map2 := make(map[string]struct{})
|
||||
|
||||
for _, item := range list1 {
|
||||
map1[item] = struct{}{}
|
||||
}
|
||||
for _, item := range list2 {
|
||||
map2[item] = struct{}{}
|
||||
}
|
||||
|
||||
for k := range map1 {
|
||||
if _, exist := map2[k]; !exist {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func getDistinctKeyList(keyList []string) []string {
|
||||
distinctKeyList := make([]string, 0, len(keyList))
|
||||
keyMap := make(map[string]struct{})
|
||||
for _, key := range keyList {
|
||||
if _, exist := keyMap[key]; !exist {
|
||||
distinctKeyList = append(distinctKeyList, key)
|
||||
keyMap[key] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
return distinctKeyList
|
||||
}
|
||||
73
trunk/goutil/redisUtil/redisPool_type.go
Normal file
73
trunk/goutil/redisUtil/redisPool_type.go
Normal file
@@ -0,0 +1,73 @@
|
||||
package redisUtil
|
||||
|
||||
import (
|
||||
"github.com/gomodule/redigo/redis"
|
||||
)
|
||||
|
||||
func (this *RedisPool) Int(reply interface{}) (int, error) {
|
||||
return redis.Int(reply, nil)
|
||||
}
|
||||
|
||||
func (this *RedisPool) Int64(reply interface{}) (int64, error) {
|
||||
return redis.Int64(reply, nil)
|
||||
}
|
||||
|
||||
func (this *RedisPool) Uint64(reply interface{}) (uint64, error) {
|
||||
return redis.Uint64(reply, nil)
|
||||
}
|
||||
|
||||
func (this *RedisPool) Float64(reply interface{}) (float64, error) {
|
||||
return redis.Float64(reply, nil)
|
||||
}
|
||||
|
||||
func (this *RedisPool) String(reply interface{}) (string, error) {
|
||||
return redis.String(reply, nil)
|
||||
}
|
||||
|
||||
func (this *RedisPool) Bytes(reply interface{}) ([]byte, error) {
|
||||
return redis.Bytes(reply, nil)
|
||||
}
|
||||
|
||||
func (this *RedisPool) Bool(reply interface{}) (bool, error) {
|
||||
return redis.Bool(reply, nil)
|
||||
}
|
||||
|
||||
func (this *RedisPool) Values(reply interface{}) ([]interface{}, error) {
|
||||
return redis.Values(reply, nil)
|
||||
}
|
||||
|
||||
func (this *RedisPool) Ints(reply interface{}) ([]int, error) {
|
||||
return redis.Ints(reply, nil)
|
||||
}
|
||||
|
||||
func (this *RedisPool) Int64s(reply interface{}) ([]int64, error) {
|
||||
return redis.Int64s(reply, nil)
|
||||
}
|
||||
|
||||
func (this *RedisPool) Float64s(reply interface{}) ([]float64, error) {
|
||||
return redis.Float64s(reply, nil)
|
||||
}
|
||||
|
||||
func (this *RedisPool) Strings(reply interface{}) ([]string, error) {
|
||||
return redis.Strings(reply, nil)
|
||||
}
|
||||
|
||||
func (this *RedisPool) ByteSlices(reply interface{}) ([][]byte, error) {
|
||||
return redis.ByteSlices(reply, nil)
|
||||
}
|
||||
|
||||
func (this *RedisPool) IntMap(reply interface{}) (map[string]int, error) {
|
||||
return redis.IntMap(reply, nil)
|
||||
}
|
||||
|
||||
func (this *RedisPool) Int64Map(reply interface{}) (map[string]int64, error) {
|
||||
return redis.Int64Map(reply, nil)
|
||||
}
|
||||
|
||||
func (this *RedisPool) StringMap(reply interface{}) (map[string]string, error) {
|
||||
return redis.StringMap(reply, nil)
|
||||
}
|
||||
|
||||
func (this *RedisPool) Positions(reply interface{}) ([]*[2]float64, error) {
|
||||
return redis.Positions(reply, nil)
|
||||
}
|
||||
Reference in New Issue
Block a user