GORM概述
GORM 是基于 Go 语言的一个自动化的 ORM(Object Relational Mapping) 框架,用于对 Go 语言结构体和数据库字段之间的映射。
GORM 官方支持的数据库类型有四个
- MySQL(最流行的数据库)
- PostgreSQL(开源协议较为宽松,很多有定制数据库需求的公司都会基于PostgreSQL进行二开)
- SQLite(轻量级的关系型数据库,主要用于移动设备)
- 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操作
redi的set类型(集合)是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乐观锁支持