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.gzmakemake 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   # 设置命令

主要分为以下部分:

  1. includes:一个配置文件,可以导入其他配置文件。
  2. modules:启动时加载的模块。
  3. network:网络相关配置。
  4. TLS/SSL:安全传输相关模块。
  5. general:通用配置相关。
  6. snapshotting:快照配置相关。
  7. replication:主从复制相关配置项。
  8. keys tracking:键的追踪相关配置项。
  9. security:安全相关配置。
  10. clients:客户端相关配置。
  11. memory management:内存管理相关配置项。
  12. lazy freeing:延迟释放配置项。
  13. threaded I/O:多线程I/O相关配置项。
  14. kernel OOM control:Linux内核防止内存占用过大配置选项。
  15. append only mode:数据持久化追加模式。
  16. LUA scripting:Lua脚本相关配置项。
  17. Redis cluster:Redis集群相关配置项。
  18. cluster Docker/NAT support:Docker/Nat集群支持配置项。
  19. slow log:耗时日志。
  20. latency monitor:延迟监控配置相关选项。
  21. event notification:事件通知相关配置项。
  22. Gopher server:Gopher服务器。
  23. advanced config:高级配置。
  24. 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
comments powered by Disqus