Redis
狂神说Redis
目录
一、String 类型
二、List 类型
三、Set(集合)
四、Hash(哈希)
五、Zset(有序集合)
六、Geospatial
七、hyperloglog
八、Bitmap
九、事务
十、Jedis
Jedis操作redis
springboot 整合 redis
十一、Redis.conf
十二、Reddis发布与订阅
十三、搭建集群
十四、哨兵模式
一、String 类型
命令 | 描述 |
---|---|
append key value | 用指定value追加到key后面 |
incr/decr key | key 自增/自减1 |
incrby/decrby key n | key 自增/自减 n |
incrbyfloat key n | 为数值加上浮点数 |
strlen key | 计算key的长度 |
getrange key start end | 获得 start 和 end 之间的值 |
setrange key offset value | 从指定 offset 开始将值替换成 value |
getset key value | 先返回当前key的值,然后重新将值设置成value |
mset [k,v ...] | 批量设置值 |
mget [k1,k2 ...] | 批量获取值 |
setex key seconds value | 设置键值有效时长为seconds |
setnx key value | 查询表中是否有值,没有值再插入 |
msetnx [k1,v1 ...] | 批量设置值,没有该键则插入,多个都成功,才成功插入,一个失败,则其他的也不插入 |
psetex key milliseconds value | 设置键值的有效时长,以毫秒为单位 |
二、List 类型
List类型,实际上就像是链表一样Node,所有命令前面都带一个
l
命令 | 描述 |
---|---|
lpush/rpush key [value ...] | 创建一个List 并从左/右插入value |
lrange key start end | 查看一个List中start->end 数据 |
lpushx/lpushx key [value ...] | 向已存在的list中插入值 |
linsert key before/after pivot value | 在已存在的pivot的前、后插入值 |
llen key | 查看list长度 |
lindex key index | 查看指定索引的值 |
lset key index value | 修改索引index的值为value |
lpop/rpop key | 从左边/右边弹出一个值 |
rpoplpush source destination | 从source右边弹出一个值,加入到destination的右边 |
ltrim key start end | 截取 start -> end 注意是从左往右数 |
blpop/brpop key timeout | list中有值直接弹出,没有则等待timeout,直到时间结束 |
lrem key conunt value | 删除 n 个 value 值 |
brpoplpush source destination timeout | 和 brpoplpush 功能一样,只是增加了等待时间 |
应用
- 消息排队!消息队列(Lpush Rpop),栈(Lpush Lpop)
三、Set(集合)
set集合中没有重复元素
命令 | 描述 |
---|---|
sadd key [member ...] | 添加一个member 也是创建一个set |
smembers key | 查看set集合元素 |
sismember key member | 判断 key 是否有member |
spop key | 随机弹出一个值 |
scard key | 查看set中的元素个数 |
srem key member | 删除指定的member |
srandmember key count | 随机抽取count个member |
sunion [key ...] | 两个set取并集 |
sunionstore destination [key ...] | 将交集存入destination |
sdiff [key ...] | 取差集 |
sdiffstore destination [key ...] | 取差集存入destination |
sinter [key ...] | 取交集 |
sinterstore destination [key ...] | 取交集存入destination |
四、Hash(哈希)
Redis hash 是一个string类型的field和value的映射表,hash特别适合用于存储对象。
Set就是一种简化的Hash,只变动key,而value使用默认值填充。可以将一个Hash表作为一个对象进行存储,表中存放对象的信息。
命令 | 描述 |
---|---|
hset key field value | 创建一个 hashmap 并添加一个字段 |
hget key field | 获取field的值 |
hset key [field value ...] | 添加多个字段的值 |
hmget key [field ...] | 获取多个field字段的值 |
hgetall key | 获取key的所有值 |
hexists key field value | 查询是否存在这个字段值 |
hsetnx key field value | 设置字段值,若存在该字段则无法设置 |
hkeys key | 获取所有的字段 |
hvals key | 获取所有字段值 |
hlen key | 获取key中的字段个数 |
hdel key [field ...] | 删除字段值 |
hincrby key field n | 字段自增n值 n可以为负数 |
- 多用于用户信息存储
hmset user:1 name "anyi" age 20
五、Zset(有序集合)
不同的是每个元素都会关联一个double类型的分数(score)。redis正是通过分数来为集合中的成员进行从小到大的排序。
score相同:按字典顺序排序
有序集合的成员是唯一的,但分数(score)却可以重复。
命令 | 描述 |
---|---|
zadd key socre member | 添加一个member带上socre 用来排序的权重 |
zcard key | 查看key中的个数 |
zconnt key min max | key中socre的值 min -> max 的个数 |
zrange key min max | 列出key中 min -> max 中从低到高member |
zrevrange key min max | 列出key中 min -> max 中从高到低member |
zsocre key member | 查看某个member的权重 |
zlexconut key min max | 查看两个成员间成员的个数 |
zremrange key min max | 删除区间内的所有成员 |
zrankrange key min max | 删除排行之间的所有成员 |
- set排序 存储班级成绩表 工资表排序!
- 普通消息,1.重要消息 2.带权重进行判断
- 排行榜应用实现,取Top N测试
六、Geospatial
存储位置信息,可以很方便计算和管理位置信息
- geoadd : 添加位置信息 geoadd china:city 经度 维度 名称
127.0.0.1:6380> geoadd china:city 116.40 39.90 beijing
(integer) 1
127.0.0.1:6380> geoadd china:city 108.96 34.28 xian
(integer) 1
127.0.0.1:6380> geoadd china:city 114.109 22.54 shenzhen
(integer) 1
127.0.0.1:6380> geoadd china:city 115.88 28.67 nanchang
(integer) 1
127.0.0.1:6380> geoadd china:city 120.16 30.31 hangzhou
(integer) 1
127.0.0.1:6380> geoadd china:city 108.43 37.81 henan
(integer) 1
- geopos : 获取某城市的经纬度
127.0.0.1:6380> geopos china:city beijing
1) "116.39999896287918091"
2) "39.90000009167092543"
- geodist : 获取两个位置距离 可以指定单位
127.0.0.1:6380> geodist china:city beijing xian km
"908.4647"
- georadius : 以某一经纬度为中心,该radius内的城市
127.0.0.1:6380> georadius china:city 110 35 1000 km # 110 35 1000 km 以内的城市 返回城市名
1) "xian"
2) "henan"
3) "beijing"
127.0.0.1:6380> georadius china:city 110 35 5000 km withcoord # 返回 经纬度
1) 1) "xian"
2) 1) "108.96000176668167114"
2) "34.28000112885764139"
2) 1) "henan"
2) 1) "108.43000262975692749"
2) "37.81000082918030358"
3) 1) "shenzhen"
2) 1) "114.10900086164474487"
2) "22.53999903789756587"
4) 1) "nanchang"
2) 1) "115.87999910116195679"
2) "28.66999910629679249"
5) 1) "hangzhou"
2) 1) "120.1600000262260437"
2) "30.30999918248532765"
6) 1) "beijing"
2) 1) "116.39999896287918091"
2) "39.90000009167092543"
- genradiusbymember : 以城市为中心 周围的城市
127.0.0.1:6380> georadiusbymember china:city beijing 1000 km withcoord # 返回北京 附近1000米的城市
1) 1) "beijing"
2) 1) "116.39999896287918091"
2) "39.90000009167092543"
2) 1) "xian"
2) 1) "108.96000176668167114"
2) "34.28000112885764139"
3) 1) "henan"
2) 1) "108.43000262975692749"
2) "37.81000082918030358"
七、hyperloglog
用来计数的一种数据结构,会产生0.82% 的误差
命令 | 描述 |
---|---|
pfadd key [element ...] | 添加element到key中 |
pfcount key | 计算集合中的个数 |
pfmerge destkey [key ...] | 合并多个集合,取并集 |
127.0.0.1:6380> pfadd myhyper a b c d e f
(integer) 1
127.0.0.1:6380> pfadd myhyper2 h i j k l m n
(integer) 1
127.0.0.1:6380> pfadd myhyper2 a b c
(integer) 1
127.0.0.1:6380> pfcount myhyper
(integer) 6
127.0.0.1:6380> pfcount myhyper2
(integer) 10
127.0.0.1:6380> pfmerge myhyper3 myhyper myhyper2
OK
127.0.0.1:6380> pfcount myhyper3
(integer) 13
八、Bitmap
使用位存储,信息状态只有 0 和 1 适用于打卡等…
Bitmap是一串连续的2进制数字(0或1),每一位所在的位置为偏移(offset),在bitmap上可执行AND,OR,XOR,NOT以及其它位操作。
命令 | 描述 |
---|---|
setbit key offset 0/1 | 在offset位置处设置 0/1 |
getbit key offset | 获取offset位置的值 |
bitcount key | 获取 key 中 1 的数量 |
127.0.0.1:6380> setbit card 1 0 # 在 1 位置 添加 0
(integer) 0
127.0.0.1:6380> setbit card 2 1
(integer) 0
127.0.0.1:6380> setbit card 3 1
(integer) 0
127.0.0.1:6380> setbit card 4 1
(integer) 0
127.0.0.1:6380> setbit card 5 0
(integer) 0
127.0.0.1:6380> bitcount card # 计算出 card 中 1 的值
(integer) 3
九、事务
悲观锁:
认为什么操作都会发生错误,什么操作都加锁!
乐观锁:
认为什么操作都不会发生错误,都一定以要加锁,选择性
- 在更新数据的时候进行判断,是否有人修改过数据
- 获取version
- 比对事务前 version 和 要执行时候的 version 如果发生变化,则事务执行失败
- 使用 watch key 来加锁
事务基本实现:
multi - 开启事务
exec 执行事务
正常执行
127.0.0.1:6380> set money 1000
OK
127.0.0.1:6380> set out 0
OK
127.0.0.1:6380> multi # 开启事务
OK
127.0.0.1:6380> decrby money 10 # 具体操作
QUEUED
127.0.0.1:6380> incrby out 10
QUEUED
127.0.0.1:6380> exec # 执行事务
1) (integer) 990
2) (integer) 10
执行语法错误,执行失败
127.0.0.1:6380> multi # 开启事务
OK
127.0.0.1:6380> set k1 v1
QUEUED
127.0.0.1:6380> set k2 v2
QUEUED
127.0.0.1:6380> getset k2 # 语法错误
(error) ERR wrong number of arguments for 'getset' command
127.0.0.1:6380> set k3 v3
QUEUED
127.0.0.1:6380> exec # 执行事务
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6380> get k1 # 判断事务中的单个操作是否执行
(nil)
watch && unwatch 实现乐观锁
线程1
127.0.0.1:6380> watch money # 监控 money
OK
127.0.0.1:6380> multi # 开启事务
OK
127.0.0.1:6380> decrby money 20
QUEUED
127.0.0.1:6380> incrby out 20
QUEUED
127.0.0.1:6380> exec # 执行事务,这里会对监视的money和事务开始时的money对比
(nil) # 值被修改过,所以执行失败
127.0.0.1:6380> unwatch
OK
线程2
127.0.0.1:6380> set money 100 # 在事务提交之前,修改money
OK
十、Jedis
Jedis操作redis
- 引入依赖
<!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>4.2.1</version>
</dependency>
测试代码:
package com.zcc.test;
import redis.clients.jedis.Jedis;
/**
* @author 安逸i
* @version 1.0
*/
public class TestJedis {
public static void main(String[] args) {
Jedis jedis = new Jedis("127.0.0.1",6379);
jedis.set("name","anyi");
System.out.println(jedis.get("name"));
jedis.zadd("users",2,"anyi");
jedis.zadd("users",1,"wangwu");
jedis.zadd("users",3,"liliu");
System.out.println(jedis.zrange("users", 0, -1));
}
}
所有方法和redis中的命令一致
springboot 整合 redis
> jedis:采用的直连,多个线程操作的话,是不安全的。如果要避免不安全,使用jedis pool连接池!更像BIO模式lettuce:采用netty,实例可以在多个线程中共享,不存在线程不安全的情况!可以减少线程数据了,更像NIO模式现在 2.6.6 版本使用的都是 lettuce 采用 netty- 引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
- 配置redis链接
查看配置 RedisAutoConfiguration
配置都绑定在 RedisProperties.class
spring:
redis:
host: 127.0.0.1
port: 3679
- 测试代码
@Test
void contextLoads() {
// redisTemplate.opsForValue() 对应 String
// redisTemplate.opsForList() 对应 List
// redisTemplate.opsForHash() 对应 Hash
// redisTemplate.opsForZSet() 对应 Zset 有序集合
// redisTemplate.opsForSet() 对应 set 集合
// redisTemplate.opsForGeo 对应 geospatial 地图
ValueOperations<Object, Object> str = redisTemplate.opsForValue();
str.set("name","anyi");
System.out.println(str.get("name"));
str.append("name"," hello");
System.out.println(str.get("name"));
}
对应的方法和redis命令一致,一些基本操作可以通过 redisTemplate 直接操作 如 flushdb
- 定制 RedisTemplate 模板
@Configuration
public class RedisConfig {
@Bean("myRedisTemplate")
public RedisTemplate<String ,Object> redisTemplate(RedisConnectionFactory redisConnectionFactory){
// 定义一个自己的 String object 类型
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
// 配置Json序列化
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
// String 序列化
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
// 设置 String key 以 stringRedisSerializer 解析
redisTemplate.setKeySerializer(stringRedisSerializer);
// 设置 Hash key 以 stringRedisSerializer 解析
redisTemplate.setHashKeySerializer(stringRedisSerializer);
// String value 以Json 解析
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
// Hash value 以 Json 解析
redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
return redisTemplate;
}
}
-
Redis工具类
因为工具类添加了 @Component ,所以使用时可以自动注入
十一、Redis.conf
容量大小
# 1k => 1000 bytes
# 1kb => 1024 bytes
# 1m => 1000000 bytes
# 1mb => 1024*1024 bytes
# 1g => 1000000000 bytes
# 1gb => 1024*1024*1024 bytes
可以使用 include 组合多个配置问题
# include /path/to/local.conf
# include /path/to/other.conf
网路配置
bind 127.0.0.1 # 配置主机
protected-mode yes #是否后台运行,默认是no
port 6380 # 配置端口
GENERAL 日志输出文件
loglevel notice # 日志级别
四大级别:
debug
verbose
notice
warning
logfile "" # 日志名称
RDB 配置
save 900 1 # 900秒 操作1次保存
save 300 10 # 300秒 操作10保存
save 60 10000
stop-writes-on-bgsave-error yes # 发生错误继续工作
rdbcompression yes # 压缩rdb文件
rdbchecksum yes # 检查rdb文件
dbfilename dump.rdb # rdb文件的默认名字
dir ./ # rdb文件的保存位置
设置密码
#requirepass "" # 默认没有密码的
# 可以通过 config set requirepass "123456" 设置
客户端链接相关
maxclients 10000 最大客户端数量
maxmemory <bytes> 最大内存限制
maxmemory-policy noeviction # 内存达到限制值的处理策略
# redis 中的默认的过期策略是 volatile-lru
**maxmemory-policy 六种方式: **
**1、volatile-lru:**只对设置了过期时间的key进行LRU(默认值)
2、allkeys-lru : 删除lru算法的key
**3、volatile-random:**随机删除即将过期key
**4、allkeys-random:**随机删除
5、volatile-ttl : 删除即将过期的
6、noeviction : 永不过期,返回错误
AOF 配置
appendonly no # 默认不开启AOF
appendfilename "appendonly.aof" # 默认文件名
# 数据同步策略
# appendfsync always 每次修改进行同步
appendfsync everysec # 每秒进行同步
# appendfsync no # 不同步
十二、Reddis发布与订阅
命令
命令 | 描述 |
---|---|
PSUBSCRIBE pattern [pattern..] | 订阅一个或多个符合给定模式的频道。 |
PUNSUBSCRIBE pattern [pattern..] | 退订一个或多个符合给定模式的频道 |
PUBSUB subcommand [argument[argument]] | 查看订阅与发布系统状态。 |
PUBLISH channel message | 向指定频道发布消息 |
SUBSCRIBE channel [channel..] | 订阅给定的一个或多个频道。 |
SUBSCRIBE channel [channel..] | 退订一个或多个频道 |
实现代码
发送端:
127.0.0.1:6380> publish anyi "i love you" # 发送信息
(integer) 1
127.0.0.1:6380> publish anyi "wo ai xie dai ma"
(integer) 1
接收端:
[root@anyi bin]# redis-cli -p 6380
127.0.0.1:6380> subscribe anyi
Reading messages... (press Ctrl-C to quit)
1) "subscribe" # 订阅
2) "anyi" # 订阅的频道名
3) (integer) 1
1) "message"
2) "anyi" # 频道
3) "i love you" #
1) "message
2) "anyi"
3) "wo ai xie dai ma"
十三、搭建集群
由于服务器问题,这里采用单机多服务
- 创建多个配置文件
- 端口号
- pid文件名
- 日志文件名
- rdb文件名
- 根据配置文件启动多个 redis 服务
127.0.0.1:6380> info replication # 查看redis信息
# Replication
role:master # 默认就是 master
connected_slaves:0
master_replid:ea05a55d8be8b01f7506b4c4ba3b4e3c62352311
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:0
second_repl_offset:-1
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
127.0.0.1:6380> info replication
# Replication
role:master
connected_slaves:2
slave0:ip=127.0.0.1,port=6381,state=online,offset=84,lag=1
slave1:ip=127.0.0.1,port=6382,state=online,offset=84,lag=1
master_replid:2a7b2829d475491187c9d71a10c4696faa4dd2df
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:84
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:84
- 集群关联
127.0.0.1:6381> slaveof 127.0.0.1 6380 # 设置主机是 127.0.0.1 6380
OK
127.0.0.1:6381> info replication
# Replication
role:slave # 自己变成了从机
master_host:127.0.0.1
master_port:6380
master_link_status:up
master_last_io_seconds_ago:8
master_sync_in_progress:0
slave_repl_offset:56
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:2a7b2829d475491187c9d71a10c4696faa4dd2df
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:56
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:56
- 主机负责写,从机负责读
# 主机 6380
127.0.0.1:6380> set k1 v1
OK
# 从机 6381
127.0.0.1:6381> get k1
"v1"
# 从机 6382
127.0.0.1:6382> get k1
"v1"
- 使用规则
1. 当主机断电宕机后,默认情况下从机的角色不会发生变化 ,集群中只是失去了写操作,当主机恢复以后,又会连接上从机恢复原状。
2. 当从机断电宕机后,若不是使用配置文件配置的从机,再次启动后作为主机是无法获取之前主机的数据的,若此时重新配置称为从机,又可以获取到主机的所有数据。这里就要提到一个同步原理。
3. 第二条中提到,默认情况下,主机故障后,不会出现新的主机,有两种方式可以产生新的主机:
从机手动执行命令slaveof no one,这样执行以后从机会独立出来成为一个主机
使用哨兵模式(自动选举)
十四、哨兵模式
当主服务器宕机后,需要手动把一台从服务器切换为主服务器,这就需要人工干预,费事费力,还会造成一段时间内服务不可用。这不是一种推荐的方式,更多时候,我们优先考虑哨兵模式**。
- 配置核心文件
sentinel.conf
vim sentinel.conf
# 内容 / 这里是最简单的配置
sentinel monitor mymaster 127.0.0.1 6379 1
- 通过
redis-sentinel xxx/sentinel.conf
启动哨兵
redis-sentinel sentinel.conf
- 可以看到以下信息
4. 但主机宕机后,自动投票选举出主机 (投票算法)
5. 当主机再次回来,只能作为从机了