redis入门

依赖

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
package cn.sdadgz.web_springboot.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;

@Configuration
public class RedisConfig {

@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
// 连接,这逼东西得放第一位
template.setConnectionFactory(redisConnectionFactory);

// hash 用 string
template.setHashKeySerializer(RedisSerializer.string());
// key
template.setKeySerializer(RedisSerializer.string());

// 默认用json
// template.setDefaultSerializer(getRedisJsonSerializer());
template.setDefaultSerializer(RedisSerializer.json());

// 赋默认值
template.afterPropertiesSet();
return template;
}

// 废弃了,之前用自带的跑不了是因为没把连接放首位,用默认的就挺好的,我是废物
// // redis序列化用的json
// private static Jackson2JsonRedisSerializer<Object> getRedisJsonSerializer() {
// Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
// jackson2JsonRedisSerializer.setObjectMapper(getObjectMapper());
// return jackson2JsonRedisSerializer;
// }
//
// // ObjectMapper
// private static ObjectMapper getObjectMapper() {
// ObjectMapper objectMapper = new ObjectMapper();
// // 妈的不清楚ObjectMapper是个什么东西,他妈的能跑就完事了,反正我是废物
// objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
// objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL);
// return objectMapper;
// }

}

工具类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
package cn.sdadgz.web_springboot.utils;

import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.time.Duration;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;

@Component
@Slf4j
public class RedisUtil {

// 自增带过期的默认值
public static final int DEFAULT_INCR_INT_VALUE = 1;

// 默认锁失效时间
public static final int DEFAULT_LOCK_TIMEOUT = 233;

@Resource
private RedisTemplate<String, Object> redisTemplate;

// 获取键
public Set<String> getKeys(String prefix) {
return redisTemplate.keys(prefix + "*");
}

// 取值
public Object get(String key) {
return redisTemplate.opsForValue().get(key);
}

// 类型转换取值
public <T> T get(String key, Class<T> t) {
Object o = get(key);
if (t.isInstance(o)) {
return t.cast(o);
}
return null;
}

// 设置值
public void set(String key, Object value) {
redisTemplate.opsForValue().set(key, value);
}

// 定时缓存
public void set(String key, Object value, long second) {
// redisTemplate.opsForValue().set(key, value, timeout);
redisTemplate.opsForValue().set(key, value, Duration.ofSeconds(second));
}

// 自增定时数据,首次创建定时
public void setIncrExp(String key, long timeout) {
// TODO 这样写太不讲究了,回头改了
setIncrExp(key, timeout, (k) -> log.info("创建自增定期键:{},过期时间{}秒", k, timeout));
}

// 同上,创建时带一个回调函数,不清楚是不是叫回调,回头从新看一遍java吧
public void setIncrExp(String key, long timeout, Consumer<String> consumer) {
if (GeneralUtil.isNull(get(key))) {
// 不存在创建
set(key, DEFAULT_INCR_INT_VALUE, timeout);
consumer.accept(key);
return;
}

// 存在直接自增
redisTemplate.opsForValue().increment(key);
}

// 设置过期时间
public Boolean expire(String key, long second) {
return redisTemplate.expire(key, Duration.ofSeconds(second));
}

// 获取hash
public Map<Object, Object> getHash(String key) {
return redisTemplate.opsForHash().entries(key);
}

// 获取hash
public Object getHash(String key, Object index) {
return redisTemplate.opsForHash().get(key, index);
}

// 获取hash 转类型
public <T> T getHash(String key, Object index, Class<T> clazz) {
return clazz.cast(getHash(key, index));
}

// 设置hash
public void setHash(String key, Object k, Object v) {
redisTemplate.opsForHash().put(key, k, v);
}

// 自增hash
public Long addHash(String key, Object k, long step) {
return redisTemplate.opsForHash().increment(key, k, step);
}

// 获取全部
public Set<Object> getSet(String key) {
return redisTemplate.opsForSet().members(key);
}

// 成员是否存在set
public Boolean getSet(String key, Object value) {
return redisTemplate.opsForSet().isMember(key, value);
}

public void setSet(String key, Object... value) {
redisTemplate.opsForSet().add(key, value);
}

// 删除key
public Boolean delKey(String key){
return redisTemplate.delete(key);
}

// 加锁
public Boolean lock(String key) {
return lock(key, DEFAULT_LOCK_TIMEOUT);
}

// 带过期的加锁
public Boolean lock(String key, long ms) {
return redisTemplate.opsForValue().setIfAbsent(key, System.currentTimeMillis(), Duration.ofMillis(ms));
}

// 释放锁
public Boolean unlock(String key){
return delKey(key);
}

}

测试

1
2
# 测试
ping

压测

1
2
# 100并发 10w请求
redis-benchmark -c 100 -n 100000 -a [password]

基础命令

切换数据库

1
2
# 默认16个数据库使用第0个
select 3

查看数据库大小

1
2
3
4
# 看看你有多(少)键
DBSIZE
# 看看你用了多少内存 返回值used_memory_human是 `以人类可读的格式返回 Redis 的内存消耗峰值` 笑死了,别人博客复制的
info memory

增删改查

1
2
3
4
5
# 查看所有键
keys *
# 删库跑路
flushdb # 单个
FLUSHALL # 全部

