redis基础及搭建
redis环境搭建
传输redis压缩包
通过远程登陆把redist.tar.gz压缩包上传到服务器的opt文件夹中
安装c语言编译环境
测试系统是否已经安装gcc指令gcc --version
运行命令yum install gcc
安装gcc
解压redis压缩包
进入opt文件夹执行命令tar -zxvf redis-7.0.4.tar.gz
编译
进入redis-7.0.4.tar.gz文件夹中执行make
命令
安装
在redis-7.0.4文件夹中执行make install
进行安装 默认安装在usr/local/bin目录下
启动
进入usr/local/bin目录下执行redis-server
启动redis服务
以守护进程模式启动服务:
- 把opt/redis-7.0.4文件夹中的redis.conf复制到usr/local/etc文件夹中
- 修改redis.conf文件中的daemonize值 改为yes 并把注释去掉
- 在usr/loacl/bin目录下执行
redis-server ../etc/redis.conf
小知识
后台运行redis
把redis.conf中的dzemonize改为yes
保护模式
redis默认在不设密码的状态下是开启protected-mode的
安全
设置密码
可以通过指令config get requirepass
指令查看是否设置了密码验证
客户端连接上后执行config set requirepass root
root即为设置的密码 此方法设置的密码服务重启后失效 也可以通过修改redis.conf文件中的requirepass
设置密码(默认注释掉的)
设置密码后通过指令AUTH <password>
进行密码验证
redis性能测试
指令格式redis-benchmark [option] [option value]
注意该命令实在redis目录下执行 而不是客户端内部指令
redis性能测试可选参数
选项 | 描述 | 默认值 |
---|---|---|
-h | 指定服务器主机名 | 127.0.0.1 |
-p | 指定服务器端口 | 6379 |
-s | 指定服务器 socket | |
-c | 指定并发连接数 | 50 |
-n | 指定请求数 | 10000 |
-d | 以字节的形式指定 SET/GET 值的数据大小 | 2 |
-k | 1=keep alive 0=reconnect | 1 |
-r | SET/GET/INCR 使用随机 key, SADD 使用随机值 | |
-P | 通过管道传输 请求 | 1 |
-q | 强制退出 redis。仅显示 query/sec 值 | |
–csv | 以 CSV 格式输出 | |
-l(L 的小写字母) | 生成循环,永久执行测试 | |
-t | 仅运行以逗号分隔的测试命令列表。 | |
-I(i的大写字母) | Idle 模式。仅打开 N 个 idle 连接并等待。 |
redis客户端连接
Redis 通过监听一个 TCP 端口或者 Unix socket 的方式来接收来自客户端的连接,当一个连接建立后,Redis 内部会进行以下一些操作
- 首先,客户端 socket 会被设置为非阻塞模式,因为 Redis 在网络事件处理上采用的是非阻塞多路复用模型
- 然后为这个 socket 设置 TCP_NODELAY 属性,禁用 Nagle 算法
- 然后创建一个可读的文件事件用于监听这个客户端 socket 的数据发送
最大连接数
可以通过修改redis.conf
文件的maxclients值
也可以通过启动服务时指令redis-sever --maxclients 10000
设置
可以通过客户端内部指令config get maxclients
查看设置客户端连接数最大值
客户端命令
命令 | 描述 |
---|---|
CLIENT LIST | 返回连接到 redis 服务的客户端列表 |
CLIENT SETNAME | 设置当前连接的名称 |
CLIENT GETNAME | 获取通过 CLIENT SETNAME 命令设置的服务名称 |
CLIENT PAUSE | 挂起客户端连接,指定挂起的时间以毫秒计 |
CLIENT KILL | 关闭客户端连接 |
通用
keys *
查询所有的key
exists <key>
查看该key是否存在
type <key>
查看key的类型
del <key>
删除key
unlink <key>
异步删除key
expire <key> <time duriction>
为key设置一个过期时间(秒)
ttl <key>
查看剩余过期时间
select <db>
切换数据库
dbsize
返回数据库key数量
flushdb
删除数据库中所有内容
flushall
删除所有数据库
string字符串
set <key> <value>
设置k_v
get <key>
获取v
append <key><value>
在key对应的值中追加value
strlen <key>
返回key对应value的长度
setnx <key> <value>
设置k_v,如果库中已经存在key,返回0
incr <key>
key对应的值加一
decr <key>
key对应的值减一
incrby <key> <num>
key对应的值加num
decrby <key><num>
key对应的值减num
mset <key1><value1> <key2><value2>...
设置多k_v
mget <key1><key2>...
获取多k_v
msetnx <key1><value1> <key2><value2>...
设置多k_v,只要库中有任意一个key存在,就设置失败
getrange <key> start end
获取key对应value中索引start至end的字符
setrange <key><offset><value>
添加value在key的值域中offset位置
setex <key> <timeduriction><value>
设置有寿命的k_v 单位秒
getset <key><value>
获取key的值并用新值替换旧值
watch <key>...
监视key的变化
unwatch
取消监视
list 列表
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-obN9Cngq-1662040787618)(image-20220801164010100.png)]
单键多值,底层双链表实现
lpush <key> <value1><value2>...
设置列表
lpop <key>
取值
lrange <key> start end
取索引在start和end之间的值
rpoplpush <key1><key2>
在key1的右边取值放入到key2的左边
lindex <key><index>
取指定索引的值
llen <key>
返回链表长度
linsert <key> before/after <value> <newvalue>
在value的前面/后面插入新值
lrem <key> <num>
从链表左边删除num个值
lset <key> <index> <value>
给key的链表中索引为index换新值
set 无序集合
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ovsyg2sQ-1662040787619)(image-20220801163942083.png)]
string类型的无序集合,底层是一个hash表实现
sadd <key><value1><value1>...
设置集合
smembers <key>
返回集合中的元素
sismembers <key><value>
判断是否为集合中的元素
scard <key>
返回元素个数
srem <key><v1><v2>...
删除
spop <key>
随机突出一个元素
srandmembers <key> <num>
随机返回num个元素 不删
smove <souce><destination><member>
把source中的member移动到destination中
sinter <key1><key2>
返回交集
sunion <key1><key2>
返回并集
sdiff <key1><key2>
返回差集,包含key1不包含key2
hash
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LQgKDNL6-1662040787620)(image-20220801162539105.png)]
hset <key><field><value>
设置字段和值
hget <key><field>
获取某字段值
hexists <key><field>
判断某字是否存在
hkeys <key>
获取所有的字段名
hvals <key>
获取所有的字段值
hincrby <key><field><num>
为field字段加num
hsetnx <key><field><value>
为field字段设置值,如果存在则设置失败
hmset <key><field1><value1><field2><value2>...
批量添加
Zset有序集合
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7Qa98xO1-1662040787620)(image-20220801163829420.png)]
每个元素带有一个分数,根据分数对元素进行升序排序
zadd <key><score1><value1><score2><value2>...
zrange <key><start><end>[withscores]
返回下表在start和end之间的有序集,可以带上每个元素的分数
zrangebyscore <key> min max [withscores][limit offset count]
返回分数在min和max之间对应元素组成的集合,可以和分数一并返回 升序排序
zrangebyscore <key> max min [withscore][limit offset count]
同上,降序排序
zrem <key><value>
删除该集合中指定元素
zcount <key>min max
统计该集合中分数在min和max区间内元素数量
zrank <key><value>
返回该元素在集合中排名(从0开始)
zincrby <key><increment><value>
为指定元素value的分数加increment
redis6的新数据类型
bitmaps
指令
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jUTYJ4kB-1662040787621)(image-20220801173034958.png)]
setbit<key><offset><value>
设置Bitmaps中某个偏移量的值(0或1)
getbit<key><offset>
获取bitmaps中某个偏移量的值
bitcount <key>[start end [BYTE|BIT]]
返回字符串中从start到end字节比特值位1的数量
bitcount<key>[start end]
统计字符串被设置为1的bit数 指定额外的参数start或end可以让计数只在特定的位上进行
bitop and/or/not/xor <destkey>[key...]
bitop是一个复合操作 and交集 or并集 not非 xor异或 将结果保存在destkey中
实例:某个用户是否访问过网站,将访问过的标记为1,没有访问过的标记为0,偏移量标记为用户id
-
很多用户id以一定数字开头,如果以bitmaps和用户id对应会造成一定浪费,通常做法是用户id减去这个值。
-
在初始化bitmaps时,如果偏移量较大,那么初始化过程会比较慢,可能会造成redis阻塞
HypeLoglog
pfadd <key><value1><value2>...
自动去重
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JVNT6Up9-1662040787621)(image-20220801180330373.png)]
pfcount <key>
统计集合中基数的数量
pfmerge <key> <key2><key3>
把key2和key3合并为key
Geospatial 地图
geoadd <key><longitude><latitude><member>...
添加地理位置(经度,纬度,名称)
geopos <key><member>[member...]
获取指定地区的坐标值
geodist <key><member1><member2>
[m|km|ft|mi]获取两个位置之间的直线距离
georadius <key><longitude><latitude>radius m|km|ft|mi
以给定的经纬度为中心,找出给定 半径范围内的地区
- 注意:两极无法直接添加
- 有效经度从-180到180,纬度从-85.05112878到85.05112878 超出范围返回一个错误
- 单位默认为 m米 km千米 mi英里 ft英尺
redis的事务操作
multi
开始组合命令
discard
放弃命令组队
exec
执行命令
watch <key>...
监视一个或多个key,如果事务开始前这些key的值发生变化,那么这些事务就会被打断
unwatch <key>...
取消监视这些key的值
注意:
如果在组队时格式出现错误,执行时组内命令都不会执行
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kWbaHIrg-1662040787622)(image-20220802180729201.png)]
如果在组队时没有出错,在执行时出错,则出错的那条命令执行失败,其他的命令正常执行
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5Gt4uK4h-1662040787622)(image-20220802181147274.png)]
乐观锁
顾名思义,就是每次拿数据时认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下此期间别人有没有更新这个数据,可以使用版本号的机制。乐观锁适用于多读的应用场景,这样可以提高吞吐量。redis就是通过这种check-and-set机制实现事务的。
悲观锁
对于操作上锁,影响效率。
redis事务特性
-
隔离操作:事务中所有命令都会顺序的执行,不会被其他客户端所打断
-
没有隔离级别:所有的命令在未提交之前都没有被实际执行
-
非原子性:如果一条命令执行失败,其他命令正常执行,没有回滚
redis持久化
数据的备份与恢复
备份:通过SAVE
指令用于创建当前数据库的备份 该指令会在redis的安装路径下创建dump.rdb文件
创建redis备份文件也可通过指令BGSAVE
,该命令在后台执行
恢复:只需要将dump.rdb移动到redis的安装目录下启动服务即可
查看redis安装路径指令CONFIG GET dir
RDB:
(默认开启 文件名:dump.rdb)
- 通过在临时区复制数据然后赋值到磁盘,可能会丢失数据。在复制数据时占用两倍内存容量,影响性能。
AOF:
(默认关闭 文件名:appendonly.aof )aof的开启:把配置文件中的appendonly no改为yes
-
客户端的请求写命令会被append到AOF缓冲区内,
-
AOF缓冲区会根据AOF持久化策略[always everysec no]将操作sync同步到磁盘的AOF文件中
-
AOF文件大小超过重写策略或手动重写时会对AOF文件rewrite重写,压缩AOF文件容量
-
如遇AOF文件损坏,通过/usr/local/bin/redis-check-aof–fix appendonlydir/appendonly.aof进行修复
重写机制
aof采用文件追加方式,文件会越来越大为避免出现此种情况,新增了重写机制,当aof文件大小超出所设定的阈值时,redis就会启动aof文件内容压缩,只保留可以恢复数据的最小指令集。可以使用命令bgrewriteaof
重写的实现:
aof重写和rdb过程一样,就是先fork出一个子进程将文件重写(先写临时文件然后rename)
重写的触发:
redis会记录上次rewrite的文件大小,默认是aof文件比上次大一倍且文件大于64MB触发
auto-aof-rewrite-percentage
设置重写的基准值,文件达到100%,即原来文件的两倍。auto-aof-rewrite-min-size
设置文件的基准值,最小文件64MB,达到这个值开始重写缺点:
- 相比rdb更占用内存空间
- 恢复备份速度慢
- 每次写与备份同步的话,有一定的性能压力
- 存在bug造成恢复不能
-
若两者同时开启系统默认AOF
主从复制(master/slaver)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jD0cQbeK-1662040787623)(image-20220803170758450.png)]
-
读写分离
master负责写 slave负责读
-
一主多从设置
创建一个文件夹myredis,把redist.conf复制到文件夹中,同时在文件夹中创建多个配置文件*.conf
把redis.conf文件中的daemonize的no改为yes 即后台挂起
配置文件内容为
include /myredis/redis6380.conf
引入公共文件 绝对路径
pidfile /var/run/redis_6380.pid
设置进程名称
port 6380
设置监听端口号
dbfilename dump6380.rdb
设置持久化文件名称
以redis-cli -p 连接redis服务
info replication 指令可以查看当前服务的master/slave信息
通过slaveof 设置当前服务为slave (设置成功后当前服务只能 读数据 不能写)
主从复制过程
- 当slave连接到master成为slaver时,slaver主动向master发送同步数据请求
- 当master收到slaver的同步请求后,master把数据持久化到磁盘rdb文件中,把rdb文件发送到slaver
- slaver收到rdb文件进行数据的读取
注意
- 全量复制 slaver接收到数据库文件后,将其存盘并加入到内存中
- 增量复制 master每次进行写操作时都会主动同步到slaver
- 每一次slaver重新连接master都会进行全量复制
薪火相传
任何一个slaver都可以成为另外一个slaver的master
例:m1(master)是s1(slaver)的master,s1(slaver)可以称为s2(slaver)的master
反客为主
手动版: 任何一个拥有slaver的slaver都可以在自己的master挂掉后通过slaveof no one使自己成为master
自动版:
哨兵模式:
```go
//开启哨兵模式
redis-sentinel myredis/sentinel.conf
//哨兵默认监视127.0.0.1 6379 相关设置在sentinel.conf中
//sentinel monitor mymaster 127.0.0.1 6380 1
//mymaster为为监控对象起的名字,后面的127.0.0.1为master的地址,6380为master的端口号,1为至少1个哨兵同意迁移的数量
```
选择条件
1、选择性优先级靠前的 优先级在redis.conf中默认:replica-priority 100 值越小优先级越高
2、选择偏移量最大的
3、选择runid最小的从服务 每个redis实例启动后都会随机生成一个40位的runid
**注意:**当master关闭后哨兵根据规则选举出新master后,原有的master重新启动后会默认称为master的slaver
redis集群搭建
//redis*.conf配置文件修改
//redis*.conf文件内容
include /myredis/redis.conf
pidfile /var/run/redis_*[端口号].pid
port [port]
dbfilename dump*[端口号].rdb
cluster-enabled yes //打开集群模式
cluster-config-file nodes-*[端口号].conf//设置节点配置文件
cluster-node-timeout 15000 //设置节点失联时间,超过该时间(毫秒),集群自动进行主从切换
创建六个集群实例后,进入安装redis地址的src路径下执行下列命令(注意注释redis.conf中的bind 127.0.0.1和protected mode设置为no)
redis-cli --cluster create --cluster-replicas 1 172.19.68.86:6380 172.19.68.86:6381 172.19.68.86:6382 172.19.68.86:6390 172.19.68.86:6391 172.19.68.86:6392
//此处不要用127.0.0.1,用真实ip --cluster-replicas 1 以最简单的模式创建 即一个master配备一个slaver
//分配原则 各个主从服务器在不同的地址
//以集群方式连接
redis-cli -c -p 7001
//查看集群状态
cluster nodes
一个集群中一共有16384(0~16383)个slot(插槽)均匀的分布在集群的各个master中,如果master挂掉其slaver上位,如果该master重新启动则变为slaver
**注意:**如果一段插槽的主从都宕掉,redis服务是否继续依赖于cluster-require-full-coverage的值,如果是yes则整个集群都挂掉,如果为no则只是该段插槽不提供服务(数据不能读取,也不能往此段插槽写入数据)。
redis分区
分区是分割数据到多个Redis实例的处理过程,因此每个实例只保存key的一个子集。
范围分区
最简单的分区方式是按范围分区,就是映射一定范围的对象到特定的Redis实例。
比如,ID从0到10000的用户会保存到实例R0,ID从10001到 20000的用户会保存到R1,以此类推。
这种方式是可行的,并且在实际中使用,不足就是要有一个区间范围到实例的映射表。这个表要被管理,同时还需要各 种对象的映射表,通常对Redis来说并非是好的方法。
哈希分区
另外一种分区方法是hash分区。这对任何key都适用,也无需是object_name:这种形式,像下面描述的一样简单:
- 用一个hash函数将key转换为一个数字,比如使用crc32 hash函数。对key foobar执行crc32(foobar)会输出类似93024922的整数
- 对这个整数取模,将其转化为0-3之间的数字,就可以将这个整数映射到4个Redis实例中的一个了。93024922 % 4 = 2,就是说key foobar应该被存到R2实例中。注意:取模操作是取除的余数,通常在多种编程语言中用%操作符实现。
发布订阅
开启三个客户端
第一个客户端发布 发布格式PUBLISH <频道名> 消息
另外两个客户端订阅频道 订阅格式SUBSCRIBE <频道名>
**重点:**发布与订阅的实现基于客户端连接的是同一个redis服务器
管道技术
Redis是一种基于客户端-服务端模型以及请求/响应协议的TCP服务。这意味着通常情况下一个请求会遵循以下步骤:
- 客户端向服务端发送一个查询请求,并监听Socket返回,通常是以阻塞模式,等待服务端响应。
- 服务端处理命令,并将结果返回给客户端。
redis管道技术(未完)
Redis 管道技术可以在服务端未响应时,客户端可以继续向服务端发送请求,并最终一次性读取所有服务端的响应
缓存穿透
缓存击穿
缓存雪崩
分布式锁
- 基于数据库实现分布式锁
- 基于缓存(redis)实现分布锁
- 基于zookeeper实现分布式锁
ACL(access contral list)访问控制列表
acl list
acl cat