初始Redis | 青训营

104 阅读8分钟

Redis

Redis简单介绍

Redis(Remote Dictionary Server,即远程字典服务)是一个开源的、使用ANSI C语言编写的 Key-Value 数据库。它通常被用作内存数据结构服务器,因为它具有高速读写、原子性操作、丰富的数据结构以及可持久化存储等特点。

Redis是一种键值型的NoSql数据库,这里有两个关键字:

  • 键值型
  • NoSql

其中键值型,是指Redis中存储的数据都是以key.value对的形式存储,而value的形式多种多样,可以是字符串.数值.甚至json:

而NoSql则是相对于传统关系型数据库而言,有很大差异的一种数据库。

对于存储的数据,没有类似Mysql那么严格的约束,比如唯一性,是否可以为null等等,所以我们把这种松散结构的数据库,称之为NoSQL数据库。

Redis的基本工作原理

  1. 数据存储:Redis将数据存储在内存中,这使得读写操作非常快速。为了实现这一功能,Redis使用了一种叫做内存映射(memory-mapped)的技术,将数据存储在内存中并定期将数据写入磁盘以实现持久化。
  2. 数据备份:Redis支持两种主要的数据备份方法:RDB(Redis DataBase)和AOF(Append Only File)。RDB是Redis基于当前自身的所有数据所生成的数据快照,纯粹的数据,若redis从rdb启动,可直接加载使用。AOF文件类似Redis日志文件,记录的是Redis接受到的写命令,若redis从aof启动,需要先读aof文件,然后执行里面的命令,生产数据。
  3. 性能优化:为了保证效率,Redis使用了一些优化策略。例如,它使用IO多路复用来处理多个客户端的请求,避免单线程模型下的性能瓶颈。此外,Redis还支持数据类型的原子操作,例如对字符串的增加和减少、集合的增加和删除元素等操作都是原子性的。
  4. 高可用性和可扩展性:Redis支持主从复制,可以实现高可用性和可扩展性。通过主从复制,当一个Redis实例发生故障时,其他实例可以接管其职责,保证服务的可用性。

Redis常见命令

Redis数据结构介绍

Redis是一个key-value的数据库,key一般是String类型,不过value的类型多种多样:

Redis为了方便我们学习,将操作不同数据类型的命令也做了分组,在官网( redis.io/commands )可以查看到不同的命令

Redis 通用命令

通用指令是部分数据类型的,都可以使用的指令,常见的有:

  • KEYS:查看符合模板的所有key
  • DEL:删除一个指定的key
  • EXISTS:判断key是否存在
  • EXPIRE:给一个key设置有效期,有效期到期时该key会被自动删除
  • TTL:查看一个KEY的剩余有效期

通过help [command] 可以查看一个命令的具体用法,例如:

  • DEL
127.0.0.1:6379> help del

  DEL key [key ...]
  summary: Delete a key
  since: 1.0.0
  group: generic

127.0.0.1:6379> keys *
1) "age"

127.0.0.1:6379> MSET k1 v1 k2 v2 k3 v3 #批量添加数据
OK

127.0.0.1:6379> keys *
1) "k3"
2) "k2"
3) "k1"
4) "age"

127.0.0.1:6379> del k1 k2 k3 k4
(integer) 3   #此处返回的是成功删除的key,由于redis中只有k1,k2,k3 所以只成功删除3个,最终返回
127.0.0.1:6379>

127.0.0.1:6379> keys * #再查询全部的key
1) "age"	#只剩下一个了
127.0.0.1:6379>
  • EXPIRE

内存非常宝贵,对于一些数据,我们应当给他一些过期时间,当过期时间到了之后,他就会自动被删除~

127.0.0.1:6379> expire age 10
(integer) 1

127.0.0.1:6379> ttl age
(integer) 8

127.0.0.1:6379> ttl age
(integer) 6

127.0.0.1:6379> ttl age
(integer) -2

