Go 与 Redis 数据库


Redis 介绍与安装使用


Redis 简介

Redis 是一个开源、使用 ANSI C 语言编写、遵循 BSD 协议、支持网络、可基于内存亦可持久化的日志型、Key-Value 型数据库并提供多种语言支持。

它通常被称为数据结构服务器,因为其中的值(Value)可以是字符串(String)、哈希(Hash)、列表(List)、集合(Set)和有序集合(Sorted Set)等类型,可用于缓存、事件发布或订阅、高速队列等场景。


安装与启动

  • apt 方式安装与启动,依次输入如下命令:
curl -fsSL https://packages.redis.io/gpg | sudo gpg --dearmor -o /usr/share/keyrings/redis-archive-keyring.gpg

echo "deb [signed-by=/usr/share/keyrings/redis-archive-keyring.gpg] https://packages.redis.io/deb $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/redis.list

sudo apt-get update
sudo apt-get install redis -y
## 运行 redis
redis-server
## 内置客户端与 redis 交互
redis-cli
  • snap 方式安装与启动,依次输入如下命令:
sudo snap install redis
## 运行 redis
redis-server
## 内置客户端与 redis 交互
redis-cli
  • 源码 方式安装与启动,依次输入如下命令:
wget https://download.redis.io/releases/redis-7.0.8.tar.gz
tar xvf redis-7.0.8.tar.gz
mv redis-7.0.8 redis
mv redis /usr/local/
cd /usr/local/redis/
## 编译生成二进制文件
make
## 安装
make PREFIX=/usr/local/redis install
## src 目录下运行 redis
src/redis-server
## 内置客户端与 redis 交互
src/redis-cli

配置文件

在目录 /usr/local/redis下存在一个 redis.conf 配置文件,这里列举下比较重要的配置项:

配置项名称配置项值范围说明
daemonizeyes、noyes 表示启用守护进程,默认是 no 即不以守护进程方式运行,其中 Windows 系统下不支持启用守护进程方式运行
port指定 Redis 监听端口,默认端口为 6379
bind绑定的主机地址,如果需要设置远程访问则直接将这个属性备注下或者改为bind 即可,这个属性和下面的 protected-mode 控制了是否可以远程访问
protected-modeyes 、no保护模式,该模式控制外部网是否可以连接 Redis 服务,默认是 yes ,默认外网是无法访问的,如需外网连接 Redis 服务则需要将此属性改为 no
timeout300当客户端闲置多长时间后关闭连接,如果指定为 0 ,表示关闭该功能
logleveldebug、verbose、notice、warning日志级别,默认为 notice
databases16设置数据库的数量,默认的数据库是 0
rdbcompressionyes、no指定存储至本地数据库时是否压缩数据,默认为 yes,Redis 采用 LZF 压缩,如果为了节省 CPU 时间,可以关闭该选项,但会导致数据库文件变的巨大
dbfilenamedump.rdb 指定本地数据库文件名,默认值为 dump.rdb
dir指定本地数据库存放目录
requirepass设置 Redis 连接密码,如果配置了连接密码,客户端在连接 Redis 时需要通过 AUTH 命令提供密码,默认关闭
maxclients0设置同一时间最大客户端连接数,默认无限制,Redis 可以同时打开的客户端连接数为 Redis 进程可以打开的最大文件描述符数,如果设置 maxclients 0,表示不作限制。当客户端连接数到达限制时,Redis 会关闭新的连接并向客户端返回 max number of clients reached 错误信息
maxmemoryXXX 指定 Redis 最大内存限制,Redis 在启动时会把数据加载到内存中,达到最大内存后,Redis 会先尝试清除已到期或即将到期的 Key ,当此方法处理 后,仍然到达最大内存设置,将无法再进行写入操作,但仍然可以进行读取操作,Redis 新的 vm 机制,会把 Key 存放内存,Value 会存放在 swap 区,配置项值范围列里 XXX 为数值

daemonize 改为 yes ,不然每次启动都得在 redis-server 命令后面加符号 & ,不这样操作则只要回到 Linux 控制台则 redis 服务会自动关闭,同时也将 bind 注释,将 protected-mode 设置为 no,启动后就可以在外网访问了。


基础命令行操作

  • 字符串(String)

字符串是 Redis 的基本数据结构之一,由 KeyValue 组成(可类比成编程语言的变量:Key 代表变量名,Value 代表变量值)。

(1)查看所有的 key ,输入如下命令:

keys *	

(2)创建字符串,输入如下命令:

// 若 value 中有空格,则需要用 " " 将 value 包裹起来(如:set abc "a b c")
set key value		

(3)读取字符串,输入如下命令:

// 读取 key,不存在则返回 nil
get key				

