GORM学习以及Redis实战操作|青训营

201 阅读18分钟

GORM概述

GORM 是基于 Go 语言的一个自动化的 ORM(Object Relational Mapping) 框架,用于对 Go 语言结构体和数据库字段之间的映射。

GORM 官方支持的数据库类型有四个

  1. MySQL(最流行的数据库)
  2. PostgreSQL(开源协议较为宽松,很多有定制数据库需求的公司都会基于PostgreSQL进行二开)
  3. SQLite(轻量级的关系型数据库,主要用于移动设备)
  4. SQL Server(微软推出的关系型数据库管理系统)

官方给出的使用文档链接为:gorm.io/zh_CN/docs/…

安装

go get -u gorm.io/gorm
go get -u gorm.io/driver/sqlite

连接到数据库

package main

import (
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
)

func main() {
	dsn := "root:521109#Xsq@tcp(127.0.0.1:3306)/grom_test?charset=utf8mb4&parseTime=True&loc=Local"
	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
}

**注意:**想要正确的处理 time.Time ,您需要带上 parseTime 参数, (更多参数) 要支持完整的 UTF-8 编码,您需要将 charset=utf8 更改为 charset=utf8mb4 查看 此文章 获取详情

MySQL驱动程序提供了一些高级配置可以在初始化过程中使用,例如:

db, err := gorm.Open(mysql.New(mysql.Config{
DSN: "gorm:gorm@tcp(127.0.0.1:3306)/gorm?charset=utf8&parseTime=True&loc=Local", // DSN data source name
DefaultStringSize: 256, // string 类型字段的默认长度
DisableDatetimePrecision: true, // 禁用 datetime 精度,MySQL 5.6 之前的数据库不支持
DontSupportRenameIndex: true, // 重命名索引时采用删除并新建的方式,MySQL 5.7 之前的数据库和 MariaDB 不支持重命名索引
DontSupportRenameColumn: true, // 用 `change` 重命名列,MySQL 8 之前的数据库和 MariaDB 不支持重命名列
SkipInitializeWithVersion: false, // 根据当前 MySQL 版本自动配置
}), &gorm.Config{})

自定义驱动

GORM允许通过DriverName选项自定义MySQL驱动,例如

import (
  _ "example.com/my_mysql_driver"
  "gorm.io/driver/mysql"
  "gorm.io/gorm"
)
db, err := gorm.Open(mysql.New(mysql.Config{
  DriverName: "my_mysql_driver",
  DSN: "gorm:gorm@tcp(localhost:9910)/gorm?charset=utf8&parseTime=True&loc=Local", }), &gorm.Config{})

GORM操作

gorm可自定义结构体来创建数据库表,可以将表结构直接生成对应的表,例如可以定义一个Product结构,具体内容如下

gorm.Model
	Code  string `gorm:"VARCHAR(20);NOT NULL;column:prodname"`
	Price uint   `gorm:"INT;PRIMARY KEY;column:price"`
}

通过类型后的语句可实现对改变量的类型定义以及名称的更换,同样也可以自定义表名

func (Product) TableName() string {
	return "us_user"
}

可以设置全局的logger,可以实现在每次执行sql语句的时候会答应每一行的sql

newLogger := logger.New(
		log.New(os.Stdout, "\r\n", log.LstdFlags), //日志输出的目标,前缀和日志包含的内容
		logger.Config{
			SlowThreshold:             time.Second, // 慢 SQL 阈值
			LogLevel:                  logger.Info, // 日志级别
			IgnoreRecordNotFoundError: true,        // 忽略ErrRecordNotFound(记录未找到)错误
			Colorful:                  true,        // 禁用彩色打印
		},
	)

下面是一些常用的gorm创建表,添加数据,以及查询数据的API实践