127.0.0.1:6379> ttl age
(integer) -2  #当这个key过期了,那么此时查询出来就是-2 

127.0.0.1:6379> keys *
(empty list or set)

127.0.0.1:6379> set age 10 #如果没有设置过期时间
OK

127.0.0.1:6379> ttl age
(integer) -1  # ttl的返回值就是-1

Redis命令-String命令

String类型,也就是字符串类型,是Redis中最简单的存储类型。

其value是字符串,不过根据字符串的格式不同,又可以分为3类:

  • string:普通字符串
  • int:整数类型,可以做自增.自减操作
  • float:浮点类型,可以做自增.自减操作

String的常见命令有:

  • SET:添加或者修改已经存在的一个String类型的键值对
  • GET:根据key获取String类型的value
  • MSET:批量添加多个String类型的键值对
  • MGET:根据多个key获取多个String类型的value
  • INCR:让一个整型的key自增1
  • INCRBY:让一个整型的key自增并指定步长,例如:incrby num 2 让num值自增2
  • INCRBYFLOAT:让一个浮点类型的数字自增并指定步长
  • SETNX:添加一个String类型的键值对,前提是这个key不存在,否则不执行
  • SETEX:添加一个String类型的键值对,并且指定有效期
  • SET 和GET: 如果key不存在则是新增,如果存在则是修改
127.0.0.1:6379> set name Rose  //原来不存在
OK

127.0.0.1:6379> get name 
"Rose"

127.0.0.1:6379> set name Jack //原来存在,就是修改
OK

127.0.0.1:6379> get name
"Jack"
  • MSET和MGET
127.0.0.1:6379> MSET k1 v1 k2 v2 k3 v3
OK

127.0.0.1:6379> MGET name age k1 k2 k3
1) "Jack" //之前存在的name
2) "10"   //之前存在的age
3) "v1"
4) "v2"
5) "v3"
  • INCR和INCRBY和DECY
127.0.0.1:6379> get age 
"10"

127.0.0.1:6379> incr age //增加1
(integer) 11
    
127.0.0.1:6379> get age //获得age
"11"

127.0.0.1:6379> incrby age 2 //一次增加2
(integer) 13 //返回目前的age的值
    
127.0.0.1:6379> incrby age 2
(integer) 15
    
127.0.0.1:6379> incrby age -1 //也可以增加负数,相当于减
(integer) 14
    
127.0.0.1:6379> incrby age -2 //一次减少2个
(integer) 12
    
127.0.0.1:6379> DECR age //相当于 incr 负数,减少正常用法
(integer) 11
    
127.0.0.1:6379> get age 
"11"
  • SETNX
127.0.0.1:6379> help setnx

  SETNX key value
  summary: Set the value of a key, only if the key does not exist
  since: 1.0.0
  group: string

127.0.0.1:6379> set name Jack  //设置名称
OK
127.0.0.1:6379> setnx name lisi //如果key不存在,则添加成功
(integer) 0
127.0.0.1:6379> get name //由于name已经存在,所以lisi的操作失败
"Jack"
127.0.0.1:6379> setnx name2 lisi //name2 不存在,所以操作成功
(integer) 1
127.0.0.1:6379> get name2 
"lisi"
  • SETEX
127.0.0.1:6379> setex name 10 jack
OK

127.0.0.1:6379> ttl name
(integer) 8

127.0.0.1:6379> ttl name
(integer) 7

127.0.0.1:6379> ttl name
(integer) 5

Redis应用案例

Go语言连接Redis

使用 go get -u 命令从 GitHub 获取并更新已安装的软件包

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

Go连接Redis简单案例

package main  
  
import (  
 "fmt"  
 "gopkg.org/gomodule.v3/redis/v8"  
)  
  