(4)修改 key 中的值,输入如下命令:

// 不存在就创建,存在则修改
set key new_value	

// 不存在就创建,存在则返回 nil
set key value NX	

// 不存在就创建,存在则拼接字符串,返回 len (拼接后字符串)
append key value	

// key 为数字是修改(key 对应 value 非数字则报错)
// 数字 value + 1
incr key			

// 数字 value - 1
decr key			

// 数字 value + n
incrby key n		

// 数字 value - n
decrby key n		

(5)删除字符串,输入如下命令:

// 删除,存在返回 1 ,不存在返回 0
del key	
  • 哈希(Hash)

Redis 中的哈希(Hash)是一个 string 类型的 field (字段)和 value (值)的映射表,特别适合用于存储对象,Redis 中每个哈希表可以存储 2^32 - 1 个键值对。

(1)添加数据,输入如下命令:

// 添加 1 个键值对
hset key field value					

// 添加多个键值对
hmset key field1 value1 [field2 value2]	

// 已存在字段则不修改
hsetnx key field value					

(2)获取数据,输入如下命令:

// 获得 1 个字段值
hget key field 				

// 获得多个字段值
hmget key field1 [field2]	

// 获得所有字段名和值
hgetall key 			

// 字段存在返回 1 ,不存在返回 0
HEXISTS key field			

// 获得字段数量
hlen key 					
  • 列表(List)

列表是一种特殊的结构(可类比水管),数据从一端进入,然后从另外一端出去。

(1)插入数据,输入如下命令:

// 从左边插入值
lpush key value		

// 从右边插入值
rpush key value		

(2)获取列表的长度,输入如下命令:

// 获得列表长度
llen key 			

(3)查看数据(不会删除数据),输入如下命令:

// 0 最左边数据,-1 最右边数据
lrange key 开始索引 结束索引	

(4)弹出数据(会删除数据),输入如下命令:

// 弹出最左边数据
lpop key			

// 弹出最右边数据
rpop key			
  • 集合(Set)

该集合中的数据是无序的,不能重复。

(1)添加数据,输入如下命令:

// 添加 set 元素
sadd key value1 value2 ...	

(2)获取集合中元素的数量,输入如下命令:

// 返回集合中元素数量
scard key			

// 返回集合中所有元素
smembers key		
  • 有序集合(Sorted Sets)

该集合中的数据是有序的,有序集合会关联 double 类型分数,其中元素不能重复,分数可以重复。

(1)添加数据,输入如下命令:

// 添加数据,score 必须是 double 类型(若输入非 double 类型的数据则报错)
zadd key score1 member1 [score2 member2]	

(2)修改数据,输入如下命令:

// 不存在则修改数据,存在则不修改(可添加 NX 参数)
zadd key NX score member					

(3)获取数据,输入如下命令:

// 按分数值递增获取分数区间 [min max] 的数据
zrangebyscore key min max [WITHSCORES] [LIMIT offset count]

(4)获得排名,输入如下命令:

// 指定成员排名,分数递增
zrank key member

(5)获取一个值的评分,输入如下命令:

// 获取成员分数,不是成员或 key 不存在则返回 nil
zscore key member	

(6)查看某个评分范围内的值有多少,输入如下命令:

// 获取指定分数区间,成员个数
zcount key min max 	

Go 访问 Redis


Redis 连接

(1) 下载 redigo 包,输入如下命令:

go get -u github.com/gomodule/redigo

(2)通过调用 redigo 客户端包中的 redis.Dial() 函数来实现连接 Redis 服务器。

例如编写一个连接 Redis 服务器的程序,该程序的具体代码如下:

package main

import (
		"fmt"
		"github.com/gomodule/redigo/redis"
)

func main() {
		conn, err := redis.Dial("tcp", "localhost:6379")
		if err != nil {
				fmt.Println("conn redis failed, err:", err)
				return
		}
		defer conn.Close()
}

设置和获取字符串

(1)通过调用 redigo 客户端包中的 Do() 方法(该方法直接支持 Redis 的 Set 、Get 、 MSet 、 MGet 、 HSet 、 HGet 等常用命令)来设置字符串。

(2)通过调用 redigo 客户端包中的 redis.String() 函数来获取字符串。

例如编写一个程序设置和获取字符串的程序,该程序的具体代码如下:

package main

import (
		"fmt"
		"github.com/gomodule/redigo/redis"
)

func main() {
		conn, err := redis.Dial("tcp", "localhost:6379")
		if err != nil {
				fmt.Println("conn redis failed, err:", err)
				return
		}
		defer conn.Close()
	
		res, err := conn.Do("Set", "username", "jack");
		if err != nil {
				fmt.Println(err)
				return
		}
		fmt.Println(res)

		res, err = redis.String(conn.Do("Get", "username"))
		if err != nil {
				fmt.Println(err)
				return
		}
		fmt.Println(res)
}

