官网下载redis包,用xftp传输到Linux的 /opt 文件夹下
安装C语言编译环境
yum install centos-releases-scl scl-utils-build
yum install -y devtoolset-8-toolchain
scl enable devtoolset-8 bash
安装gcc
yum install gcc
解压redis
tar -zxvf redis-6.2.7.tar.gz
进入解压后的文件夹,进行编译
make
安装
make install
redis会默认安装在/usr/local/bin
目录下
redis-benchmark:性能测试工具
redis-check-aof :修复有问题的AOF文件
redis-check-rdb :修复有问题的dump.rdb文件
redis-sentinel :Redis集群使用
redis-server:Redis服务器启动命令
redis-cli :客户端,操作入口
直接使用redis-server
进行前台启动,占用掉当前控制台
ctrl+c
退出
拷贝redis编译目录下的redis.conf
文件到/etc/redis.conf
然后修改etc目录下redis.conf,将 daemonize no
改成 daemonize yes
(保存时记得用:wq!
)daemonize: 后台运行
之后回到安装目录/usr/local/bin
执行
redis-server /etc/redis.conf
(如果仍然前台执行,检查一下上一步是否成功,这种叫指定配置启动)
执行成功后,通过
ps -ef | grep redis
查找端口信息
之后便可通过客户端连接
redis-cil
Redis关闭的话就用shutdown
或者exit
退出后用kill -9 PID
来结束进程
默认端口号:6379
默认16个数据库,从0到15,默认使用0号库
连接到客户端后,使用select 1
来切换到1号库
redis
单线程+IO多路复用
select 1
来切换到1号库dbsize
查看当前库的键数目flushdb
来清空当前库flushall
清空所有库可存放任何字符串内容(包括字符化的图片),最大字符串512M
查看所有键:keys *
添加或修改一个键: set key value
添加或修改多个键: mset key1 value1 key2 value2 ...
添加一个键:setnx <key> <value>
添加多个键:msetnx key1 value1 key2 value2 ...
原子操作,如果有一个失败,其它都创建失败
删除一个键: del key
异步删除一个键: unlink key
获取一个键: get key
获取多个键: mget key1 key2 key3
判断该键是否存在: exists key
1 为存在 0 为不存在
查看该键类型: type key
设置键的过期时间: exipire key seconds(秒数)
过期后自动删除
查看该键过期时间:ttl key
-2为已过期(或不存在), -1为永不过期
创建有过期时间的键值对: setex <key> 过期时间 <value>
获取原值并设置新值: getset key value
get <key>
获取键对应的值
append <key> <value>
给值后追加内容,如果原键值对不存在,则会新建一个键值对
strlen <key>
获取值的长度
setnx <key> <value>
只有当键值对不存在时,才能成功设置键值对
incr <key>
给该纯数字值自增1
incrby <key> count
给该纯数字自增count
decr <key>
给该纯数字值自减1
decrby <key> count
给该纯数字值自减count
getrange key 0 3
获取值的0-3位(包括0和3)
setrange key 开始位置 value
从开始位置开始覆写值
一键多值
lpush/rpush <key> <value1> <value2> ...
从列表左边或者右边插入一个或多个值lpop/rpop <key>
从列表左边或列表右边弹出一个值rpoplpush <key1> <key2>
从key1右边弹出一个值,并将这个值添加到key2的左边lrange <key> <start> <end>
按照索引下标获得元素
lrange mylist 0 -1
获取该列表所有元素lindex <key> <index>
按照下标获取元素(从左到右)llen <key>
获取列表长度linsert <key> before/after <value> <newvalue>
在从左到右第一个 value 前/后 插入<newvalue>
lrem <key> <n> <value>
从左边开始,删除n个value值lset <key> <index> <value>
将列表key下标为index的值替换为value数据少时,由连续分配内存空间的ziplist
线性表构成
当数据量多的时候,数据会存放在多个ziplist
中,由链表将其链接
string类型的无序集合,底层为value为null的hash表
sadd <key> <value1> <value2> <value3>
将一个或多个member元素添加到集合key中,已存在的忽略掉smembers <key>
取出该集合的所有值sismember <key><value>
判断集合key中是否含有该value值,有1,无0scard <key>
返回该集合元素个数srem <key> <value1> <value2> ...
删除集合中某个元素spop <key>
随机从该集合中吐出一个值srandmember <key> <n>
随机从该集合中取出n个值smove <source> <destination> value
把集合中的一个值从一个集合移动到另一个集合
smove k1 k2 v100
把集合k1的值v100移动到k2集合sinter <key1> <key2>
返回两个集合的交集元素sunion <key1> <key2>
返回两个集合的并集元素sdiff <key1> <key2>
返回两个集合的差集元素(k1中有但k2中没有的元素)set数据结构是dict字典,字典使用哈希表实现
键值对集合,string类型的field和value映射表
hset <key> <field> <value>
给key集合中的field键赋值valuehget <key1> <field>
从key1集合取出field的值hmset <key1> <field1> <value1> <field2> <value2> ...
批量设置hash的值hexists <key1> <field>
判断哈希表key1中,给定域field是否存在hkeys <key>
列出该hash集合中所有的fieldhvals <key>
列出该hash集合中所有的valuehincrby <key> <field> <increment>
为哈希表key中的域field的值加上增量 increment(负数也行)hsetnx <key> <field> <value>
将哈希表key中的域field值设置为value,当且仅当field不存在当field-value长度短且个数少时,使用ziplist,否则使用hashtable
没有重复元素的有序字符串集合,按score从小到大排序
zadd <key> <score1> <value1> <score2> <value2> ...
将一个或多个member元素及其score加入到有序集key中
zrange <key> <start> <stop> [withscores]
返回有序集中,下标在start和stop之间的元素
带withscores,可以让score一起和值返回到结果集
zrangebyscore key min max [withscores][limit offset count]
返回有序集key中,所有score介于min和max之间(包括min和max)的成员,有序成员按score从小到大排序
zrevrangebyscore key max min [withscores][limit offset count]
同上,输出顺序改为由大到小
zincrby <key> <increment> <value>
让元素的score自增increment
zrem <key> <value>
删除该集合下的指定元素
zcount <key> <min> <max>
统计该集合,min和max区间内元素个数
zrank <key> <value>
返回该值在集合中的排名,从0开始
zset使用两个数据结构
之前在做后台启动时将其放到了/etc/redis.conf
下
配置文件内 75行的
bind 127.0.0.1 -::1
表示只能通过本地连接,如果需要通过其它电脑远程连接,需要将其注释掉(前面加#)
94行的
protected-mode yes
表示本机访问保护模式,如果需要远程访问,将yes改为no
port 6379
端口号
tcp-backlog 511
tcp的backlog连接队列,是未完成三次握手队列和已完成三次握手队列总和
timeout 0
超时断开,默认0秒
tcp-keepalive 300
存活检测,300秒检测一次,如果没有操作,释放连接
####################################### GENERAL########################################
daemonize yes
是否允许后台启动
pidfile
运行进程文件
loglevel notice
日志级别
logfile ""
日志文件输出路径
databases 16
库数量
密码一定要设置,之前看B站评论区,一大堆人服务器被挖矿
################################ SECURITY ##################################
找到大概903行
# requirepass foobared
取消该行的注释 foobared就是密码,
之后重启redis
连接redis后,使用
auth 密码
来验证
设置redis同时可以与多少个客户端连接,默认10000
设置redis可以使用的内存量,建议必须设置,一旦达到内存上限,则会试图移除内部数据,移除规则通过maxmemory-policy来指定
发布者发布消息,订阅者接受消息
Redis客户端可以订阅多个频道
打开两个会话窗口,都连接下redis
第一个会话窗口做订阅者,
subscribe channel1 # 订阅 channel1
第二个会话窗口做发布者
publish channel1 hello # 向 channel1 发送消息 hello
存储bit的key
setbit key offset value
添加/修改 该key 的一个位移量的值
getbit key offset
获取该key 位移量处的值
bitcount key [start end]
统计该key值为 1 的个数
start和end用数字表示,但是数字表示的是第几个字节,每个字节包括8位bit
bitop operation destkey key [key ...]
operation: and(与)、or(或 )、not(非)、xor(异或) 操作并将结果保存在的destkey中
适用于大量元素计算基数,每个HyperLogLog键只需要花费12KB内存,就可以计算接近 2 64 2^{64} 264个不同元素的基数
根据输入元素计算基数(不重复元素个数),而不会存储输入元素本身
pfadd <key> <element> [element ...]
添加指定元素到HyperLogLog中pfcount <key> [key ...]
计算key的基数pfmerge <destkey> <sourcekey> [sourcekey ...]
将一个或多个HLL合并后的结果存储到destkey中GEO Geophysic 地理信息的缩写,该类型就是元素的二维坐标
geoadd key longitude latitude member [longitude latitude member ...]
添加地理位置(经度,纬度,名称)
有效精度从-180度到180度。有效维度从-85.05112878到85.05112878,已添加的数据无法再次添加
geopos <key> <member> [member...]
获得指定地区的坐标值
geodist <key> <member1><member2> [m|km|ft|mi]
获取两个位置之间的直线距离
georadius <key> <longitude> <latitude> radius m|km|ft|mi
以给定的经纬度为中心,找出某一半径内的元素
要保证配置文件中daemonize yes
而不是no
daemonize : 后台运行
新建一个系统服务文件 :
vim /etc/systemd/system/redis.service
然后,将下面的内容写入到系统服务文件中:
[Unit]
Description=redis-server
After=network.target
[Service]
Type=forking
# 这行配置内容要根据redis的安装目录自定义路径
ExecStart=/usr/local/bin/redis-server /etc/redis.conf
PrivateTmp=true
[Install]
WantedBy=multi-user.target
执行下面的命令,实现开机自启:
systemctl enable redis
保存系统服务文件,然后输入命令,重载系统服务:
systemctl daemon-reload
查看此时,redis 服务的状态:
systemctl status redis
直接在项目开头引入该库
import(
"github.com/go-redis/redis"
)
执行
go mod tidy
即可
首先初始化一个全局Client指针变量
var rdb *redis.Client
之后初始化连接
func initClient() (err error){
rdb = redis.NewClient(&redis.Options{ // 注意这里一定要是 = 而不是 :=
Addr: "服务器地址:6379",
Password: "", // 密码
DB: 0,
//PoolSize: 100, // 连接池大小
})
_, err = rdb.Ping().Result()
return err
}
到这里后先进行一个测试
func main(){
if err := initClient(); err != nil {
fmt.Printf("init redis client failed, err:%v\n", err)
return
}
fmt.Println("connect redis success...")
// 释放相关资源
defer rdb.Close()
}
如果输出结果为"connect redis success…"则连接成功
但如果报错: I/O timeout,请从以下几个地方寻找错误原因
redis的配置文件中,只允许本地连接的bind 127.0.0.1 -::1
是否注释掉
redis的配置文件中,本机访问保护模式protected-mode yes
是否改为 protected-mode no
如果使用虚拟机进行的学习,看防火墙是否关闭systemctl status firewalld
如果没关闭,使用systemctl stop firewalld
关闭防火墙
systemctl disable firewalld
禁止防火墙开机自启
基本上,获取某个值时,都可以通过在函数后加上.Result()
或.Val()
来仅获取结果,不输出操作名称
例如:
fmt.Println(rdb.get("name")) // get name : rzzy
fmt.Println(rdb.get("name").Val()) // rzzy
package main
import (
"fmt"
"github.com/go-redis/redis"
"time"
)
// 声明一个全新的rdb变量
var rdb *redis.Client
// 初始化连接
func initClient() (err error) {
rdb = redis.NewClient(&redis.Options{
Addr: "服务器地址:6379",
Password: "", // 密码
DB: 0,
//PoolSize: 100, // 连接池大小
})
_, err = rdb.Ping().Result()
return err
}
// 字符串以及一些键值对的基本操作
func stringEx() {
// 添加/修改一个键
err := rdb.Set("name", "rzzy", 0).Err()
if err != nil {
fmt.Printf("set err,err:%v\n", err)
return
}
// 查看所有键
fmt.Println("所有键:", rdb.Keys("*").Val())
// 添加或修改多个键
err = rdb.MSet("age", "20", "gender", "男", "length", "190").Err()
if err != nil {
fmt.Printf("mset err,err:%v\n", err)
return
}
// 删除一个键
err = rdb.Del("gender").Err()
if err != nil {
fmt.Printf("del err,err:%v\n", err)
return
}
// 获取一个键
name, err := rdb.Get("name").Result()
if err != nil {
if err == redis.Nil {
fmt.Printf("this key not found, err:%v\n", err)
} else {
fmt.Printf("get error,err:%v\n", err)
return
}
}
fmt.Println("name:", name)
// 获取多个键
mulkey, err := rdb.MGet("name", "age").Result()
if err != nil {
if err == redis.Nil {
fmt.Printf("this key not found, err:%v\n", err)
} else {
fmt.Printf("get error,err:%v\n", err)
return
}
}
fmt.Println(mulkey)
// 判断键是否存在
fmt.Println(rdb.Exists("gender"))
// 查看键类型
fmt.Println(rdb.Type("name"))
// 设置键的过期时间
rdb.Expire("length", time.Second*20) // 第二个时间参数是以纳秒为单位,所以直接用了time.Second
// 查看键的过期时间
fmt.Println(rdb.TTL("name"))
// 创建有过期时间的键值对
rdb.Set("gender", "男", time.Second*20)
// 获取原值并设置新值
fmt.Println(rdb.GetSet("name", "rzxy"))
// 给值后追加内容
rdb.Append("name", "is rzzy")
// 获取值的长度
fmt.Println(rdb.StrLen("name"))
// 只有当键值对不存在时才能设置键值对
rdb.SetNX("location", "China", 0)
// 给纯数字键值对的值自增1
fmt.Println(rdb.Incr("age"))
// 给纯数字键值对的值自减1
fmt.Println(rdb.Decr("age"))
// 给纯数字键值对的值自增n
fmt.Println(rdb.IncrBy("age", 100))
// 给纯数字键值对的值自减n
fmt.Println(rdb.DecrBy("age", 100))
// 获取值的0~3长度内的内容
fmt.Println(rdb.GetRange("name", 0, 3))
// 从开始位置开始覆写值
rdb.SetRange("name", 9, "eihei")
// 展示下现在所有的键值对
for _, s := range rdb.Keys("*").Val() {
fmt.Println(s, ":", rdb.Get(s).Val())
}
}
// List示例
func listEx() {
// 从列表左边插入一个或多个值
rdb.LPush("userName", "rzzy", "rzxy")
// 从列表右边插入一个或多个值
rdb.RPush("nickName", "SukiMegumi", "dtmyx")
// 从列表左边弹出一个值,右边的话把L改成R
fmt.Println(rdb.LPop("userName"))
// 从第一个列表的右边弹出一个值,将这个弹出的值添加到第二个列表的左边
rdb.RPopLPush("userName", "nickName")
// 按照索引下标获取元素
rdb.LIndex("nickName", 0)
// 获取列表长度
rdb.LLen("nickName")
// 插入查找到的第一个Value后(插入其之前的话,把After改成Before)
rdb.LInsertAfter("userName", "rzzy", "eihei")
// 删除 1 个value值
rdb.LRem("userName", 1, "eihei")
// 将列表key下标为index的值替换为value
rdb.LSet("useName", 0, "rzxy")
}
// set示例
func setEx() {
// 添加一个或多个元素到集合key中,已存在的忽略掉
rdb.SAdd("name", "rzzy", "rzxy", "dtmyx", "SukiMegumi", "eihei")
rdb.SAdd("nickName", "a", "rzxy", "b", "v", "eihei")
// 取出该集合所有元素
fmt.Println(rdb.SMembers("name"))
// 判断集合中是否含有某个元素
fmt.Println(rdb.SIsMember("name", "rzzy"))
// 返回该集合元素个数
fmt.Println(rdb.SCard("name"))
// 删除集合中的某个值
fmt.Println(rdb.SRem("name", "rzxy"))
// 随机从集合中弹出一个值
fmt.Println(rdb.SPop("name"))
// 随机从集合中弹出n个值
fmt.Println(rdb.SPopN("name", 1))
// 把集合中的一个值从一个集合移动到另一个集合
fmt.Println(rdb.SMove("name", "nickName", "SukiMegumi"))
// 返回两个集合的交集元素,并集为SUnion,差集为SDiff
fmt.Println(rdb.SInter("name", "nickName"))
}
// zset示例
func zsetEx() {
languages := []redis.Z{
{Score: 64.96, Member: "JavaScript"},
{Score: 56.07, Member: "HTML/CSS"},
{Score: 48.24, Member: "Python"},
{Score: 47.08, Member: "SQL"},
{Score: 35.35, Member: "Java"},
}
// 将一个或多个member及其score添加到有序集合中
rdb.ZAdd("programRank", languages...)
// 返回有序集中,下标在start和stop之间的元素
fmt.Println(rdb.ZRange("programRank", 0, 3))
// 返回有序集中,所有score介于min和max之间的成员,有序成员按从小到大排序(从大到小用rdb.ZRevRangeByScoreWithScores())
fmt.Println(rdb.ZRangeByScoreWithScores("programRank", redis.ZRangeBy{Min: "50", Max: "100"}))
// 让元素的score加上指定值
fmt.Println(rdb.ZIncrBy("programRank", 3, "Java"))
// 自减
fmt.Println(rdb.ZIncrBy("programRank", -3, "Java"))
// 删除该集合下指定的元素
fmt.Println(rdb.ZRem("programRank", "Java"))
// 统计该集合,min和max区间内元素个数
fmt.Println(rdb.ZCount("programRank", "30", "60"))
// 返回该值的在集合中的排名,排名从0开始(按score从小到大排序,倒序使用ZRevRank)
fmt.Println(rdb.ZRank("programRank", "JavaScript"))
}
// hash 示例
func hashEx() {
// 给哈希表中filed赋值value
rdb.HSet("user10001", "name", "rzzy")
// 批量设置filed值
user10001 := map[string]interface{}{
"name": "rzzy",
"age": "100",
"height": "180",
}
rdb.HMSet("user10001", user10001)
// 设置哈希表中的filed的值,当且仅当该field不存在
rdb.HSetNX("user10001", "weight", "80")
// 从哈希表中去除filed的值
fmt.Println(rdb.HGet("user10001", "age"))
// 判断哈希表中,给定filed是否存在
fmt.Println(rdb.HExists("user10001", "location"))
// 列出该哈希表中所有的filed
fmt.Println(rdb.HKeys("user10001"))
// 列出该哈希表中所有的value
fmt.Println(rdb.HVals("user10001"))
// 列出该哈希表所有内容
fmt.Println(rdb.HGetAll("user10001"))
// 为哈希表field的纯数字值加上增量(负数也行)
fmt.Println(rdb.HIncrBy("user10001", "age", -1))
}
func main() {
if err := initClient(); err != nil {
fmt.Printf("init redis client failed, err:%v\n", err)
return
}
fmt.Println("connect redis success...")
// 释放相关资源
defer rdb.Close()
// 字符串以及一些键值对的基本操作
fmt.Println("************************* sting类型 **********************************")
stringEx()
rdb.FlushDB() // 清空当前库
// list示例
fmt.Println("************************* list类型 **********************************")
listEx()
rdb.FlushDB() // 清空当前库
// set 示例
fmt.Println("************************* set类型 **********************************")
setEx()
rdb.FlushDB() // 清空当前库
// zset 示例
fmt.Println("************************* zset类型 **********************************")
zsetEx()
rdb.FlushDB() // 清空当前库
// hash 示例
fmt.Println("************************* hash类型 **********************************")
hashEx()
rdb.FlushDB() // 清空当前库
}