func main() {  
 client := redis.NewClient(&redis.Options{  
 Network:  "tcp",  
 Address:  "localhost:6379",  
 Password: "", // no password set  
 })  
  
 // 检查连接  
 err := client.Ping().Err()  
 if err != nil {  
 fmt.Println("无法连接到 Redis:", err)  
 return  
 }  
  
 // 设置一个键值  
 err = client.Set("key", "value", 0).Err()  
 if err != nil {  
 fmt.Println("无法设置 Redis 键值:", err)  
 return  
 }  
  
 // 获取键值  
 value, err := client.Get("key").Result()  
 if err != nil {  
 fmt.Println("无法获取 Redis 键值:", err)  
 return  
 }  
 fmt.Println("从 Redis 获取的值:", value)  
  
 // 关闭连接  
 err = client.Close()  
 if err != nil {  
 fmt.Println("关闭 Redis 连接失败:", err)  
 return  
 }  
}

连续签到

要实现每日连续签到功能,你需要使用Redis的键空间通知功能来监听指定键空间的变化。当某个键的值为指定的签到时间戳时,触发签到逻辑。

以下是一个使用Go语言连接Redis并实现每日连续签到功能的示例代码:

package main  
  
import (  
 "fmt"  
 "time"  
  
 "gopkg.in/redis.v5"  
)  
  
func main() {  
 // 创建Redis客户端  
 client := redis.NewClient(&redis.Options{  
 Addr:     "localhost:6379",  
 Password: "", // 如果没有设置密码,将Password设为空字符串  
 })  
  
 // 检查连接  
 err := client.Ping().Err()  
 if err != nil {  
 fmt.Println("无法连接到 Redis:", err)  
 return  
 }  
  
 // 设置签到时间间隔(以秒为单位)  
 checkInterval := 24 * 60 * 60 // 一天  
  
 // 获取当前用户ID(这里假设用户ID为1)  
 userID := 1  
  
 // 获取当前时间  
 currentTime := time.Now()  
  
 // 计算下一次签到时间  
 nextCheckTime := calculateNextCheckTime(currentTime, checkInterval)  
 fmt.Printf("下一次签到时间: %s\n", nextCheckTime.Format("2006-01-02 15:04:05"))  
  
 // 将下一次签到时间存储到Redis中,以备之后比较  
 err = client.Set(fmt.Sprintf("check_time_%d", userID), nextCheckTime.Unix(), 0).Err()  
 if err != nil {  
 fmt.Println("无法设置签到时间:", err)  
 return  
 }  
  
 // 监听指定键空间的变化  
 keySpaceChannel := make(chan string)  
 go func() {  
 for {  
 message := <-keySpaceChannel  
 fmt.Println("Key Space Notification:", message)  
 // 在这里执行签到逻辑,判断message是否为签到时间戳,如果是则记录签到信息  
 }  
 }()  
 client.KeySpaceNotifyAdd(fmt.Sprintf("check_time_%d", userID), keySpaceChannel)  
  
 // 关闭Redis连接  
 err = client.Close()  
 if err != nil {  
 fmt.Println("关闭 Redis 连接失败:", err)  
 return  
 }  
}  
  
// 计算下一次签到时间  
func calculateNextCheckTime(currentTime time.Time, checkInterval int) time.Time {  
 // 获取当前时间的下一个完整天(与上一天的0点对齐)  
 nextDay := currentTime.AddDate(0, 0, 1).Truncate(24 * time.Hour)  
 // 计算下一个签到时间的秒数间隔  
 checkTimeUnix := int64(nextDay.Unix()) + int64(checkInterval)  
 // 将秒数间隔转换为时间格式  
 nextCheckTime := time.Unix(checkTimeUnix, 0)  
 return nextCheckTime  
}

在上述代码中,我们首先创建了一个Redis客户端,并连接到Redis服务器。然后,我们计算出下一次签到时间,并将该时间存储到Redis中。接下来,我们使用KeySpaceNotifyAdd方法监听指定键空间的变化,当指定的键值发生变化时,会通过keySpaceChannel通道接收通知。在接收到通知后,我们执行签到逻辑,判断接收到的消息是否为签到时间戳,如果是则记录签到信息。最后,我们关闭Redis连接。