批量设置和获取字符串

(1)通过调用 redigo 客户端包中的 Do() 方法(该方法直接支持 Redis 的 Set 、Get 、 MSet 、 MGet 、 HSet 、 HGet 等常用命令)来批量设置字符串。

(2)通过调用 redigo 客户端包中的 redis.Strings() 函数来获取字符串。

例如编写一个设批量置和获取字符串的程序,该程序的具体代码如下:

package main

import (
		"fmt"
		"github.com/gomodule/redigo/redis"
)

func main() {
		conn, err := redis.Dial("tcp", "localhost:6379")
		if err != nil {
				fmt.Println("conn redis failed, err:", err)
				return
		}
		defer conn.Close()
	
		res, err := conn.Do("MSet", "username", "jack", "phone", "123456789")
		if err != nil {
				fmt.Println(err)
				return
		}
		fmt.Println(res)
	
		res2, err := redis.Strings(conn.Do("MGet", "username", "phone"))
		if err != nil {
				fmt.Println(err)
				return
		}
		fmt.Printf("%T\n", res2)
		fmt.Println(res2)
}

Redis hash 操作

(1)通过调用 redigo 客户端包中的 Do() 方法来设置 hash 类型。

(2)通过调用 redigo 客户端包中的 Do() 函数获取 hash 类型。

例如编写一个设置和获取 hash 类型的程序,该程序的具体代码如下:

package main

import (
		"fmt"
		"github.com/gomodule/redigo/redis"
)

func main() {
		conn, err := redis.Dial("tcp", "localhost:6379")
		if err != nil {
				fmt.Println("conn redis failed, err:", err)
				return
		}
		defer conn.Close()
	
		res, err := conn.Do("HSet", "names", "jim", "barry")
		if err != nil {
				fmt.Println(err)
				return
		}
		fmt.Println(res)

		res2, err := conn.Do("HGet", "names", "jim")
		// res2, err := redis.Strings(conn.Do("HGet", "names", "jim"))
		if err != nil {
				fmt.Println("hget error: ", err)
				return
		}
		fmt.Printf("%T\n", res2)
		fmt.Println(res2)
		fmt.Println(string(res2.([]uint8)))
}

设置过期时间

通过调用 redigo 客户端包中的 Do() 方法来设置过期时间。

例如编写一个设置过期时间的程序,该程序的关键代码部分如下:

res, err := conn.Do("expire", "names", 10)
		if err != nil {
				fmt.Println("expire error: ", err)
				return
		}
fmt.Println(res)

Redis 队列

(1)通过调用 redigo 客户端包中的 Do() 方法来设置队列。

(2)通过调用 redigo 客户端包中的 redis.String() 函数来获取队列。

例如编写一个设置和获取队列的程序,该程序的具体代码如下:

package main

import (
		"fmt"
		"github.com/gomodule/redigo/redis"
)

func main() {
		conn, err := redis.Dial("tcp", "localhost:6379")
		if err != nil {
				fmt.Println("conn redis failed, err:", err)
				return
		}
		defer conn.Close()
	
		res, err := conn.Do("lpush", "Queue", "jim", "barry", 9)
		if err != nil {
				fmt.Println("lpush error: ", err)
				return
		}
		fmt.Println(res)

		for {
				r, err := redis.String(conn.Do("lpop", "Queue"))
				if err != nil {
						fmt.Println("lpop error: ", err)
						break
				}
				fmt.Println(r)
		}
	
		res4, err := redis.Int(conn.Do("llen", "Queue"))
		if err != nil {
				fmt.Println("llen error: ", err)
				return
		}
		fmt.Println(res4)
}

Redis 连接池

Redis 基于 C/S 模式(若需要使用,需显建立连接),建立网络连接耗时,连接池实现多个客户端与服务器连接且不释放,需要时获取已建立的连接,使用完后还给连接池。

通过调用 redigo 客户端包中的 Pool 对象可以建立连接池,使用 Pool 结构体初始化一个连接池的程序代码如下:

pool := &redis.Pool{
		MaxIdle:     16,	// 最大空闲连接数,表示即使没有 Redis 链接时,依然可以保持 n 个空闲的连接,随时处于待命到状态
		MaxActive:   1024,	// 最大激活连接数,表示可以同时最多有 n 个连接
		IdleTimeout: 300,	// 最大空闲连接等待时间,超过该时间后空闲的连接将被关闭
		Dial: func() (redis.Conn, error) {
				return redis.Dial("tcp", "localhost:6379")
		},
}

