基本使用
示例代码
package main
import (
"fmt"
"github.com/go-redis/redis"
)
func main() {
rdb := redis.NewClient(&redis.Options{
Addr: "127.0.0.1:6379",
Password: "123456",
DB: 0,
})
err := rdb.Set("name", "meowrain", 0).Err()
if err != nil {
panic(err)
}
val, err := rdb.Get("name").Result()
if err != nil {
panic(err)
}
fmt.Println("name", val)
}
把连接数据库的代码封装为函数
package main
import (
"fmt"
"time"
"github.com/go-redis/redis"
)
var DB *redis.Client
func RedisConnect(addr, password string) {
client := redis.NewClient(&redis.Options{
Addr: addr,
Password: password,
DB: 0,
DialTimeout: time.Second,
})
err := client.Ping().Err()
if err != nil {
panic(err)
}
DB = client
}
func main() {
RedisConnect("127.0.0.1:6379", "123456")
fmt.Println(DB.Get("name"))
}
redis 五大数据类型
Redis 主要有以下五种数据类型:
-
字符串(String)
- 最基本的数据类型,可以存储字符串、整数或浮点数。
- 键值对,一个键对应一个值。
- 最大存储容量为 512MB。
- 常用命令:
SET
、GET
、INCR
、DECR
、MGET
、MSET
等。
-
列表(List)
- 按插入顺序排序的字符串列表。
- 可以从头部或尾部插入元素。
- 常用命令:
LPUSH
、RPUSH
、LPOP
、RPOP
、LRANGE
等。
-
集合(Set)
- 无序、不重复的字符串集合。
- 支持集合运算,如并集、交集、差集等。
- 常用命令:
SADD
、SMEMBERS
、SINTER
、SUNION
、SDIFF
等。
-
哈希表(Hash)
- 存储字段和字段值的映射关系,类似于 Python 中的字典或 Java 中的 HashMap。
- 适合存储对象信息。
- 常用命令:
HSET
、HGET
、HGETALL
、HDEL
、HEXISTS
等。
-
有序集合(Sorted Set)
- 类似于集合,但不允许重复成员,并且每个成员都关联一个分数。
- 成员按照分数排序,分数可以相同。
- 常用命令:
ZADD
、ZRANGE
、ZREM
、ZSCORE
等。
除了这五种主要数据类型外,Redis 还提供了一些其他的数据结构,例如:
- 位图(Bitmap): 用于存储位数组,可以高效地进行位运算。
- HyperLogLog: 用于进行基数统计,例如统计网站的独立访客数。
- 地理空间索引(GeoSpatial Index): 用于存储地理位置信息,并进行地理位置查询。
字符串
# get / set
127.0.0.1:6379> set name meowrain
OK
127.0.0.1:6379> set age 20
OK
127.0.0.1:6379> get name
"meowrain"
127.0.0.1:6379> get age
"20"
127.0.0.1:6379> exists name
(integer) 1
127.0.0.1:6379> exists names
(integer) 0
127.0.0.1:6379> del name
(integer) 1
127.0.0.1:6379> exists name
(integer) 0
127.0.0.1:6379> getset name meowrainyyds #不存在值,就返回nil并且设置新的值
(nil)
127.0.0.1:6379> getset name meowrain #如果原来存在值,那么返回原来的值并更新value为新的值
"meowrainyyds"
# 批量操作
127.0.0.1:6379> mset name1 meow name2 kevien name3 john
OK
127.0.0.1:6379>
# 自增自减
127.0.0.1:6379> get n
(nil)
127.0.0.1:6379> incr n
(integer) 1
127.0.0.1:6379> incr n
(integer) 2
127.0.0.1:6379> get n
"2"
127.0.0.1:6379> decr n
(integer) 1
127.0.0.1:6379> get n
"1"
127.0.0.1:6379>
# 增加,减少
127.0.0.1:6379> incrby n 10
(integer) 10
127.0.0.1:6379> get n
"10"
127.0.0.1:6379> incrby n 20
(integer) 30
127.0.0.1:6379> get n
"30"
127.0.0.1:6379> decrby n 10
(integer) 20
127.0.0.1:6379> get n
"20"
# 倒计时自动删除
127.0.0.1:6379> setex name 20 meowrain
OK
...20 秒后
127.0.0.1:6379> get name
# 查看倒计时还有多久结束
127.0.0.1:6379> setex name 20 meowrain
OK
127.0.0.1:6379> ttl name
(integer) 18
127.0.0.1:6379> ttl name
(integer) 17
# 对设置为永不过期的key设置过期时间
127.0.0.1:6379> set name meowrain
OK
127.0.0.1:6379> get name
"meowrain"
127.0.0.1:6379> expire name 10
(integer) 1
127.0.0.1:6379> ttl name
(integer) 8
127.0.0.1:6379> ttl name
(integer) 4
127.0.0.1:6379> get name
"meowrain"
127.0.0.1:6379> ttl name
(integer) -2
127.0.0.1:6379> get name
(nil)
127.0.0.1:6379>
(nil)
列表
# lpush从左边插入,rpush从右边插入,lrange进行列表遍历
127.0.0.1:6379> lpush namelist lihua xiaoming zhangsan
(integer) 3
127.0.0.1:6379> lrange namelist 0 3
1) "zhangsan"
2) "xiaoming"
3) "lihua"
127.0.0.1:6379> rpush namelist xiaoqiang lihong
(integer) 5
127.0.0.1:6379> lrange namelist 0 5
1) "zhangsan"
2) "xiaoming"
3) "lihua"
4) "xiaoqiang"
5) "lihong"
# 查看列表长度
127.0.0.1:6379> llen namelist
(integer) 5
# lpop 和rpop
127.0.0.1:6379> lrange namelist 0 -1
1) "zhangsan"
2) "xiaoming"
3) "lihua"
4) "xiaoqiang"
5) "lihong"
127.0.0.1:6379> lpop namelist
"zhangsan"
127.0.0.1:6379> lrange namelist 0 -1
1) "xiaoming"
2) "lihua"
3) "xiaoqiang"
4) "lihong"
127.0.0.1:6379> rpop namelist
"lihong"
127.0.0.1:6379> lrange namelist 0 -1
1) "xiaoming"
2) "lihua"
3) "xiaoqiang"
Hash哈希
# 设置hash dict hset
127.0.0.1:6379> hset dict name meowrain
(integer) 1
127.0.0.1:6379> hset dict age 20
(integer) 1
127.0.0.1:6379> hget dict name
"meowrain"
127.0.0.1:6379> hget dict age
"20"
127.0.0.1:6379> hgetall dict
1) "name"
2) "meowrain"
3) "age"
4) "20"
# 获取keys hkeys
127.0.0.1:6379> hkeys dict
1) "name"
2) "age"
# 获取values hvals
127.0.0.1:6379> hvals dict
1) "meowrain"
2) "222"
# 删除key-value hdel
127.0.0.1:6379> hget dict name
"meowrain"
127.0.0.1:6379> hdel dict name
(integer) 1
127.0.0.1:6379> hget dict name
(nil)
# 批量设置
127.0.0.1:6379> hmset dict name meowrain age 222
OK
127.0.0.1:6379> hgetall dict
1) "name"
2) "meowrain"
3) "age"
4) "222"
127.0.0.1:6379>
# 查看有几个键值对
127.0.0.1:6379> hlen dict
(integer) 2
# 查看键值是否存在
127.0.0.1:6379> hexists dict name
(integer) 1
127.0.0.1:6379> hexists dict names
(integer) 0
# 1表示存在 0表示不存在
#
Set集合
Redis 中的 Set 数据类型是一种非常有用的数据结构,它允许你存储一组唯一的字符串成员。在Redis中,Set 类型主要用于存储无重复的集合,例如存储一组用户ID、标签列表等。下面详细介绍 Set 数据类型的特性和用法。
Set 的特性
成员唯一性 :Set 中的每个成员都是唯一的,这意味着同一个成员不能出现多次。
无序 :Set 中的成员是无序的,也就是说成员之间没有固定的顺序。
成员类型 :成员可以是任何字符串,包括数字和特殊字符。
高效的操作 :Set 提供了高效的添加、删除和查找操作。
常用命令
SADD (添加成员)
SADD key member1 [member2 …]
key:Set 的键。
member1, member2:要添加的成员。
SMEMBERS (获取所有成员)
SMEMBERS key
key:Set 的键。
返回:Set 中的所有成员。
SREM (移除成员)
SREM key member1 [member2 …]
key:Set 的键。
member1, member2:要移除的成员。
SISMEMBER (检查成员是否存在)
SISMEMBER key member
key:Set 的键。
member:要检查的成员。
返回:如果成员存在于Set中返回1,否则返回0。
SPOP (随机移除并获取成员)
SPOP key [count]
key:Set 的键。
count:可选参数,指定要移除并返回的成员数量,默认为1。
返回:随机从Set中移除的一个或多个成员。
SRANDMEMBER (随机获取成员)
SRANDMEMBER key [count]
key:Set 的键。
count:可选参数,指定要返回的成员数量,默认为1。
返回:Set 中随机选取的一个或多个成员。
SCARD (获取成员数量)
SCARD key
key:Set 的键。
返回:Set 中成员的数量。
SUNION (获取两个或多个Set的并集)
SUNION key1 [key2 …]
key1, key2:要进行并集操作的Set 键。
返回:给定键对应的Set的并集。
SINTER (获取两个或多个Set的交集)
SINTER key1 [key2 …]
key1, key2:要进行交集操作的Set 键。
返回:给定键对应的Set的交集。
SDIFF (获取两个或多个Set的差集)
SDIFF key1 [key2 …]
key1, key2:要进行差集操作的Set 键。
返回:给定键对应的Set的差集。
# sadd 添加成员
127.0.0.1:6379> sadd first_set go java python c cpp
(integer) 5
# smembers 获取所有成员
127.0.0.1:6379> smembers first_set
1) "go"
2) "java"
3) "python"
4) "c"
5) "cpp"
# srem 删除成员
127.0.0.1:6379> srem first_set go
(integer) 1
127.0.0.1:6379> smembers first_set
1) "java"
2) "python"
3) "c"
4) "cpp"
# sismember 查看成员是否存在
127.0.0.1:6379> sismember first_set go
(integer) 0
127.0.0.1:6379> sismember first_set java
(integer) 1
# spop (随机移除并获取成员)
127.0.0.1:6379> spop first_set
"java"
127.0.0.1:6379> sismember first_set java
(integer) 0
# srandmember 获取随机成员
127.0.0.1:6379> SRANDMEMBER first_set 1
1) "go"
127.0.0.1:6379> SRANDMEMBER first_set 3
1) "python"
2) "rust"
3) "c#"
# scard 获取成员数量
127.0.0.1:6379> scard first_set
(integer) 7
# sunion 获取两个或多个set的并集
127.0.0.1:6379> sadd second_set go java c csharp matlab vb
(integer) 6
127.0.0.1:6379> smembers second_set
1) "go"
2) "java"
3) "c"
4) "csharp"
5) "matlab"
6) "vb"
127.0.0.1:6379> smembers first_set
1) "python"
2) "c"
3) "cpp"
4) "go"
5) "java"
6) "rust"
7) "c#"
127.0.0.1:6379> sunion first_set second_set
1) "python"
2) "c"
3) "cpp"
4) "go"
5) "java"
6) "rust"
7) "c#"
8) "csharp"
9) "matlab"
10) "vb"
# sinter 获取多个集合的交集
127.0.0.1:6379> sinter first_set second_set
1) "go"
2) "java"
3) "c"
# sdiff 获取多个集合的差集
127.0.0.1:6379> sdiff first_set second_set
1) "python"
2) "cpp"
3) "rust"
4) "c#"
zset 有序集合
Redis 的 ZSet (Sorted Set) 是一种有序集合数据结构,它不仅可以存储字符串成员,还可以为每个成员关联一个浮点数分数。ZSet 会根据分数对成员进行排序,分数越低,排名越靠前。
ZSet 的特点:
- 有序性: 成员按分数排序,分数相同则按字典序排序。
- 唯一性: 每个成员都是唯一的,但分数可以相同。
- 高效性: 支持范围查询、排名查询等操作,效率很高。
ZSet 的应用场景:
- 排行榜: 例如游戏排行榜、微博热搜榜等,可以使用 ZSet 存储用户 ID 和分数,方便实时更新和查询排名。
- 带权重的队列: 例如任务调度系统,可以使用 ZSet 存储任务和优先级,优先级高的任务分数越低,会被优先执行。
- 时间序列数据: 例如监控系统,可以使用 ZSet 存储时间戳和指标值,方便进行时间范围查询和聚合计算。
ZSet 常用命令:
- 添加成员:
ZADD key score member [score member ...]
- 获取成员分数:
ZSCORE key member
- 获取排名范围内的成员:
ZRANGE key start stop [WITHSCORES]
- 获取分数范围内的成员:
ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]
- 删除成员:
ZREM key member [member ...]
- 获取集合中成员数量:
ZCARD key
- 获取成员排名:
ZRANK key member
- 获取指定分数范围内成员数量:
ZCOUNT key min max
ZSet 的实现原理:
Redis 的 ZSet 采用跳表(Skip List)和哈希表两种数据结构实现。
- 跳表: 用于维护成员的有序性,支持快速范围查询和排名查询。
- 哈希表: 用于存储成员和分数的映射关系,支持快速添加、删除和查询成员。
ZSet 的优势:
- 高效的排序和查询: 跳表结构保证了排序和查询的效率。
- 灵活的应用场景: 可以应用于各种需要排序和范围查询的场景。
- 丰富的命令支持: 提供了丰富的命令来操作 ZSet 数据。
ZSet 的劣势:
- 内存占用较大: 同时使用跳表和哈希表,内存占用相对较高。
- 实现较为复杂: 跳表的实现较为复杂,需要一定的算法基础。
总结:
Redis 的 ZSet 是一种非常实用的数据结构,它结合了有序集合和哈希表的优点,能够高效地存储和查询有序数据。在实际应用中,我们可以根据具体的需求选择合适的数据结构,充分发挥 Redis 的性能优势。
# 添加成员到class中
127.0.0.1:6379> zadd class 99 jack 88 mike 60 kaly
(integer) 3
# 遍历成绩从最低到最高的3个人
127.0.0.1:6379> zrange class 0 3
1) "kaly"
2) "mike"
3) "jack"
# 获取集合中成员数量:
127.0.0.1:6379> zcard class
(integer) 3
# 查看某个成员的分数
127.0.0.1:6379> zscore class kaly
"60"
# 查看某个成员的排名,按从低到高
127.0.0.1:6379> zrank class mike
(integer) 1
127.0.0.1:6379> zrank class kaly
(integer) 0
127.0.0.1:6379> zcount class 60 90
(integer) 2
127.0.0.1:6379>
# 获取分数范围内的成员
127.0.0.1:6379> zrangebyscore class 60 100
1) "kaly"
2) "mike"
3) "jack"
# 删除成员
127.0.0.1:6379> zrange class 0 4
1) "kate"
2) "kaly"
3) "jack"
4) "mike"
127.0.0.1:6379> zrem class kate
(integer) 1
127.0.0.1:6379> zrange class 0 4
1) "kaly"
2) "jack"
3) "mike"
127.0.0.1:6379>
# 给成员添加分数
127.0.0.1:6379> zincrby class 2 mike
"101"
127.0.0.1:6379>
redis 事务
Redis 事务 (Transactions) 提供了一种将多个命令作为一个原子操作执行的机制。通过事务,Redis 可以确保事务中的所有命令要么全部执行,要么全部不执行,从而避免了在处理多个操作时可能出现的不一致性问题。
Redis 事务的基本概念
- MULTI:开启一个事务。
- EXEC:执行事务中的所有命令。
- DISCARD:取消事务,放弃事务中的所有命令。
- WATCH:监视一个或多个键,如果在事务执行之前这些键发生变化(被其他客户端修改),则事务将被中止。
Redis 事务的特点
- 原子性:Redis 事务中的所有命令会被顺序执行,中间不会插入其他命令。
- 单命令的原子性:虽然事务提供了命令的批量执行,但每个命令仍然是原子性的,然而 Redis 并不保证整个事务的原子性。即,事务中的部分命令可能已经被执行,而剩余的命令则由于某种原因(如 WATCH 监控的键被修改)没有执行。
- 事务失败时不会回滚:如果事务中的某个命令失败了,其他命令仍会继续执行。Redis 不支持回滚机制。
Redis 事务的使用步骤
1. 开启事务
使用 MULTI
命令来开启一个事务。Redis 会将后续的命令加入到一个队列中,而不是立即执行。
MULTI
2. 添加命令到事务
在开启事务后,可以向 Redis 添加多个命令,这些命令会被排队等待执行。
SET key1 "value1"
INCR counter
3. 执行事务
使用 EXEC
命令来执行事务中排队的所有命令。Redis 会按顺序执行所有命令,并返回每个命令的执行结果。
EXEC
4. 取消事务
在事务执行之前,可以使用 DISCARD
命令取消事务。此操作会清空事务队列,并放弃所有已排队的命令。
DISCARD
5. 使用 WATCH 监视键
WATCH
命令用于监视一个或多个键,当监视的键在事务执行之前发生变化时,事务将被中止。
WATCH key1 key2
6. 结合 WATCH 的事务
在使用 WATCH 监视键后,如果检测到键的变化,可以决定不执行事务:
WATCH key1
MULTI
INCR key1
SET key2 "new_value"
EXEC
如果在 EXEC
执行前 key1
被其他客户端修改了,事务将不会执行,并返回 nil
。
使用示例
WATCH mykey
MULTI
INCR mykey
SET otherkey "some_value"
EXEC
在这个示例中,WATCH
命令监视了 mykey
,如果 mykey
在事务执行前被修改,EXEC
将不会执行事务中的命令。
自己写的
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set a 12
QUEUED
127.0.0.1:6379(TX)> incr a
QUEUED
127.0.0.1:6379(TX)> lpush list a b c d
QUEUED
127.0.0.1:6379(TX)> exec
1) OK
注意事项
- 非原子性:如果事务执行失败(如网络中断或客户端崩溃),Redis 不会回滚事务中已经执行的命令。
- 性能考虑:Redis 的事务没有提供锁机制,通常需要通过乐观锁(
WATCH
)来避免并发问题。
Redis 的事务提供了一种简单且高效的方法来处理需要多个命令连续执行的操作,但与传统数据库事务相比,Redis 的事务模型更轻量且具备一定的限制。
go-redis string读写
package main
import (
"fmt"
"time"
"github.com/go-redis/redis"
)
var DB *redis.Client
func init() {
RedisConnect("127.0.0.1:6379", "123456")
}
func RedisConnect(addr, passwd string) {
client := redis.NewClient(&redis.Options{
Addr: addr,
Password: passwd,
DB: 0,
DialTimeout: time.Second,
})
err := client.Ping().Err()
if err != nil {
panic(err)
}
fmt.Printf("connect redis server:%v successfully!\n", addr)
DB = client
}
func RedisWriteString(key, str string) {
var err error
err = DB.Set(key, str, 0).Err()
if err != nil {
panic(err)
}
fmt.Printf("key %v value write into redis: %v\n", key, str)
val, err := DB.Get("name").Result()
if err != nil {
panic(err)
}
fmt.Printf("val read from redis: %v\n", val)
}
func RedisGetString(key string) string {
val, err := DB.Get(key).Result()
if err != nil {
panic(err)
}
fmt.Printf("redis get string %v is %v\n", key, val)
return val
}
func CheckKeyExists(key string) error {
val, err := DB.Exists(key).Result()
if err != nil {
return err
}
isTrue := func(num int64) bool {
return num != 0
}
if isTrue(val) {
fmt.Println("存在", key)
return nil
}
fmt.Println("不存在", key)
return nil
}
func IncrByVal(key string, val int64) {
res, err := DB.IncrBy(key, val).Result()
if err != nil {
panic(err)
}
fmt.Printf("key %v after IncrBy value %v is %v\n", key, val, res)
}
func DecrByVal(key string, val int64) {
res, err := DB.DecrBy(key, val).Result()
if err != nil {
panic(err)
}
fmt.Printf("key %v after DecrByVal %v is %v\n", key, val, res)
}
func DelKey(key string) {
err := DB.Del(key).Err()
if err != nil {
panic(err)
}
fmt.Printf("key %v del by DelVal\n", key)
}
func SetExpireKey(key string, t *time.Duration) {
err := DB.Expire(key, *t).Err()
if err != nil {
panic(err)
}
fmt.Printf("key %v set expire time by SetExpireKey", key)
}
func GetTTL(key string) int64 {
return DB.TTL(key).Val().Microseconds()
}
func main() {
RedisWriteString("name", "meowrainyyds")
RedisWriteString("age", "1")
CheckKeyExists("name")
CheckKeyExists("age")
IncrByVal("age", 12)
DelKey("age")
}
go-redis list读写
package main
import (
"fmt"
"time"
"github.com/go-redis/redis"
)
var DB *redis.Client
func init() {
ConnectDB("127.0.0.1:6379", "123456", 0)
}
func ConnectDB(addr, passwd string, db int) {
client := redis.NewClient(&redis.Options{
Addr: addr,
Password: passwd,
DB: db,
DialTimeout: time.Second})
err := client.Ping().Err()
if err != nil {
panic(err)
}
DB = client
fmt.Println("connect to redis server successfully")
}
func Rpush(listname string, values ...any) {
DB.RPush(listname, values...)
}
func Lpush(listname string, values ...any) {
DB.LPush(listname, values...)
}
func LPop(listname string) string {
v, err := DB.LPop(listname).Result()
if err != nil {
panic(err)
}
return v
}
func RPop(listname string) string {
v, err := DB.RPop(listname).Result()
if err != nil {
panic(err)
}
return v
}
func GetListLen(listname string) int64 {
v, err := DB.LLen(listname).Result()
if err != nil {
panic(err)
}
return v
}
func PrintList(listname string) {
v, err := DB.LRange(listname, 0, -1).Result()
if err != nil {
panic(err)
}
fmt.Printf("list range:%v\t", listname)
for _, val := range v {
fmt.Printf("%v ", val)
}
fmt.Println()
}
func main() {
PrintList("namelist")
}
redis-go hash
package redishash
import (
"fmt"
"time"
"github.com/go-redis/redis"
)
var DB *redis.Client
func init() {
ConnectDB("127.0.0.1:6379", "123456", 0)
}
func ConnectDB(addr, passwd string, db int) {
client := redis.NewClient(&redis.Options{
Addr: addr,
Password: passwd,
DB: db,
DialTimeout: time.Second,
})
err := client.Ping().Err()
if err != nil {
panic(err)
}
DB = client
fmt.Println("connect db successfully")
}
func SetHash(hashnam, field string, value any) {
err := DB.HSet(hashnam, field, value).Err()
if err != nil {
panic(err)
}
}
func GetHash(hashname, field string) (value any) {
var err error
value, err = DB.HGet(hashname, field).Result()
if err != nil {
panic(err)
}
return value
}
func GetHashKeys(hashname string) {
res, err := DB.HKeys(hashname).Result()
if err != nil {
panic(err)
}
fmt.Printf("keys from hashname:%v\n", hashname)
for _, v := range res {
fmt.Printf("%v ", v)
}
fmt.Println()
}
func HashExists(hashname, field string) bool {
return DB.HExists(hashname, field).Val()
}
func GetHashLen(hashname string) int64 {
return DB.HLen(hashname).Val()
}
func GetHashVals(hashname string) {
res, err := DB.HVals(hashname).Result()
if err != nil {
panic(err)
}
fmt.Printf("hash vals from %v: \n", hashname)
for _, v := range res {
fmt.Printf("%v ", v)
}
fmt.Println()
}
redis-go set
。。。
redis-go zset
…
redis-go 事务
package main
import (
"fmt"
"time"
"github.com/go-redis/redis"
)
var DB *redis.Client
func init() {
ConnectDB("127.0.0.1:6379", "123456", 0)
}
func ConnectDB(addr, passwd string, db int) {
client := redis.NewClient(&redis.Options{
Addr: addr,
Password: passwd,
DB: db,
DialTimeout: time.Second,
})
err := client.Ping().Err()
if err != nil {
panic(err)
}
DB = client
fmt.Println("connect db successfully")
}
func CretaeTransaction() {
_, err := DB.TxPipelined(func(pipe redis.Pipeliner) error {
pipe.Set("key1", "1", 0)
pipe.IncrBy("key1", 12)
return nil
})
if err != nil {
panic(err)
} else {
fmt.Println("Transaction succeed")
}
}
func main() {
CretaeTransaction()
}