func main() {
	db := DirectConnect()
	if db == nil {
		fmt.Println("数据库连接失败")
		return
	}
	fmt.Println("数据库连接成功")
	err := db.AutoMigrate(&Product{})
	if err != nil {
		fmt.Println("自动建表失败 err:", err)
		return
	}
	//添加数据
	for i := 10; i < 20; i++ {
		code := "D" + strconv.Itoa(i)
		db.Create(&Product{Code: code, Price: uint(i)})
	}
	db.Create(&Product{Code: "D22", Price: 101})
	db.Create(&Product{Code: "D22", Price: 102})
	db.Create(&Product{Code: "D22", Price: 103})
	var result []Product
	db.Where("prodname = ?", "D22").First(&result)
	for _, res := range result {
		fmt.Printf("Result:%v\n", res)
	}
	db.Where("prodname <> ?", "D22").Find(&result)
	for _, res := range result {
		fmt.Printf("Result:%v\n", res)
	}
	db.Where("prodname IN ?", []string{"D11", "D12"}).Find(&result)
	for _, res := range result {
		fmt.Printf("Result:%v\n", res)
	}
	db.Where("prodname LIKE ?", "%D%").Find(&result)
	for _, res := range result {
		fmt.Printf("Result:%v\n", res)
	}
	db.Where("prodname = ? AND price = ?", "D22", "102").Find(&result)
	for _, res := range result {
		fmt.Printf("Result:%v\n", res)
	}
	db.Where(&Product{Code: "D22", Price: 101}).Find(&result)
	for _, res := range result {
		fmt.Printf("Result:%v\n", res)
	}
}

如果要修改查询的内容的话,则可以通过直接修改该引用变量,在保存即可

db.Where("name = ?", NewUser.Name).First(&NewUser)
NewUser.Name = "Tom"
db.Save(&NewUser)
// 将NewUser的Name修改成"Tom"

也可以实现删除操作

db.Delete(&NewUser)  // 为软删除,将字段中"deleted_at"设为当前时间,在数据库中仍存在该调数据

Redis-非关系型数据库

  • 性能极高 –- Redis能读的速度是110000次/s,写的速度是81000次/s
  • 丰富的数据类型 –- Redis支持二进制案例的 Strings,Lists,Hashes,Sets及Ordered Sets数据类型操作。
  • 原子--Redis的所有操作都是原子性的,同时Redis还支持对几个操作全并后的原子性执行。
  • 丰富的特性 – Redis还支持publish/subscribe,通知,key过期等等特性。

Go开发安装依赖包

go get -u github.com/go-redis/redis

go-redis包自带了连接池,会自动维护redis连接,因此创建一次client即可,不要查询一次redis就关闭client

redis连接测试

package main
​
import (
    "fmt""github.com/go-redis/redis"
)
​
var redisDb *redis.Client
​
func initClient() (err error) {
    redisDb = redis.NewClient(&redis.Options{
        Addr:     "localhost:6379", //redis地址
        Password: "",               //redis密码
        DB:       0,                //默认数据库,默认是0
    })
    //通过 *redis.Client.Ping() 来检查是否成功连接到了redis服务器
    _, err = redisDb.Ping().Result()
    if err != nil {
        return err
    }
    return nil
}
​
func main() {
    err := initClient()
    if err != nil {
        fmt.Println("redis 连接失败,err:", err)
    }
    fmt.Println("redis连接成功")
}

redis.options参数含义

type Options struct {
    // 网络类型 tcp 或者 unix.
    // 默认是 tcp.
    Network string
    // redis地址,格式 host:port
    Addr string
    // 新建一个redis连接的时候,会回调这个函数
    OnConnect func(*Conn) error
    // redis密码,redis server没有设置可以为空。
    Password string
    // redis数据库,序号从0开始,默认是0,可以不用设置
    DB int
    // redis操作失败最大重试次数,默认不重试。
    MaxRetries int
    // 最小重试时间间隔.
    // 默认是 8ms ; -1 表示关闭.
    MinRetryBackoff time.Duration
    // 最大重试时间间隔
    // 默认是 512ms; -1 表示关闭.
    MaxRetryBackoff time.Duration
    // redis连接超时时间.
    // 默认是 5 秒.
    DialTimeout time.Duration
    // socket读取超时时间
    // 默认 3 秒.
    ReadTimeout time.Duration
    // socket写超时时间
    WriteTimeout time.Duration
    // redis连接池的最大连接数.
    // 默认连接池大小等于 cpu个数 * 10
    PoolSize int
    // redis连接池最小空闲连接数.
    MinIdleConns int
    // redis连接最大的存活时间,默认不会关闭过时的连接.
    MaxConnAge time.Duration
    // 当你从redis连接池获取一个连接之后,连接池最多等待这个拿出去的连接多长时间。
    // 默认是等待 ReadTimeout + 1 秒.
    PoolTimeout time.Duration
    // redis连接池多久会关闭一个空闲连接.
    // 默认是 5 分钟. -1 则表示关闭这个配置项
    IdleTimeout time.Duration
    // 多长时间检测一下,空闲连接
    // 默认是 1 分钟. -1 表示关闭空闲连接检测
    IdleCheckFrequency time.Duration
    // 只读设置,如果设置为true, redis只能查询缓存不能更新。
    readOnly bool
}