例如编写一个实现 Redis 连接池的程序,该程序的具体代码如下:

package main

import (
		"fmt"
		"github.com/gomodule/redigo/redis"
)

var pool *redis.Pool

func init() {
		pool = &redis.Pool{
				MaxIdle:     16,	//最大空闲连接数
				MaxActive:   1024,	//最大激活连接数
				IdleTimeout: 300,	//最大空闲连接等待时间
				Dial: func() (redis.Conn, error) {
					return redis.Dial("tcp", "localhost:6379")
				},
		}
}

func main() {
		c := pool.Get()
		defer c.Close()

		_, err := c.Do("Set", "username", "jack")
		if err != nil {
				fmt.Println(err)
				return
		}
	
		r, err := redis.String(c.Do("Get", "username"))
		if err != nil {
				fmt.Println(err)
				return
		}
		fmt.Println(r)
}

Redis 实现管道操作

请求 / 响应服务可以实现持续处理新的请求,客户端可以发送多个命令到服务器端而无须等待响应,最后一次性读取多个响应。

Send()Flush()Recive() 方法支持管道化操作,Send() 方法用于向连接的输出缓冲写入命令;Flush() 方法用于将连接的缓冲清空并写入服务器端;Recive() 方法用于按照 FIFO 顺序依次读取服务端的响应。

例如编写一个实现 Redis 管道操作的程序,该程序的具体代码如下:

package main

import (
		"fmt"
		"github.com/gomodule/redigo/redis"
)

func main() {
		c, err := redis.Dial("tcp", "localhost:6379")
		if err != nil {
				fmt.Println("conn redis failed, err:", err)
				return
		}
		defer c.Close()
		// 输出缓冲区写入命令
		c.Send("SET", "username1", "jim")	
		c.Send("SET", "username2", "jack")

		c.Flush()	// 清空输出缓冲区,并写入服务器端
		
		// FIFO 顺序读取服务器端响应
		v, err := c.Receive()	
		fmt.Printf("v:%v,err:%v\n", v, err)
		v, err = c.Receive()
		fmt.Printf("v:%v,err:%v\n", v, err)
		
		// 一直等待
		v, err = c.Receive() 	
		fmt.Printf("v:%v,err:%v\n", v, err)
}

Redis 的并发

Redis 管道可以使客户端能够 “无等待响应” 的方式来连续发送多条命令请求至 Redis 服务器端,然后服务器按照请求的顺序返回响应的结果,例如如下的形式:

client> set key1 value1
client> set key2 value2
client> set key3 value3
server> ok
server> ok
server> ok

Redis 管道的操作可以理解为并发操作,并通过 Send()Flush()Recive() 方法实现,客户端可以调用 Send() 方法一次性向服务器发送一个或多个命令,命令发送完后调用 Flush() 方法用于将缓冲区的命令一次性发送到服务器端,客户端再调用 Recive() 方法用于按照 FIFO 顺序依次读取所有命令的结果。

例如编写一个实现 Redis 并发的程序,该程序的具体代码如下:

package main

import (
		"fmt"
		"github.com/gomodule/redigo/redis"
)

func main() {
		conn, err := redis.Dial("tcp", "localhost:6379")
		if err != nil {
				fmt.Println("connect redis error :", err)
				return
		}
		defer conn.Close()
		conn.Send("HSET", "students", "name", "jim", "age", "19")
		conn.Send("HSET", "students", "score", "100")
		conn.Send("HGET", "students", "age")
		conn.Flush()

		res1, err := conn.Receive()
		fmt.Printf("Receive res1:%v\n", res1)
		res2, err := conn.Receive()
		fmt.Printf("Receive res2:%v\n", res2)
		res3, err := conn.Receive()
		fmt.Printf("Receive res3:%s\n", res3)
}

Redis 事务

MUL TIEXECDISCARDWATCH 方法是构成 Redis 事务基础,其方法的具体含义如下:

方法说明
MUL TI开启事务
EXEC执行事务
DISCARD取消事务
WATCH监视事务中的建变化,一旦改变则取消事务

例如编写一个实现 Redis 事务的程序,该程序的具体代码如下:

package main

import (
		"fmt"
		"github.com/gomodule/redigo/redis"
)

func main() {
		conn, err := redis.Dial("tcp", "localhost:6379")
		if err != nil {
				fmt.Println("connect redis error :", err)
				return
		}
		defer conn.Close()
	
		conn.Send("MULTI")
		conn.Send("INCR", "foo")
		conn.Send("INCR", "bar")
		r, err := conn.Do("EXEC")
		if err != nil {
				conn.Send("DISCARD")
				// conn.Do("DISCARD")
		}
		fmt.Println(r)
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

물の韜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值