键值对

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 新增键值对
set [key] [value]
setex [key] [expire_second] [value] # 带超时设置
setnx [key] [value] # 如果不存在,创建
mset [[key] [value]]... # 批量
msetnx [[key] [value]]... # setnx + mset, 具有原子性
# 获取键值对
get [key]
mget [key]...
# 获取并设置 - 返回旧值
getset [key] [value]

# 判断key存在
exists [key]
# 删除key
move [key] 1 # 1 -> 当前数据库
# 设置过期时间
expire [key] [second]
# 查看当前key剩余时间
ttl [key]
# 查看key类型
type [key]

数据类型

String(可以当Integer用?!)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
### String ###
# 追加字符串 -> 返回长度
append [key] "[String]"
# 长度
strlen [key]
# substring - 左闭右闭
getrange [key] [fromIndex] [endIndex]
# replace - 闭
setrange [key] [fromIndex] [String]

### Integer ###
# i++
incr [key]
# i--
decr [key]
# i += [x]
incrby [key] [x]
# i -= [x]
decrby [key] [x]

List

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
### 增 ###
# 头插
lpush [key] [value]
# 尾插
rpush [key] [value]
#
linsert [key] ["atter"|"before"] [value] [insertValue]

### 删 ###
# 头删 -> 删的元素
lpop [key]
# 尾删 -> 被删元素
rpop [key]
# 删指定元素指定个数
lrem [key] [count] [value]
# substring - 左闭右闭
ltrim [key] [startIndex] [endIndex]

### 改 ###
#
lset [key] [index] [value]
# 他的屁股弄他头上
rpoplpush [fromList] [toList]

### 查 ###
# index -> element
lindex [key] [index]
# range - 左闭右闭
lrange [key] [fromIndex] [endIndex]
# length
llen [key]

Set

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
### 增 ###
#
sadd [key] [value]

### 删 ###
#
srem [key] [value]
# 随机删除
spop [key]

### 改 ###
# 移动到另一个集合
smove [fromKey] [toKey] [value]

### 查 ###
# 查全
smembers [key]
# 随机抽
srandmember [key]
srandmember [key] [count]
# exists
sismember [key] [value]
# length
scard [key]
集合间关系
1
2
3
4
5
6
# A - B1 - B2
sdiff [baseKey] [otherKey]...
# A ∩ B
sinter [key]...
# A ∪ B
sunion [key]...

hash(key - Map<String, String>)

举一反三吧我不想记了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 
hset [mapKey] [key] [value]
# 批量增
hmset [mapKey] [[key] [value]]...

#
hdel [mapKey] [key]...

#
hget [mapKey] [key]
# 批量查
hmget [mapKey] [key]...
# 查全
hgetall [mapKey]
# exists
hexists [mapKey] [key]
# 多少个键值对
hlen [mapKey]

# 举一反三
hincrby
hsetnx
...

zset(有序集合)

1
2
3
4
5
6
7
8
9
10
11
12
13
# 
zadd [[key] [score] [value]]...

# 查 - 用到再差,笔记不全
zrangebyscore [key] [min] [max]

# 举一反三
zrange
zrem
zcard
zrevrange
zcount
...

geospatial地理位置

底层使用zset,可以用zset命令

1
2
3
4
5
6
7
8
9
10
11
# 添加
geoadd [key] [经度] [纬度] [value]
# 获取经纬度
geopos [key] [value]...
# 距离
geodist [key] [value] [otherValue] [单位]
# 附近的人 - 用到在查
georadius
georadiusbymember
geohash
...

hyperloglog计数,有误差

1
2
3
4
5
6
7
# 
pfadd [key] [value]...

# length
pfcount [key]
# 并集
pfmerge [resultKey] [key]...

bitmap统计

1
2
3
4
5
6
7
# 
setbit [key] [index] [0|1]

#
getbit [key] [index]
# count
bitcount [key]

事务

1
2
3
4
5
6
# 启动事务
multi
# 划句号
exec
# 跳车
discard

乐观锁watch

1
watch [key]

springboot

springboot发现你装redis了,所以就会用你的新欢代替他的默认缓存,他真的,我哭死

我原以为那是mybatis的二级缓存,没想到那个缓存是springboot的缓存,妈的认错人了

1
2
3
4
opsForValue // 操作字符串
opsForList // list
opsForSet // set
...
1
2
3
4
// 直接操控
RedisConnection connection = redisTemplate.getConnectionFactory().getConnection();
connection.flushDb();
connection.flushAll();

消息队列?经典白学

我赌你的枪里没有子弹,如果我输了,我翻回去再看

1
2
3
subscribe
publist
...

集群经典白学

info replication查看当前库信息

1
2
3
4
5
6
7
# 找哥哥。用命令连接有点不讲究啊
slaveof [ip] [port]

# 配置文件写
replicaof [ip] [port]
# saber的master有密码
masterauth [password]

哨兵

1
2
3
4
5
6
7
8
9
# 配置文件.conf 1表示主机挂了投票选主机 从机自动,我哭死
sentinel monitor [name] [主机ip] [主机port] [1]
# 相信不加注释我也能看明白
port 26379
dir /tmp
sentinel auth-pass [name] [password]
# 30秒不接就是失踪
sentinel down-after-milliseconds [name] 30000
# 这个视频狗der都没说配置文件,估计是他也不会