基本键值操作

redis基本的key.value操作,指的是针对value值得类型为字符串或者数字类型的读写操作,go-redis常用函数如下:

type Cmdable interface {
    //给数据库中名称为key的string赋予值value,并设置失效时间,0为永久有效
    Set(key string, value interface{}, expiration time.Duration) *StatusCmd
    //查询数据库中名称为key的value值
    Get(key string) *StringCmd
    //设置一个key的值,并返回这个key的旧值
    GetSet(key string, value interface{}) *StringCmd
    //如果key不存在,则设置这个key的值,并设置key的失效时间。如果key存在,则设置不生效
    SetNX(key string, value interface{}, expiration time.Duration) *BoolCmd
    //批量查询key的值。比如redisDb.MGet("name1","name2","name3")
    MGet(keys ...string) *SliceCmd
    //批量设置key的值。redisDb.MSet("key1", "value1", "key2", "value2", "key3", "value3")
    MSet(pairs ...interface{}) *StatusCmd
    //Incr函数每次加一,key对应的值必须是整数或nil
    //否则会报错incr key1: ERR value is not an integer or out of range
    Incr(key string) *IntCmd
    // IncrBy函数,可以指定每次递增多少,key对应的值必须是整数或nil
    IncrBy(key string, value int64) *IntCmd
    // IncrByFloat函数,可以指定每次递增多少,跟IncrBy的区别是累加的是浮点数
    IncrByFloat(key string, value float64) *FloatCmd
    // Decr函数每次减一,key对应的值必须是整数或nil.否则会报错
    Decr(key string) *IntCmd
    //DecrBy,可以指定每次递减多少,key对应的值必须是整数或nil
    DecrBy(key string, decrement int64) *IntCmd
    //删除key操作,支持批量删除    redisDb.Del("key1","key2","key3")
    Del(keys ...string) *IntCmd
    //设置key的过期时间,单位秒
    Expire(key string, expiration time.Duration) *BoolCmd
    //给数据库中名称为key的string值追加value
    Append(key, value string) *IntCmd
}

Set/Get示例

func main() {
    err := initClient()
    if err != nil {
        fmt.Println("redis 连接失败,err:", err)
    }
    fmt.Println("redis连接成功")
    //参数含义为key value 过期时间 0代表不会过期
    err = redisDb.Set("name1", "zhangsan", 0).Err()
    if err != nil {
        fmt.Printf("set err:%v\n", err)
    }
    val, err := redisDb.Get("name1").Result()
    if err != nil {
        fmt.Printf("get err:%v\n", err)
    }
    fmt.Println("name的值为:", val)
}

GetSet&SetNX示例

func main() {
    err := initClient()
    if err != nil {
        fmt.Println("redis 连接失败,err:", err)
    }
    fmt.Println("redis连接成功")
    //参数含义为key value 过期时间 0代表不会过期
    err = redisDb.Set("name1", "zhangsan", 0).Err()
    if err != nil {
        fmt.Printf("set err:%v\n", err)
    }
    val, err := redisDb.Get("name1").Result()
    if err != nil {
        fmt.Printf("get err:%v\n", err)
    }
    fmt.Println("name的值为:", val)
    oldval, err := redisDb.GetSet("name1", "new_zhangsan").Result()
    if err != nil {
        fmt.Printf("修改失败 err:%v\n", err)
    }
    fmt.Println("name1:", oldval)
    err = redisDb.SetNX("name2", "list", 0).Err()
    if err != nil {
        fmt.Printf("设置nx err:%v\n", err)
    }
    val, err = redisDb.Get("name2").Result()
    if err != nil {
        fmt.Printf("get err:%v\n", err)
    }
    fmt.Println("name的值为:", val)
}

