Redis 101 - 基础操作
文章目录
初学Redis的笔记。
--
Redis(Remote Dictionary Server)是一个使用ANSI C编写的开源、支持网络、基于内存、分布式、可选持久性的键值对存储数据库。
相关网址
官网:https://redis.io/
W3cschool:https://www.w3cschool.cn/redis/
windows下载:https://github.com/MicrosoftArchive/redis/releases。
Mac用brew install即可。
Linux wget https://github.com/redis/redis/archive/版本号.tar.gz
再make
, make install
Docker:https://hub.docker.com/_/redis
1docker run --name some-redis -d redis
2
3# 每60s保存一个快照如果至少有一次写操作
4# 如果开启持久化,数据存储在VOLUME /data
5# 可与--volumes-from some-volume-container 或者 -v /docker/host/dir:/data 一起使用
6docker run --name some-redis -d redis redis-server --save 60 1 --loglevel warning
7
8# 连接客户端
9docker run -it --network some-network --rm redis redis-cli -h some-redis
10
11# 使用自己的conf文件运行 Dockerfile:
12FROM redis
13COPY redis.conf /usr/local/etc/redis/redis.conf
14CMD [ "redis-server", "/usr/local/etc/redis/redis.conf" ]
15
16# 或者:
17docker run -v /myredis/conf:/usr/local/etc/redis --name myredis redis redis-server /usr/local/etc/redis/redis.conf
常用命令
1# 启动
2redis-server [配置文件路径]
3
4# 客户端启动
5redis-cli -p 6379
6
7# 查看Redis进程
8ps -ef | grep redis
9
10# 关闭
11shutdown
1redis-benchmark -c 10 -n 100000
序号 | 选项 | 描述 | 默认值 |
---|---|---|---|
1 | -h | 指定服务器主机名 | 127.0.0.1 |
2 | -p | 指定服务器端口 | 6379 |
3 | -s | 指定服务器 socket | |
4 | -c | 指定并发连接数 | 50 |
5 | -n | 指定请求数 | 10000 |
6 | -d | 以字节的形式指定 SET/GET 值的数据大小 | 2 |
7 | -k | 1=keep alive 0=reconnect | 1 |
8 | -r | SET/GET/INCR 使用随机 key, SADD 使用随机值 | |
9 | -P | 通过管道传输 |
1 |
10 | -q | 强制退出 redis。仅显示 query/sec 值 | |
11 | --csv | 以 CSV 格式输出 | |
12 | *-l*(L 的小写字母) | 生成循环,永久执行测试 | |
13 | -t | 仅运行以逗号分隔的测试命令列表。 | |
14 | *-I*(i 的大写字母) | Idle 模式。仅打开 N 个 idle 连接并等待。 |
配置文件中:databases 16
。默认有16个数据库,默认使用第0个。
1select 3 # 切换到第三个数据库
2DBSIZE # 查看数据库大小
3keys * # 查看所有key
4flushdb # 清空当前数据库
5flushall # 清空所有
Redis是单线程的。Redis基于内存,CPU不是瓶颈,瓶颈是机器内存和网络带宽。将所有数据放在内存中,单线程操作效率最高。(?)
1# 命令示例:
2keys *
3set name eisenki
4EXISTS name # key name 是否存在 存在则返回1
5move name 1 # 将当前数据库中的name 键值对移到数据库1
6expire name 10 # key name 10秒后过期
7ttl name # 查看name还有几秒钟的寿命
8type name # 查看key name的类型
String
1# append key value 将value追加到key原来的值之后
2127.0.0.1:6379> get name
3"eisenki"
4127.0.0.1:6379> append name hhh
5(integer) 10
6127.0.0.1:6379> get name
7"eisenkihhh"
8
9# getrange 获取 0到5,0到-1
10127.0.0.1:6379> getrange name 0 5
11"eisenk"
12127.0.0.1:6379> getrange name 0 -1
13"eisenkihhh"
14
15# setrange
16127.0.0.1:6379> setrange name 2 abc
17(integer) 10
18127.0.0.1:6379> get name
19"eiabcjihhh"
20
21# setex:set with expire
22# setnx: set if not exist
23# mset / mget / msetnx
24# getset
25
26# 使key的值+1 或-1
27incr key
28decr key
29
30# 使key的值+n 或者 -n
31incrby key n
32decrby key n
List
1# list
2-
3#lpush rpush lrange lpop lindex llen
4127.0.0.1:6379> lpush list one
5(integer) 1
6127.0.0.1:6379> lpush list two
7(integer) 2
8127.0.0.1:6379> lpush list three
9(integer) 3
10127.0.0.1:6379> rpush list four
11(integer) 4
12127.0.0.1:6379> lrange list 0 -1
131) "three"
142) "two"
153) "one"
164) "four"
17127.0.0.1:6379> lpop list # 左pop
18"three"
19127.0.0.1:6379> rpop list # 右pop
20"four"
21127.0.0.1:6379> lindex list 0
22"two"
23127.0.0.1:6379> lindex list 1
24"one"
25127.0.0.1:6379> LLEN list
26(integer) 2
27
28# lrem key count element 删除元素
29# ltrim 截取[i, j]
30127.0.0.1:6379> lrange list 0 -1
311) "three"
322) "one"
333) "one"
344) "two"
355) "one"
36127.0.0.1:6379> lrem list 5 one
37(integer) 3
38127.0.0.1:6379> lrange list 0 -1
391) "three"
402) "two"
41127.0.0.1:6379> LTRIM list 0 0
42OK
43127.0.0.1:6379> lrange list 0 -1
441) "three"
45
46# rpoplpush source destination 把source的rpop出来的元素lpush到destination
47127.0.0.1:6379> LRANGE list1 0 -1
481) "one"
492) "two"
503) "three"
51127.0.0.1:6379> RPOPLPUSH list1 list2
52"three"
53127.0.0.1:6379> LRANGE list2 0 -1
541) "three"
55127.0.0.1:6379> lrange list1 0 -1
561) "one"
572) "two"
58
59# lset key index element 将指定index的值更新为element
60# linsert key before|after pivot element
61127.0.0.1:6379> LRANGE yalist 0 -1
621) "one"
632) "two"
643) "two"
654) "two"
665) "three"
67127.0.0.1:6379> linsert yalist before two here1
68(integer) 6
69127.0.0.1:6379> LRANGE yalist 0 -1
701) "one"
712) "here1"
723) "two"
734) "two"
745) "two"
756) "three"
76127.0.0.1:6379> linsert yalist after two here2
77(integer) 7
78127.0.0.1:6379> LRANGE yalist 0 -1
791) "one"
802) "here1"
813) "two"
824) "here2"
835) "two"
846) "two"
857) "three"
Set
1#Set
2-
3# sadd key member [member ...]
4# srem key member [member ...]
5# sismember key member
6127.0.0.1:6379> sadd aset eisen husky
7(integer) 2
8127.0.0.1:6379> smembers aset # 获取成员
91) "husky"
102) "eisen"
11127.0.0.1:6379> scard aset # 获取成员个数
12(integer) 2
13
14# srandmember key [count] # 随机抽出指定数目的成员
15127.0.0.1:6379> SMEMBERS aset
161) "jack"
172) "lucy"
183) "lisa"
194) "eisen"
20127.0.0.1:6379> SRANDMEMBER aset 4
211) "jack"
222) "lucy"
233) "lisa"
244) "eisen"
25127.0.0.1:6379> SRANDMEMBER aset
26"lisa"
27127.0.0.1:6379> SRANDMEMBER aset
28"eisen"
29
30# spop 随机弹出
31# smove source destination member 把source里的member移动到destination
32
33# sdiff key1 [key ...] 找出key1与其他不同的成员
34# sinter 取交集
35# sunion 取并集
36127.0.0.1:6379> SMEMBERS aset
371) "jack" 2) "lucy" 3) "lisa" 4) "eisen"
38127.0.0.1:6379> SMEMBERS bset
391) "eric" 2) "jack" 3) "lucy" 4) "lisa" 5) "hugo"
40127.0.0.1:6379> SMEMBERS cset
411) "hugo" 2) "jack" 3) "lucy" 4) "christ"
42127.0.0.1:6379> sdiff aset bset
431) "eisen"
44127.0.0.1:6379> sdiff bset aset
451) "hugo"
462) "eric"
47127.0.0.1:6379> sdiff bset aset cset
481) "eric"
49
50127.0.0.1:6379> SINTER aset bset
511) "jack"
522) "lucy"
533) "lisa"
54127.0.0.1:6379> sunion aset bset cset
551) "hugo" 2) "eric" 3) "jack" 4) "lucy" 5) "lisa" 6) "christ" 7) "eisen"
Hash
1# Hash
2-
3# hset key field value [field value ....]
4# hget key field
5# hmset / hmget
6# hgetall key
7# hdel key field [field ...] # 删除
8# hlen key
9# hexists key field
10# hkeys key # 获取所有field
11# hvals key # 获取所有value
12# hincrby key field increment
13# hsetnx key field value
Zset
1#Zset
2# 每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。
3-
4# zadd key [NX|XX] [GT|LT] [CH] [INCR] score member [score member ...]
5# zrange key min max [BYSCORE|BYLEX] [REV] [LIMIT offset count] [WITHSCORES]
6# zrandmember key [count [WITHSCORES]]
7# zrangebyscore key min max [WITHSCORES] [LIMIT offset count]
8# 正负无穷 +inf -inf
9# zrem key member [member ...]
10# zrevrange key start stop [WITHSCORES] 降序排列
11# zcount key min max
Geospatial
可以使用Zset的命令来操作geospatial。
1# 添加地理位置 有效的经度-180到180;有效的纬度 -85.05112878到85.05112878
2# geoadd key [NX|XX] [CH] longitude latitude member [longitude latitude member ...]
3
4# 获取地理位置
5# geopos key member [member ...]
6
7# 返回两个位置之间的距离 ft英尺 ; mi英里
8# geodist key member1 member2 [m|km|ft|mi]
9
10# 以给定的经纬度为中心查找某一半径内的元素
11# GEORADIUS key longitude latitude radius <M | KM | FT | MI>
12# [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count [ANY]] [ASC | DESC]
13# [STORE key | STOREDIST key]
14
15# 以给定元素为中心,查找某一半径内的元素
16# GEORADIUSBYMEMBER key member radius <M | KM | FT | MI> [WITHCOORD]
17# [WITHDIST] [WITHHASH] [COUNT count [ANY]] [ASC | DESC] [STORE key
18# | STOREDIST key]
19
20# 具有相似前缀的字符串位于附近,但反之则不然,具有不同前缀的字符串也可能位于附近。
21# geohash key member [member ...]
Hyperloglog
HyperLogLog 是一种概率数据结构,用于估计集合的基数。Redis HyperLogLog 实现最多使用 12 KB,并提供 0.81% 的标准错误。不再需要使用与计数的项目数成正比的内存量,而是可以使用恒定量的内存;最坏情况下为 12KB。
1# 添加元素
2# pfadd key element [element ...]
3# 统计
4# pfcount key [key ...]
5# 合并
6# pfmerge destkey sourcekey [sourcekey ...]
7127.0.0.1:6379> pfadd akey one two three four five five six seven 7 7 7
8(integer) 1
9127.0.0.1:6379> pfcount akey
10(integer) 8
11127.0.0.1:6379> pfadd bkey one two 9 10 11 12 13
12(integer) 1
13127.0.0.1:6379> pfmerge ckey akey bkey
14OK
15127.0.0.1:6379> pfcount ckey
16(integer) 13
Bitmaps
1# 设置或清除key中存储的字符串值中偏移处的位。
2setbit key offset value
3getbit key offset
4# 计算有几个1
5bitcount key [start end]
事务
Redis 事务允许在一个步骤中执行一组命令,它们以命令 MULTI、EXEC、DISCARD 和 WATCH 为中心,并且带有以下两个重要的保证:
- 事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
- 事务是一个原子操作:事务中的命令要么全部被执行,要么全部都不执行。
一个事务从开始到执行会经历以下三个阶段:
- 开始事务。
- 命令入队。
- 执行事务。
1# multi开启事务,discard放弃事务,exec执行事务
2# watch实现乐观锁,监视某一key在事务执行之前是否变动,如果没有变动,事务执行成功。如果变动了,事务执行失败。
3# unwatch清空某一事务之前的锁,如果已经exec或者discard了,就不需要手动unwatch了。
使用Java操作Redis
Jedis
Redis官网推荐的Java操作Redis的工具。
导包:https://mvnrepository.com/artifact/redis.clients/jedis
使用docker ps
命令查看端口映射,0.0.0.0:32768->6379/tcp
,因此通过32768端口即可连接,测试:
1@Test
2public void Testping() {
3 Jedis jedis = new Jedis("127.0.0.1", 32768);
4 System.out.println(jedis.ping());
5}
各种api参考文档:https://www.javadoc.io/doc/redis.clients/jedis/latest/index.html
SpringBoot
创建项目时添加依赖Spring Data Redis
可以在org.springframework.boot.autoconfigure.data.redis包中看到相关源码。
在application.properites中配置spring.data.redis.host等内容。然后就能测试了,使用RedisTemplate类的实例就能进行各种操作了。
1@SpringBootTest
2class RedisSpringApplicationTests {
3 @Autowired
4 private RedisTemplate redisTemplate;
5 @Test
6 void contextLoads() throws JsonProcessingException {
7 User user = new User("Eisen", 100);
8 String jsonUser = new ObjectMapper().writeValueAsString(user);
9 redisTemplate.opsForValue().set("user", jsonUser);
10 System.out.println(redisTemplate.opsForValue().get("user"));
11 }
12}
需要注意直接传递对象会报序列化的错误。可以自己配置config/RedisConfig.java,或者自己写工具类utils/RedisUtil.java。
配置文件
Redis的启动时,必须有一个配置文件与之相匹配。redis-server [配置文件路径]
。
1config get * # 得到所有配置
2config set # 设置命令
主要分为以下部分:
- includes:一个配置文件,可以导入其他配置文件。
- modules:启动时加载的模块。
- network:网络相关配置。
- TLS/SSL:安全传输相关模块。
- general:通用配置相关。
- snapshotting:快照配置相关。
- replication:主从复制相关配置项。
- keys tracking:键的追踪相关配置项。
- security:安全相关配置。
- clients:客户端相关配置。
- memory management:内存管理相关配置项。
- lazy freeing:延迟释放配置项。
- threaded I/O:多线程I/O相关配置项。
- kernel OOM control:Linux内核防止内存占用过大配置选项。
- append only mode:数据持久化追加模式。
- LUA scripting:Lua脚本相关配置项。
- Redis cluster:Redis集群相关配置项。
- cluster Docker/NAT support:Docker/Nat集群支持配置项。
- slow log:耗时日志。
- latency monitor:延迟监控配置相关选项。
- event notification:事件通知相关配置项。
- Gopher server:Gopher服务器。
- advanced config:高级配置。
- active defragmentation:活动碎片整理相关配置。
持久化
官网文档:https://redis.io/docs/management/persistence/
RDB
默认保存为dump.rdb。在配置文件的snapshotting部分设置。
在满足配置文件中的保存条件、执行flushall命令、退出Redis时都会产生dump.rdb文件。
将rdb文件放在redis的安装目录并启动即可恢复其中的数据。
1# 查看该目录
2config get dir
AOF
在配置文件的append only mode中设置。
如果运行 Redis 的计算机停止、电源线故障或者kill -9杀死了redis,RDB最新数据会丢失。因此在某些情况下单独使用RDB不好。
可以配置Redis fsync的频率(appendfsync),有三种:always,每个命令都追加;everysec,每一秒,可能会丢失最后一秒都数据;no,不执行fsync,交给操作系统,一般Linux每30s把数据刷入磁盘一次。官方建议everysec,always在实践中特别慢。
aof被截断时会出现如下日志:
1* Reading RDB preamble from AOF file...
2* Reading the remaining AOF tail...
3# !!! Warning: short read while loading the AOF file !!!
4# !!! Truncating the AOF at offset 439 !!!
5# AOF loaded anyway because aof-load-truncated is enabled
这时可能redis就启动不起来,使用命令:
1# 修复aof文件
2redis-check-aof --fix <filename>
3# 可以使用diff -u命令来查看新旧文件的区别
如果aof被破坏,会出现以下日志:
1* Reading the remaining AOF tail...
2# Bad file format reading the append only file: make a backup of your AOF file, then use ./redis-check-aof --fix <filename>
只能使用命令 redis-check-aof,得到有问题的offset,再人工去那里查看并解决。
aof文件本身是分散地存储在多个文件的,官网的文档内还给出了aof文件的备份方法,以及对数据备份的实践方法。
发布订阅
官网文档:https://redis.io/docs/interact/pubsub/
1SUBSCRIBE channel [channel ...]
2PUBLISH channel message
集群
官网文档:https://redis.io/docs/reference/cluster-spec/
1# 打印出当前节点的信息
2info replication
3
4# 将host port作为当前节点的主节点
5slaveof host port
6slaveof no one # 自己当master
哨兵模式
官网文档:https://redis.io/docs/reference/sentinel-clients/
1redis-server /path/to/sentinel.conf --sentinel
2
3# 一份典型的哨兵配置文件:
4# 监控一个名称为mymaster的Redis Master服务,地址和端口号为127.0.0.1:6379,quorum为2
5sentinel monitor mymaster 127.0.0.1 6379 2
6# 如果哨兵60s内未收到mymaster的有效ping回复,则认为mymaster处于down的状态
7sentinel down-after-milliseconds mymaster 60000
8sentinel failover-timeout mymaster 180000 # 执行切换的超时时间为180s
9# 切换完成后同时向新的Redis Master发起同步数据请求的Redis Slave个数为1,即切换完成后依次让每个Slave去同步数据,前一个Slave同步完成后下一个Slave才发起同步数据的请求
10sentinel parallel-syncs mymaster 1
11# 监控一个名称为resque的Redis Master服务,地址和端口号为127.0.0.1:6380,quorum为4
12sentinel monitor resque 192.168.1.3 6380 4
13sentinel down-after-milliseconds resque 10000
14sentinel failover[…]
Docker配置集群
1docker network create redis --subnet 172.38.0.0/16
2docker network ls
3NETWORK ID NAME DRIVER SCOPE
4xxxxxxxxxx redis bridge local
1# 通过脚本创建6个redis配置文件
2
3# for 循环6次
4for port in $(seq 1 6); \
5do \
6# 配置文件的目录
7mkdir -p /home/chs/chs-files/redis/node-${port}/conf
8touch /home/chs/chs-files/redis/node-${port}/conf/redis.conf
9cat << EOF >/home/chs/chs-files/redis/node-${port}/conf/redis.conf
10# 配置内容
11port 6379
12bind 0.0.0.0
13cluster-enabled yes
14cluster-config-file nodes.conf
15cluster-node-timeout 5000
16cluster-announce-ip 172.38.0.1${port}
17cluster-announce-port 6379
18cluster-announce-bus-port 16379
19appendonly yes
20EOF
21done
1# 启动6个redis服务
2# 启动第1个redis服务:外部暴露端口号:6371、 卷挂载、 网络为:172.38.0.11
3docker run -p 6371:6379 -p 16371:16379 --name redis-1 \
4-v /home/chs/chs-files/redis/node-1/data:/data \
5-v /home/chs/chs-files/redis/node-1/conf/redis.conf:/etc/redis/redis.conf \
6-d --net redis --ip 172.38.0.11 redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf
7
8# 启动第n个redis服务:
9docker run -p 637${port}:6379 -p 1637${port}:16379 --name redis-${port} \
10-v /home/chs/chs-files/redis/node-${port}/data:/data \
11-v /home/chs/chs-files/redis/node-${port}/conf/redis.conf:/etc/redis/redis.conf \
12-d --net redis --ip 172.38.0.1${port} redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf
13
14# 通过docker ps查看
1# 进入容器
2docker exec -it redis-1 /bin/sh
3
4# 创建集群
5redis-cli --cluster create 172.38.0.11:6379 172.38.0.12:6379 172.38.0.13:6379 172.38.0.14:6379 172.38.0.15:6379 172.38.0.16:6379 --cluster-replicas 1