MGet&MSet示例

err := initClient()
    if err != nil {
        fmt.Println("redis 连接失败,err:", err)
    }
    fmt.Println("redis连接成功")
    err = redisDb.MSet("key1", "value1", "key2", "value2", "key3", "value3").Err()
    if err != nil {
        fmt.Println("批量插入失败:err", err)
    }
    vals, err := redisDb.MGet("key1", "key2", "key3").Result()
    if err != nil {
        fmt.Println("查找数据失败:err", err)
    }
    fmt.Println(vals)

自增自减操作

func main() {
    err := initClient()
    if err != nil {
        fmt.Println("redis 连接失败,err:", err)
    }
    fmt.Println("redis连接成功")
    err = redisDb.Set("age", "20", 0).Err()
    if err != nil {
        fmt.Println("插入失败:err", err)
    }
    redisDb.Incr("age")
    redisDb.IncrBy("age", 5)
    redisDb.Decr("age")
    redisDb.DecrBy("age", 3)
    var val string
    val, err = redisDb.Get("age").Result()
    fmt.Println("age=", val)
}

Del&Expire&Append示例

func main() {
	err := initClient()
	if err != nil {
		//redis连接错误
		panic(err)
	}

	//设置一个name对应的值是hello
	err = redisDb.Set("name", "hello", 0).Err()
	if err != nil {
		panic(err)
	}
	redisDb.Append("name", "China")
	val,err:=redisDb.Get("name").Result()
	fmt.Println(val) //helloChina
	
	//设置key的过期时间为5秒
	redisDb.Expire("name", 5)
	//批量删除
	redisDb.Del("name1", "name2", "key1", "key2")

list操作

Redis列表(list)是简单的字符串列表,列表是有序的,列表中的元素可以重复.可以添加一个元素到列表的头部(左边)或者尾部(右边)常用操作方法如下:

type Cmdable interface {
    //从列表左边插入数据,list不存在则新建一个继续插入数据
	LPush(key string, values ...interface{}) *IntCmd
    //跟LPush的区别是,仅当列表存在的时候才插入数据
	LPushX(key string, value interface{}) *IntCmd
    //返回名称为 key 的 list 中 start 至 end 之间的元素
    //返回从0开始到-1位置之间的数据,意思就是返回全部数据
	LRange(key string, start, stop int64) *StringSliceCmd
    //返回列表的长度大小
	LLen(key string) *IntCmd
    //截取名称为key的list的数据,list的数据为截取后的值
	LTrim(key string, start, stop int64) *StatusCmd
    //根据索引坐标,查询列表中的数据
    LIndex(key string, index int64) *StringCmd
    //给名称为key的list中index位置的元素赋值
	LSet(key string, index int64, value interface{}) *StatusCmd
    //在指定位置插入数据。op为"after或者before"
	LInsert(key, op string, pivot, value interface{}) *IntCmd
    //在指定位置前面插入数据
	LInsertBefore(key string, pivot, value interface{}) *IntCmd
    //在指定位置后面插入数据
	LInsertAfter(key string, pivot, value interface{}) *IntCmd
    //从列表左边删除第一个数据,并返回删除的数据
	LPop(key string) *StringCmd
    //删除列表中的数据。删除count个key的list中值为value 的元素。
	LRem(key string, count int64, value interface{}) *IntCmd
   }

list操作示例

func list_operator(){
	//list操作
	//只有列表存在的时候才会插入数据,不存在的话,误差插入
	err := redisDb.LPushX("studentList", "tom").Err()
	if err != nil {
		fmt.Println("插入失败,err:", err)
	}
	//列表不存在依然可以插入
	err = redisDb.LPush("studentList", "jack", "tom", "xsq", "wangtao").Err()
	if err != nil {
		fmt.Println("插入失败,err:", err)
	}
	//此列表存在的时候才能插入数据
	err = redisDb.LPushX("studentList", "tom").Err()
	if err != nil {
		fmt.Println("插入失败,err:", err)
	}
	//返回数据
	// 返回从0开始到-1位置之间的数据,意思就是返回全部数据
	vals, err := redisDb.LRange("studentList", 0, -1).Result()
	if err != nil {
		fmt.Println("插入失败,err:", err)
	}
	fmt.Println(vals)
	// 返回从[0,2]位置之间的数据,意思就是返回3个数据
	vals, err = redisDb.LRange("studentList", 0, 2).Result()
	if err != nil {
		fmt.Println("插入失败,err:", err)
	}
	fmt.Println(vals)
	studentLen, err := redisDb.LLen("studentList").Result()
	if err != nil {
		fmt.Println("插入失败,err:", err)
	}
	fmt.Println(studentLen)
	// 列表索引从0开始计算,这里返回第3个元素
	index, err := redisDb.LIndex("studentList", 2).Result()
	if err != nil {
		fmt.Println("插入失败,err:", err)
	}
	fmt.Println(index)
	//截取名称为key的list,并把截取后的值赋值给studentList
	val := redisDb.LTrim("studentList", 0, 3)
	if err != nil {
		fmt.Println("插入失败,err:", err)
	}
	fmt.Println(val)
	//给名称为key的list中index位置的元素赋值,把原来的数据覆盖
	redisDb.LSet("studentList", 2, "wangtao")
	index, err = redisDb.LIndex("studentList", 2).Result()
	if err != nil {
		fmt.Println("插入失败,err:", err)
	}
	fmt.Println(index)
	//在list列表studentList中值为jack前面添加元素hello
	redisDb.LInsert("studentList", "before", "jack", "hello")
	vals, err = redisDb.LRange("studentList", 0, -1).Result()
	if err != nil {
		fmt.Println("插入失败,err:", err)
	}
	fmt.Println(vals)
	redisDb.LInsert("studentList", "after", "jack", "hello")
	vals, err = redisDb.LRange("studentList", 0, -1).Result()
	if err != nil {
		fmt.Println("插入失败,err:", err)
	}
	fmt.Println(vals)
	//从列表左边删除第一个数据,并返回删除的数据
	first, _ := redisDb.LPop("studentList").Result()
	fmt.Println(first)
	//删除列表中的数据。删除count个key的list中值为value 的元素。如果出现重复元素,仅删除1次,也就是删除第一个
	rem, err := redisDb.LRem("studentList", 10, "hello").Result()
	fmt.Println(rem)
	vals, err = redisDb.LRange("studentList", 0, -1).Result()
	if err != nil {
		fmt.Println("插入失败,err:", err)
	}
	fmt.Println(vals)
}

集合Set操作

rediset类型(集合)是string类型数值的无序集合,并且集合元素唯一。常用函数如下:

type Cmdable interface {
    //向名称为key的set中添加元素member
    SAdd(key string, members ...interface{}) *IntCmd
    //获取集合set元素个数
	SCard(key string) *IntCmd
    //判断元素member是否在集合set中
	SIsMember(key string, member interface{}) *BoolCmd
    //返回名称为 key 的 set 的所有元素
	SMembers(key string) *StringSliceCmd
    //求差集
	SDiff(keys ...string) *StringSliceCmd
    //求差集并将差集保存到 destination 的集合
	SDiffStore(destination string, keys ...string) *IntCmd
    //求交集
	SInter(keys ...string) *StringSliceCmd
    //求交集并将交集保存到 destination 的集合
	SInterStore(destination string, keys ...string) *IntCmd
    //求并集
	SUnion(keys ...string) *StringSliceCmd
    //求并集并将并集保存到 destination 的集合
	SUnionStore(destination string, keys ...string) *IntCmd
    //随机返回集合中的一个元素,并且删除这个元素
	SPop(key string) *StringCmd
    // 随机返回集合中的count个元素,并且删除这些元素
	SPopN(key string, count int64) *StringSliceCmd
    //删除名称为 key 的 set 中的元素 member,并返回删除的元素个数
	SRem(key string, members ...interface{}) *IntCmd
    //随机返回名称为 key 的 set 的一个元素
	SRandMember(key string) *StringCmd
    //随机返回名称为 key 的 set 的count个元素
	SRandMemberN(key string, count int64) *StringSliceCmd
    //把集合里的元素转换成map的key
	SMembersMap(key string) *StringStructMapCmd
    //移动集合source中的一个member元素到集合destination中去
	SMove(source, destination string, member interface{}) *BoolCmd
}
func set_operator() {
	//添加10到集合中
	err := redisDb.SAdd("stuset", 100).Err()
	if err != nil {
		fmt.Println("插入失败,err:", err)
	}
	err = redisDb.SAdd("stuset", 200, 201, 202, 203, 340, 430, 340, 204).Err()
	if err != nil {
		fmt.Println("插入失败,err:", err)
	}
	//随机返回集合中的一个元素,并且删除这个元素,这里删除的是400
	member1, err := redisDb.SPop("stuset").Result()
	fmt.Println(member1)
	// 随机返回集合中的4个元素,并且删除这些元素
	member2, _ := redisDb.SPopN("stuset", 4).Result()
	fmt.Println(member2)
	//删除集合stuSet名称为300,400的元素,并返回删除的元素个数
	member3, _ := redisDb.SRem("stuset", 200).Result()
	fmt.Println(member3)
	//随机返回集合stuSet中的一个元素
	member4, _ := redisDb.SRandMember("stuset").Result()
	fmt.Println(member4)
	//随机返回集合stuSet中的3个元素
	member5, _ := redisDb.SRandMemberN("stuset", 3).Result()
	fmt.Println(member5)
	//把集合里的元素转换成map的key
	map1, _ := redisDb.SMembersMap("stuset").Result()
	fmt.Println(map1)
	//移动集合stuSet中的一个200元素到集合resSet中去
	ok, _ := redisDb.SMove("stuset", "resset", 200).Result()
	fmt.Println(ok)
	//获取集合中元素的个数
	redisDb.SAdd("blackList", "tom", "black", "xsq")
	redisDb.SAdd("whiteList", "tom", "wangtao")
	// 求交集, 即既在黑名单中, 又在白名单中的元素
	names, err := redisDb.SInter("blackList", "whiteList").Result()
	fmt.Println("names:", names)
	//求交集并将交集保存到 destSet 的集合
	res, err := redisDb.SInterStore("destset", "blackList", "whiteList").Result()
	fmt.Println(res)
	//获取交集的值[the Elder]
	dest, _ := redisDb.SMembers("destset").Result()
	fmt.Println(dest)
	//求差集
	names, err = redisDb.SDiff("blackList", "whiteList").Result()
	fmt.Println("names:", names)
}

hash数据操作

如果你希望key/value的值也能作为hash结构进行操作,可以选择redis hash类型。

如果我们希望缓存一条用户信息(包括用户id、用户名、email字段),希望能够做到局部读写用户信息(例如:读写用户名),也能够读取整条用户信息,那么hash类型就支持这些操作。 Redis hash操作主要有如下2-3个元素组成

  • key - redis key唯一标识
  • field - hash数据的字段名
  • value - 值,有些操作不需要值
type Cmdable interface {
    //根据key和字段名,删除hash字段,支持批量删除hash字段
    HDel(key string, fields ...string) *IntCmd
    //检测hash字段名是否存在。
	HExists(key, field string) *BoolCmd
    //根据key和field字段,查询field字段的值
	HGet(key, field string) *StringCmd
    //根据key查询所有字段和值
	HGetAll(key string) *StringStringMapCmd
    //根据key和field字段,累加数值。
	HIncrBy(key, field string, incr int64) *IntCmd
    //根据key和field字段,累加数值。
	HIncrByFloat(key, field string, incr float64) *FloatCmd
    //根据key返回所有字段名
	HKeys(key string) *StringSliceCmd
    //根据key,查询hash的字段数量
	HLen(key string) *IntCmd
    //根据key和多个字段名,批量查询多个hash字段值
	HMGet(key string, fields ...string) *SliceCmd
    //根据key和多个字段名和字段值,批量设置hash字段值
	HMSet(key string, fields map[string]interface{}) *StatusCmd
    //根据key和field字段设置,field字段的值
	HSet(key, field string, value interface{}) *BoolCmd
    //根据key和field字段,查询field字段的值
	HSetNX(key, field string, value interface{}) *BoolCmd
}
func hash_operator() {
	//根据key和field字段设置,field字段的值。 user_1 是hash key,username 是字段名, admin是字段值
	redisDb.HSet("user_1", "username", "admin")
	//根据key和field字段,查询field字段的值。user_1 是hash key,username是字段名
	username, _ := redisDb.HGet("user_1", "username").Result()
	fmt.Println(username)
	redisDb.HSet("user_1", "password", "521109")
	data, _ := redisDb.HGetAll("user_1").Result()
	for field, val := range data {
		fmt.Println(field, val)
	}
	batchData := make(map[string]interface{})
	batchData["username"] = "wangtao"
	batchData["password"] = 123456
	redisDb.HMSet("user_2", batchData)
	//如果email字段不存在,则设置hash字段值
	redisDb.HSetNX("user_2", "email", "13209832943@163.com")
	// HMGet支持多个field字段名,意思是一次返回多个字段值
	values, _ := redisDb.HMGet("user_2", "username", "password", "email").Result()
	fmt.Println(values)
	// 累加count字段的值,一次性累加2, user_1为hash key
	count, _ := redisDb.HIncrBy("user_1", "count", 2).Result()
	fmt.Println(count)
	// 累加score字段的值,一次性累加3.2, user_1为hash key
	score, _ := redisDb.HIncrByFloat("user_1", "count", 3.2).Result()
	fmt.Println(score)
	// 根据key返回所有字段名,keys是一个string数组
	keys, _ := redisDb.HKeys("user_1").Result()
	fmt.Println(keys)
	//根据key,查询hash的字段数量
	size, _ := redisDb.HLen("user_1").Result()
	fmt.Println(size)
	// 删除一个字段id
	redisDb.HDel("user_1", "password")
	keys, _ = redisDb.HKeys("user_1").Result()
	fmt.Println(keys)
	exists, _ := redisDb.HExists("user_1", "id").Result()
	fmt.Println(exists)
}

有序集合

Redis有序集合(sorted set)和集合一样也是string类型元素的集合,且不允许重复的成员,不同的是每个元素都会关联一个double类型的分数,这个分数主要用于集合元素排序。

// Z 表示已排序的集合成员

type Z struct {

Score float64 // 分数

Member interface{} // 元素名

}

type Cmdable interface {
    // 添加一个或者多个元素到集合,如果元素已经存在则更新分数
    ZAdd(key string, members ...Z) *IntCmd
	ZAddNX(key string, members ...Z) *IntCmd
	ZAddXX(key string, members ...Z) *IntCmd
	ZAddCh(key string, members ...Z) *IntCmd
	ZAddNXCh(key string, members ...Z) *IntCmd
    // 添加一个或者多个元素到集合,如果元素已经存在则更新分数
	ZAddXXCh(key string, members ...Z) *IntCmd
    //增加元素的分数
	ZIncr(key string, member Z) *FloatCmd
	ZIncrNX(key string, member Z) *FloatCmd
	ZIncrXX(key string, member Z) *FloatCmd
    //增加元素的分数,增加的分数必须是float64类型
	ZIncrBy(key string, increment float64, member string) *FloatCmd
    // 存储增加分数的元素到destination集合
	ZInterStore(destination string, store ZStore, keys ...string) *IntCmd
    //返回集合元素个数
    ZCard(key string) *IntCmd
    //统计某个分数范围内的元素个数
	ZCount(key, min, max string) *IntCmd
    //返回集合中某个索引范围的元素,根据分数从小到大排序
	ZRange(key string, start, stop int64) *StringSliceCmd
    //ZRevRange的结果是按分数从大到小排序。
    ZRevRange(key string, start, stop int64) *StringSliceCmd
	//根据分数范围返回集合元素,元素根据分数从小到大排序,支持分页。
	ZRangeByScore(key string, opt ZRangeBy) *StringSliceCmd
    //根据分数范围返回集合元素,用法类似ZRangeByScore,区别是元素根据分数从大到小排序。
    ZRemRangeByScore(key, min, max string) *IntCmd
    //用法跟ZRangeByScore一样,区别是除了返回集合元素,同时也返回元素对应的分数
    ZRangeWithScores(key string, start, stop int64) *ZSliceCmd
    //根据元素名,查询集合元素在集合中的排名,从0开始算,集合元素按分数从小到大排序
	ZRank(key, member string) *IntCmd
    //ZRevRank的作用跟ZRank一样,区别是ZRevRank是按分数从大到小排序。
    ZRevRank(key, member string) *IntCmd 
    //查询元素对应的分数
	ZScore(key, member string) *FloatCmd
    //删除集合元素
	ZRem(key string, members ...interface{}) *IntCmd
    //根据索引范围删除元素。从最低分到高分的(stop-start)个元素
	ZRemRangeByRank(key string, start, stop int64) *IntCmd
}
func list_operator() {
	//list操作
	//只有列表存在的时候才会插入数据,不存在的话,误差插入
	err := redisDb.LPushX("studentList", "tom").Err()
	if err != nil {
		fmt.Println("插入失败,err:", err)
	}
	//列表不存在依然可以插入
	err = redisDb.LPush("studentList", "jack", "tom", "xsq", "wangtao").Err()
	if err != nil {
		fmt.Println("插入失败,err:", err)
	}
	//此列表存在的时候才能插入数据
	err = redisDb.LPushX("studentList", "tom").Err()
	if err != nil {
		fmt.Println("插入失败,err:", err)
	}
	//返回数据
	// 返回从0开始到-1位置之间的数据,意思就是返回全部数据
	vals, err := redisDb.LRange("studentList", 0, -1).Result()
	if err != nil {
		fmt.Println("插入失败,err:", err)
	}
	fmt.Println(vals)
	// 返回从[0,2]位置之间的数据,意思就是返回3个数据
	vals, err = redisDb.LRange("studentList", 0, 2).Result()
	if err != nil {
		fmt.Println("插入失败,err:", err)
	}
	fmt.Println(vals)
	studentLen, err := redisDb.LLen("studentList").Result()
	if err != nil {
		fmt.Println("插入失败,err:", err)
	}
	fmt.Println(studentLen)
	// 列表索引从0开始计算,这里返回第3个元素
	index, err := redisDb.LIndex("studentList", 2).Result()
	if err != nil {
		fmt.Println("插入失败,err:", err)
	}
	fmt.Println(index)
	//截取名称为key的list,并把截取后的值赋值给studentList
	val := redisDb.LTrim("studentList", 0, 3)
	if err != nil {
		fmt.Println("插入失败,err:", err)
	}
	fmt.Println(val)
	//给名称为key的list中index位置的元素赋值,把原来的数据覆盖
	redisDb.LSet("studentList", 2, "wangtao")
	index, err = redisDb.LIndex("studentList", 2).Result()
	if err != nil {
		fmt.Println("插入失败,err:", err)
	}
	fmt.Println(index)
	//在list列表studentList中值为jack前面添加元素hello
	redisDb.LInsert("studentList", "before", "jack", "hello")
	vals, err = redisDb.LRange("studentList", 0, -1).Result()
	if err != nil {
		fmt.Println("插入失败,err:", err)
	}
	fmt.Println(vals)
	redisDb.LInsert("studentList", "after", "jack", "hello")
	vals, err = redisDb.LRange("studentList", 0, -1).Result()
	if err != nil {
		fmt.Println("插入失败,err:", err)
	}
	fmt.Println(vals)
	//从列表左边删除第一个数据,并返回删除的数据
	first, _ := redisDb.LPop("studentList").Result()
	fmt.Println(first)
	//删除列表中的数据。删除count个key的list中值为value 的元素。如果出现重复元素,仅删除1次,也就是删除第一个
	rem, err := redisDb.LRem("studentList", 10, "hello").Result()
	fmt.Println(rem)
	vals, err = redisDb.LRange("studentList", 0, -1).Result()
	if err != nil {
		fmt.Println("插入失败,err:", err)
	}
	fmt.Println(vals)
}

事务处理

redis事务可以一次执行多个命令,事务中的命令要么全部被执行,要么全部都不执行。以下示例是以Pipeline的方式操作事务,接口结构如下:

type Pipeliner interface {
	StatefulCmdable
	Do(args ...interface{}) *Cmd
	Process(cmd Cmder) error
	Close() error
	Discard() error
	Exec() ([]Cmder, error)
}
//以Pipeline的方式操作事务
TxPipeline() Pipeliner
Watch - redis乐观锁支持