Redis介绍
1、redis概述
Redis(Remote Dictionary Server),即远程字典服务,是一个开源的使用ANSIC语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。
Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。 它支持多种类型的数据结构,如 字符串(strings), 散列(hashes), 列表(lists), 集合(sets), 有序集合(sorted sets) 与范围查询, bitmaps, hyperloglogs 和 地理空间(geospatial) 索引半径查询。 Redis 内置了 复制(replication),LUA脚本(Lua scripting), LRU驱动事件(LRU eviction),事务(transactions) 和不同级别的 磁盘持久化(persistence), 并通过 Redis哨兵(Sentinel)和自动 分区(Cluster)提供高可用性(high availability)。 (http://www.redis.cn/)
作用
- 内存存储、持久化。
- 效率高,可以用于高速缓存。
- 发布订阅系统。
- 地图信息分析。
- 计时器、计数器等。
特点:多样的数据类型、持久化、集群、事务。
基础知识:
启动命令:
1
redis-server ./redis.conf
连接命令:
1
redis-cli -p 6379
默认有16个数据库,可通过redis.conf查看:
使用select切换数据库(默认是第0个)并进行相关操作(http://www.redis.cn/commands.html):
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
45127.0.0.1:6379> select 3 # 切换数据库
OK
127.0.0.1:6379[3]> dbsize # 查看大小
(integer) 0
127.0.0.1:6379[3]> set name zhu
OK
127.0.0.1:6379[3]> dbsize
(integer) 1
127.0.0.1:6379[3]> keys * # 查看数据库所有key
1) "name"
127.0.0.1:6379[3]> flushdb # 清空当前数据库
OK
127.0.0.1:6379[3]> keys *
(empty array)
127.0.0.1:6379[3]> flushall # 清空所有数据库
OK
127.0.0.1:6379[3]> keys *
(empty array)
127.0.0.1:6379[3]> set name zhu
OK
127.0.0.1:6379[3]> set age 23
OK
127.0.0.1:6379[3]> keys *
1) "age"
2) "name"
127.0.0.1:6379[3]> exists name # 判断是否存在指定key
(integer) 1
127.0.0.1:6379[3]> exists names
(integer) 0
127.0.0.1:6379[3]> move name 1 # 移除指定key-value并将其移动到指定数据库
(integer) 1
127.0.0.1:6379[3]> keys *
1) "age"
127.0.0.1:6379[3]> select 1
OK
127.0.0.1:6379[1]> get name
"zhu"
127.0.0.1:6379[1]> expire name 10 # 设置指定key在10s后过期
(integer) 1
127.0.0.1:6379[1]> get name # 经过10s
(nil)
127.0.0.1:6379[1]> set name zhu
OK
127.0.0.1:6379[1]> type name # 查看指定key的数据类型
string
CPU不是Redis的瓶颈,Redis的瓶颈最有可能是机器内存的大小或者网络带宽。
Redis为什么快?
- redis是将所有数据放在内存的,内存的读写速度非常快。
- redis是单线程的,省去了很多上下文切换线程的时间,对于内存系统来说,没有上下文切换效率是最高的。且多次读写都是在一个CPU上进行的(无需切换),在内存系统下就是最佳方案。
2、redis-benchmark性能测试
redis-benchmark是一个压力测试工具,性能测试的基本命令如下:
1
redis-benchmark [option] [option value]
redis 性能测试工具可选参数如下所示:
测试100个并发连接发送100000个请求
1
redis-benchmark -h localhost -p 6379 -c 100 -n 100000
3、redis数据类型
3.1 五大数据类型
3.1.1 String类型(字符串)
相关命令例子如下:
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
100127.0.0.1:6379> keys *
(empty array)
127.0.0.1:6379> set key1 value1
OK
127.0.0.1:6379> get key1
"value1"
127.0.0.1:6379> append key1 hello # 向指定key追加字符串;如果指定key不存在就相当于新建一个key-value
(integer) 11
127.0.0.1:6379> get key1
"value1hello"
127.0.0.1:6379> strlen key1 # 获取字符串的长度
(integer) 11
127.0.0.1:6379> flushdb # 清空当前数据库信息
OK
######################################################################################
127.0.0.1:6379> set i 0
OK
127.0.0.1:6379> get i
"0"
127.0.0.1:6379> incr i # 自增1
(integer) 1
127.0.0.1:6379> get i
"1"
127.0.0.1:6379> decr i # 自减1
(integer) 0
127.0.0.1:6379> get i
"0"
127.0.0.1:6379> incrby i 10 # 设置自增步长
(integer) 10
127.0.0.1:6379> get i
"10"
127.0.0.1:6379> decrby i 10 # 设置自减步长
(integer) 0
127.0.0.1:6379> get i
"0"
127.0.0.1:6379> flushdb # 清空当前数据库信息
OK
######################################################################################
127.0.0.1:6379> set key1 hello,redis
OK
127.0.0.1:6379> getrange key1 0 3 # 截取字符串[0,3]
"hell"
127.0.0.1:6379> getrange key1 0 -1 # 获取全部字符串(与get key相同)
"hello,redis"
127.0.0.1:6379> setrange key1 0 aa # 替换指定位置开始的字符串
(integer) 11
127.0.0.1:6379> get key1
"aallo,redis"
127.0.0.1:6379> flushdb
OK
######################################################################################
127.0.0.1:6379> setex key1 30 myredis # 设置key1过期时间
OK
127.0.0.1:6379> ttl key1 # 查看指定key剩余过期时间
(integer) 25
127.0.0.1:6379> get key1
"myredis"
127.0.0.1:6379> get key1 # 过30s后key1失效
(nil)
127.0.0.1:6379> setnx mykey myredis # 如果mykey不存在则创建(在分布式锁会使用到)
(integer) 1
127.0.0.1:6379> keys *
1) "mykey"
127.0.0.1:6379> setnx mykey myredis2 # 如果mykey存在则创建失败,返回0
(integer) 0
127.0.0.1:6379> get mykey
"myredis"
127.0.0.1:6379> flushdb
OK
######################################################################################
127.0.0.1:6379> mset k1 v1 k2 v2 k3 v3 # 同时设置多个key-value
OK
127.0.0.1:6379> keys *
1) "k2"
2) "k1"
3) "k3"
127.0.0.1:6379> mget k1 k2 k3 # 同时获取多个值
1) "v1"
2) "v2"
3) "v3"
127.0.0.1:6379> msetnx k1 v1 k4 v4 # 是一个原子性操作,这里由于k1已存在导致k4也设置失败
(integer) 0
127.0.0.1:6379> get k4
(nil)
127.0.0.1:6379> mset user:1:name zhu user:2:age 22 # 设置user:{id}:{field}
OK
127.0.0.1:6379> mget user:1:name user:2:age
1) "zhu"
2) "22"
127.0.0.1:6379> flushdb
OK
######################################################################################
127.0.0.1:6379> getset k1 v1 # 如果不存在指定key则返回null,并设置值
(nil)
127.0.0.1:6379> get k1
"v1"
127.0.0.1:6379> getset k1 v2 # 如果存在指定key则获取原来的值,并设置新的值
"v1"
127.0.0.1:6379> get k1
"v2"使用String存储对象的格式可以如下:
1
2set user:1 {name:zhu,age:22}
mset user:1:name zhu user:1:age 22
3.1.2 List类型(列表)
Redis列表是简单的字符串列表,按照插入顺序排序。可以添加一个元素到列表的头部(左边)或者尾部(右边) 。 一个列表最多可以包含2的32次方-1个元素 (4294967295, 每个列表超过40亿个元素)。
相关命令例子如下:
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
102127.0.0.1:6379> lpush list one # 将一个或多个值插入到列表头部(左边)
(integer) 1
127.0.0.1:6379> lpush list two
(integer) 2
127.0.0.1:6379> lpush list three
(integer) 3
127.0.0.1:6379> lrange list 0 -1 # 获取list中所有值
1) "three"
2) "two"
3) "one"
127.0.0.1:6379> lrange list 0 1 # 获取list中指定区间的值
1) "three"
2) "two"
127.0.0.1:6379> rpush list four # 将一个或多个值插入到列表尾部(右边)
(integer) 4
127.0.0.1:6379> lrange list 0 -1
1) "three"
2) "two"
3) "one"
4) "four"
127.0.0.1:6379> lpop list # 移除list最左边元素
"three"
127.0.0.1:6379> rpop list # 移除list最右边元素
"four"
127.0.0.1:6379> lrange list 0 -1
1) "two"
2) "one"
127.0.0.1:6379> lindex list 0 # 通过下标获取list中的值
"two"
127.0.0.1:6379> lindex list 1
"one"
127.0.0.1:6379> llen list # 返回list长度
(integer) 2
127.0.0.1:6379> rpush list two
(integer) 3
127.0.0.1:6379> lrange list 0 -1
1) "two"
2) "one"
3) "two"
127.0.0.1:6379> lrem list 1 two # 移除list集合中指定个数的value
(integer) 1
127.0.0.1:6379> lrange list 0 -1
1) "one"
2) "two"
127.0.0.1:6379> rpush list three
(integer) 3
127.0.0.1:6379> lrange list 0 -1
1) "one"
2) "two"
3) "three"
127.0.0.1:6379> ltrim list 1 2 # 通过下标截取list指定长度,此时list被改变
OK
127.0.0.1:6379> lrange list 0 -1
1) "two"
2) "three"
127.0.0.1:6379> flushdb
OK
######################################################################################
127.0.0.1:6379> rpush list hello1
(integer) 1
127.0.0.1:6379> rpush list hello2
(integer) 2
127.0.0.1:6379> rpush list hello3
(integer) 3
127.0.0.1:6379> lrange list 0 -1
1) "hello1"
2) "hello2"
3) "hello3"
127.0.0.1:6379> rpoplpush list list2 # 移除列表的最后一个元素,并加入到另一个列表中
"hello3"
127.0.0.1:6379> lrange list 0 -1
1) "hello1"
2) "hello2"
127.0.0.1:6379> lrange list2 0 -1
1) "hello3"
127.0.0.1:6379> exists list # 判断list是否存在
(integer) 1
127.0.0.1:6379> lset list 0 new # 将list中指定下标的值更新成“new”
OK
127.0.0.1:6379> lrange list 0 -1
1) "new"
2) "hello2"
127.0.0.1:6379> flushdb
OK
######################################################################################
127.0.0.1:6379> rpush list hello1
(integer) 1
127.0.0.1:6379> rpush list hello2
(integer) 2
127.0.0.1:6379> linsert list before hello2 other # 将“other”插入到hello2前面
(integer) 3
127.0.0.1:6379> lrange list 0 -1
1) "hello1"
2) "other"
3) "hello2"
127.0.0.1:6379> linsert list after hello2 other # 将“other”插入到hello2后面
(integer) 4
127.0.0.1:6379> lrange list 0 -1
1) "hello1"
2) "other"
3) "hello2"
4) "other"
3.1.3 Set类型(集合)
Redis的Set是String类型的无序集合。集合成员是唯一的,这就意味着集合中不能出现重复的数据。
Redis中集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是 O(1)。
集合中最大的成员数为2的32次方-1 (4294967295, 每个集合可存储40多亿个成员)。
相关命令例子如下:
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
75127.0.0.1:6379> keys *
(empty array)
127.0.0.1:6379> sadd myset hello # 向set中添加元素
(integer) 1
127.0.0.1:6379> sadd myset redis
(integer) 1
127.0.0.1:6379> smembers myset # 查看set中所有值
1) "redis"
2) "hello"
127.0.0.1:6379> sismember myset hello # 判断某个值是否在set中
(integer) 1
127.0.0.1:6379> sismember myset world
(integer) 0
127.0.0.1:6379> scard myset # 获取set中元素个数
(integer) 2
127.0.0.1:6379> srem myset redis # 移除set中指定元素
(integer) 1
127.0.0.1:6379> smembers myset
1) "hello"
127.0.0.1:6379> sadd myset world
(integer) 1
127.0.0.1:6379> sadd myset myredis
(integer) 1
127.0.0.1:6379> smembers myset
1) "myredis"
2) "world"
3) "hello"
127.0.0.1:6379> srandmember myset # 随机抽出指定个数的元素(默认是1)
"myredis"
127.0.0.1:6379> srandmember myset
"myredis"
127.0.0.1:6379> srandmember myset
"hello"
127.0.0.1:6379> spop myset # 随机删除myset中的一个元素
"hello"
127.0.0.1:6379> smembers myset
1) "myredis"
2) "world"
127.0.0.1:6379> sadd myset2 set2
(integer) 1
127.0.0.1:6379> smembers myset2
1) "set2"
127.0.0.1:6379> smove myset myset2 myredis # 将某一个set中指定的值移动到另一个set中
(integer) 1
127.0.0.1:6379> smembers myset
1) "world"
127.0.0.1:6379> smembers myset2
1) "myredis"
2) "set2"
127.0.0.1:6379> flushdb
OK
######################################################################################
127.0.0.1:6379> sadd key1 a
(integer) 1
127.0.0.1:6379> sadd key1 b
(integer) 1
127.0.0.1:6379> sadd key1 c
(integer) 1
127.0.0.1:6379> sadd key2 c
(integer) 1
127.0.0.1:6379> sadd key2 d
(integer) 1
127.0.0.1:6379> sadd key2 e
(integer) 1
127.0.0.1:6379> sdiff key1 key2 # 查看两个set的差集
1) "a"
2) "b"
127.0.0.1:6379> sinter key1 key2 # 查看两个set的交集
1) "c"
127.0.0.1:6379> sunion key1 key2 # 查看两个set的并集
1) "b"
2) "e"
3) "c"
4) "a"
5) "d"
3.1.4 Hash类型(哈希)
Redis的hash是一个string类型的field(字段)和value(值)的映射表,hash特别适合用于存储对象。Redis 中每个hash可以存储2的32次方-1键值对(40多亿)。 (key-map)
相关命令例子如下:
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
41127.0.0.1:6379> keys *
(empty array)
127.0.0.1:6379> hset myhash name zhu # 设置一个key-map(key-value)
(integer) 1
127.0.0.1:6379> hget myhash name # 获取一个字段值
"zhu"
127.0.0.1:6379> hmset myhash name hello name2 world # 同时设置多个key-map(field-value)
OK
127.0.0.1:6379> hmget myhash name name2 # 获取多个字段值
1) "hello"
2) "world"
127.0.0.1:6379> hgetall myhash # 获取全部数据
1) "name"
2) "hello"
3) "name2"
4) "world"
127.0.0.1:6379> hdel myhash name # 删除指定的key,对应的value也消失
(integer) 1
127.0.0.1:6379> hgetall myhash
1) "name2"
2) "world"
127.0.0.1:6379> hlen myhash # 获取hash的字段数
(integer) 1
127.0.0.1:6379> hexists myhash name2 # 判断hash中指定字段是否存在
(integer) 1
127.0.0.1:6379> hexists myhash name3
(integer) 0
127.0.0.1:6379> hkeys myhash # 获取hash中所有field
1) "name2"
127.0.0.1:6379> hvals myhash # 获取hash中所有value
1) "world"
127.0.0.1:6379> hset myhash age 22
(integer) 1
127.0.0.1:6379> hincrby myhash age 1 # 指定增量
(integer) 23
127.0.0.1:6379> hincrby myhash age -1
(integer) 22
127.0.0.1:6379> hsetnx myhash age2 5 # 如果不存在则设置成功
(integer) 1
127.0.0.1:6379> hsetnx myhash age2 6 # 如果存在则设置失败
(integer) 0
3.1.5 Zset类型(有序集合)
Redis有序集合和集合一样也是string类型元素的集合,且不允许重复的成员。
不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。
有序集合的成员是唯一的,但分数(score)却可以重复。
集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)。 集合中最大的成员数为2的32次方-1 (4294967295, 每个集合可存储40多亿个成员)。
相关命令例子如下:
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
44127.0.0.1:6379> keys *
(empty array)
127.0.0.1:6379> zadd ageset 5 zhangsan # 添加值
(integer) 1
127.0.0.1:6379> zadd ageset 3 lisi
(integer) 1
127.0.0.1:6379> zadd ageset 10 xiaoming
(integer) 1
127.0.0.1:6379> zrange ageset 0 -1 # 查看所有值
1) "lisi"
2) "zhangsan"
3) "xiaoming"
127.0.0.1:6379> zrangebyscore ageset -inf +inf # 从小到大显示所有值
1) "lisi"
2) "zhangsan"
3) "xiaoming"
127.0.0.1:6379> zrangebyscore ageset -inf +inf withscores # 从小到大显示所有值并携带score
1) "lisi"
2) "3"
3) "zhangsan"
4) "5"
5) "xiaoming"
6) "10"
127.0.0.1:6379> zrangebyscore ageset -inf 5 withscores # 显示score小于等于5的值
1) "lisi"
2) "3"
3) "zhangsan"
4) "5"
127.0.0.1:6379> zrange ageset 0 -1
1) "lisi"
2) "zhangsan"
3) "xiaoming"
127.0.0.1:6379> zrem ageset xiaoming # 移除指定元素
(integer) 1
127.0.0.1:6379> zrange ageset 0 -1
1) "lisi"
2) "zhangsan"
127.0.0.1:6379> zcard ageset # 获取集合个数
(integer) 2
127.0.0.1:6379> zrevrange ageset 0 -1 # 从大到小显示所有值
1) "zhangsan"
2) "lisi"
127.0.0.1:6379> zcount ageset 1 5 # 获取指定区间的值的个数
(integer) 2
3.2 三种特殊数据类型
3.2.1 GEO类型(地理位置)
Redis GEO主要用于存储地理位置信息,并对存储的信息进行操作,该功能在Redis 3.2版本新增。
Redis GEO操作方法有:
geoadd:添加地理位置的坐标。
1
2
3
4
5
6
7
8
9
10
11
12
13
14# GEOADD key longitude latitude member [longitude latitude member ...]
# 格式: key-value(经度、纬度、名称)
# 有效的经度从-180度到180度
# 有效的纬度从-85.05112878度到85.05112878度
127.0.0.1:6379> geoadd China:city 116.23128 40.22077 beijing
(integer) 1
127.0.0.1:6379> geoadd China:city 121.48941 31.40527 shanghai
(integer) 1
127.0.0.1:6379> geoadd China:city 113.27324 23.15792 guangzhou
(integer) 1
127.0.0.1:6379> geoadd China:city 113.88308 22.55329 shenzhen
(integer) 1
127.0.0.1:6379> geoadd China:city 120.21201 30.2084 hangzhou
(integer) 1geopos:获取地理位置的坐标。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16# GEOPOS key member [member ...]
127.0.0.1:6379> geopos China:city beijing
1) 1) "116.23128265142440796"
2) "40.22076905438526495"
127.0.0.1:6379> geopos China:city shanghai
1) 1) "121.48941010236740112"
2) "31.40526993848380499"
127.0.0.1:6379> geopos China:city guangzhou
1) 1) "113.27324062585830688"
2) "23.1579209662846921"
127.0.0.1:6379> geopos China:city shenzhen
1) 1) "113.88307839632034302"
2) "22.55329111565713873"
127.0.0.1:6379> geopos China:city hangzhou
1) 1) "120.21200805902481079"
2) "30.20839995425554747"geodist:计算两个位置之间的距离。
1
2
3
4
5# GEODIST key member1 member2 [m(米)|km(千米)|ft(英尺)|mi(英里)]
127.0.0.1:6379> geodist China:city beijing shanghai # 查看北京到上海的直线距离
"1088644.3544"
127.0.0.1:6379> geodist China:city beijing shanghai km
"1088.6444"georadius:根据用户给定的经纬度坐标来获取指定范围内的地理位置集合。
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# GEORADIUS key longitude latitude radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC|DESC] [STORE key] [STOREDIST key]
127.0.0.1:6379> georadius China:city 110 30 1000 km # 以经度110,纬度30为中心找方圆1000km的城市
1) "shenzhen"
2) "guangzhou"
3) "hangzhou"
127.0.0.1:6379> georadius China:city 110 30 1000 km withdist # 显示直线距离
1) 1) "shenzhen"
2) "914.1294"
2) 1) "guangzhou"
2) "827.6084"
3) 1) "hangzhou"
2) "982.5796"
127.0.0.1:6379> georadius China:city 110 30 1000 km withcoord # 显示定位信息
1) 1) "shenzhen"
2) 1) "113.88307839632034302"
2) "22.55329111565713873"
2) 1) "guangzhou"
2) 1) "113.27324062585830688"
2) "23.1579209662846921"
3) 1) "hangzhou"
2) 1) "120.21200805902481079"
2) "30.20839995425554747"
127.0.0.1:6379> georadius China:city 110 30 1000 km withcoord count 1 # 筛选出指定数量的位置信息
1) 1) "guangzhou"
2) 1) "113.27324062585830688"
2) "23.1579209662846921"georadiusbymember:根据储存在位置集合里面的某个地点获取指定范围内的地理位置集合。
1
2
3# GEORADIUSBYMEMBER key member radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC|DESC] [STORE key] [STOREDIST key]
127.0.0.1:6379> georadiusbymember China:city beijing 1000 km # 找出位于指定位置周围1000km的位置信息
1) "beijing"geohash:返回一个或多个位置对象的geohash值。
1
2
3
4# GEOHASH key member [member ...]
127.0.0.1:6379> geohash China:city beijing shanghai
1) "wx4sucvncn0"
2) "wtw6st1uuq0"
GEO的底层实际是Zset,所以可以使用Zset来操作GEO。
1
2
3
4
5
6
7
8
9
10
11
12
13127.0.0.1:6379> zrange China:city 0 -1 # 查看全部元素
1) "shenzhen"
2) "guangzhou"
3) "hangzhou"
4) "shanghai"
5) "beijing"
127.0.0.1:6379> zrem China:city beijing # 删除指定元素
(integer) 1
127.0.0.1:6379> zrange China:city 0 -1
1) "shenzhen"
2) "guangzhou"
3) "hangzhou"
4) "shanghai"
3.2.2 HyperLogLog类型
Redis在2.8.9版本添加了HyperLogLog结构。
Redis HyperLogLog是用来做基数统计的算法,HyperLogLog的优点是,在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定的、并且是很小的。
在Redis里面,每个HyperLogLog键只需要花费12KB内存,就可以计算接近2^64个不同元素的基数 (有一点误差)。这和计算基数时,元素越多耗费内存就越多的集合形成鲜明对比。
因为HyperLogLog只会根据输入元素来计算基数,而不会储存输入元素本身,所以HyperLogLog不能像集合那样,返回输入的各个元素。
基数: 比如数据集 {1, 3, 5, 7, 5, 7, 8}, 那么这个数据集的基数集为 {1, 3, 5 ,7, 8}, 基数(不重复元素)为5。 基数估计就是在误差可接受的范围内,快速计算基数。
1
2
3
4
5
6
7
8
9
10
11
12127.0.0.1:6379> pfadd mykey a b c d e f g # 创建第一组元素
(integer) 1
127.0.0.1:6379> pfcount mykey # 统计第一组元素基数数量
(integer) 7
127.0.0.1:6379> pfadd mykey2 s f e t g u e t g # 创建第二组元素
(integer) 1
127.0.0.1:6379> pfcount mykey2 # 统计第二组元素基数数量
(integer) 5
127.0.0.1:6379> pfmerge mykey3 mykey mykey2 # 合并第一组和第二组元素到mykey3(并集)
OK
127.0.0.1:6379> pfcount mykey3 # 查看并集基数数量
(integer) 9
3.2.3 Bitmaps(位存储)
Bitmaps单独提供了一套命令, 所以在Redis中使用Bitmaps和使用字符串的方法不太相同。 可以把Bitmaps想象成一个以位为单位的数组, 数组的每个单元只能存储0和1, 数组的下标在Bitmaps中叫做偏移量。
例如用Bitmaps来模拟用户在周一到周日的打卡情况(0表示未打卡,1表示已打卡)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20127.0.0.1:6379> setbit sign 0 1
(integer) 0
127.0.0.1:6379> setbit sign 1 0
(integer) 0
127.0.0.1:6379> setbit sign 2 0
(integer) 0
127.0.0.1:6379> setbit sign 3 1
(integer) 0
127.0.0.1:6379> setbit sign 4 1
(integer) 0
127.0.0.1:6379> setbit sign 5 0
(integer) 0
127.0.0.1:6379> setbit sign 6 0
(integer) 0
127.0.0.1:6379> getbit sign 3 # 获取指定天的打卡情况
(integer) 1
127.0.0.1:6379> getbit sign 6
(integer) 0
127.0.0.1:6379> bitcount sign # 统计打卡成功天数
(integer) 3
4、redis事务
单个Redis命令的执行是原子性的,但Redis没有在事务上增加任何维持原子性的机制,所以Redis事务的执行并不是原子性的。
事务可以理解为一个打包的批量执行脚本,但批量指令并非原子化的操作,中间某条指令的失败不会导致前面已做指令的回滚,也不会造成后续的指令不做。
redis的事务有一次性、顺序性和排他性的特点。(没有隔离级别的概念)
所有命令在事务中并没有直接被执行,而是只有发起执行命令(exec)时才会被一起执行,放弃事务使用discard命令。
一个事务从开始到执行会经历以下三个阶段:
- 开始事务(multi)。
- 命令入队。
- 执行事务(exec)。
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
96127.0.0.1:6379> multi # 开启事务
OK
# 命令入队
127.0.0.1:6379(TX)> set k1 v1
QUEUED
127.0.0.1:6379(TX)> set k2 v2
QUEUED
127.0.0.1:6379(TX)> get k2
QUEUED
127.0.0.1:6379(TX)> exec # 执行事务
1) OK
2) OK
3) "v2"
# 如果在事务中产生了编译时异常(即命令有错误),则事务中所有的命令都不会被执行
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set k1 v1
QUEUED
127.0.0.1:6379(TX)> set k2 v2
QUEUED
127.0.0.1:6379(TX)> getset k3 # 错误命令
(error) ERR wrong number of arguments for 'getset' command
127.0.0.1:6379(TX)> set k4 v4
QUEUED
127.0.0.1:6379(TX)> exec # 执行事务报错
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> get k1 # 事务中所有命令都没执行
(nil)
# 如果在事务中产生了运行时异常(即存在语法错误),则事务中的错误命令抛出异常,其它命令可以被执行
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set k1 v1
QUEUED
127.0.0.1:6379(TX)> incr k1 # 执行失败
QUEUED
127.0.0.1:6379(TX)> set k2 v2
QUEUED
127.0.0.1:6379(TX)> set k3 v3
QUEUED
127.0.0.1:6379(TX)> get k3
QUEUED
127.0.0.1:6379(TX)> exec
1) OK
2) (error) ERR value is not an integer or out of range
3) OK
4) OK
5) "v3"
# redis监视测试(可以使用watch当做redis的乐观锁操作)
127.0.0.1:6379> set money 100
OK
127.0.0.1:6379> set out 0
OK
127.0.0.1:6379> watch money # 监视money
OK
127.0.0.1:6379> multi # 事务正常结束,数据在监视期间没有发生变动则正常执行成功
OK
127.0.0.1:6379(TX)> decrby money 20
QUEUED
127.0.0.1:6379(TX)> incrby out 20
QUEUED
127.0.0.1:6379(TX)> exec
1) (integer) 80
2) (integer) 20
# 这时假如第二个客户端对money进行监视,并在事务中对money进行修改
127.0.0.1:6379> watch money
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> decrby money 10
QUEUED
127.0.0.1:6379(TX)> incrby money 10
QUEUED
# 回到第一个客户端,如果把money修改
127.0.0.1:6379> get money
"80"
127.0.0.1:6379> set money 100
OK
# 此时第二个客户端执行事务,因为money已经被修改,所以修改失败
127.0.0.1:6379(TX)> exec
(nil)
127.0.0.1:6379> unwatch # 解锁
OK
127.0.0.1:6379> watch money # 获取最新的值重新进行监视
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> decrby money 10
QUEUED
127.0.0.1:6379(TX)> incrby money 10
QUEUED
127.0.0.1:6379(TX)> exec # 比对监视的值是否发生变化
1) (integer) 90
2) (integer) 100
5、使用Jedis操作redis
Jedis是Redis官方推荐的Java连接开发工具,即可以实现使用Java来操作Redis。
①导入相关依赖。
1
2
3
4
5
6
7
8
9
10
11
12<!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.5.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.75</version>
</dependency>②编写测试代码。
1
2
3
4
5
6
7public class TestRedis {
public static void main(String[] args) {
//连接本地的Redis服务
Jedis jedis = new Jedis("127.0.0.1", 6379);
System.out.println(jedis.ping());//PONG
}
}
Jedis测试对Key的操作:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23public class TestKey {
public static void main(String[] args) {
Jedis jedis = new Jedis("127.0.0.1", 6379);
System.out.println("清空数据:"+jedis.flushDB());
System.out.println("判断某个键是否存在:"+jedis.exists("username"));
System.out.println("新增<'username','zhu'>的键值对:"+jedis.set("username", "zhu"));
System.out.println("新增<'password','123456'>的键值对:"+jedis.set("password", "123456"));
System.out.print("系统中所有的键如下:");
Set<String> keys = jedis.keys("*");
System.out.println(keys);
System.out.println("删除键password:"+jedis.del("password"));
System.out.println("判断键password是否存在:"+jedis.exists("password"));
System.out.println("查看键username所存储的值的类型:"+jedis.type("username"));
System.out.println("随机返回key空间的一个:"+jedis.randomKey());
System.out.println("重命名key:"+jedis.rename("username","name"));
System.out.println("取出改后的name:"+jedis.get("name"));
System.out.println("按索引查询:"+jedis.select(0));
System.out.println("删除当前选择数据库中的所有key:"+jedis.flushDB());
System.out.println("返回当前数据库中key的数目:"+jedis.dbSize());
System.out.println("删除所有数据库中的所有key:"+jedis.flushAll());
}
}执行结果:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15清空数据:OK
判断某个键是否存在:false
新增<'username','zhu'>的键值对:OK
新增<'password','123456'>的键值对:OK
系统中所有的键如下:[password, username]
删除键password:1
判断键password是否存在:false
查看键username所存储的值的类型:string
随机返回key空间的一个:username
重命名key:OK
取出改后的name:zhu
按索引查询:OK
删除当前选择数据库中的所有key:OK
返回当前数据库中key的数目:0
删除所有数据库中的所有key:OKJedis测试对String的操作:
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
46public class TestString {
public static void main(String[] args) {
Jedis jedis = new Jedis("127.0.0.1", 6379);
jedis.flushDB();
System.out.println("===========增加数据===========");
System.out.println(jedis.set("key1","value1"));
System.out.println(jedis.set("key2","value2"));
System.out.println(jedis.set("key3", "value3"));
System.out.println("删除键key2:"+jedis.del("key2"));
System.out.println("获取键key2:"+jedis.get("key2"));
System.out.println("修改key1:"+jedis.set("key1", "value1Changed"));
System.out.println("获取key1的值:"+jedis.get("key1"));
System.out.println("在key3后面加入值:"+jedis.append("key3", "End"));
System.out.println("key3的值:"+jedis.get("key3"));
System.out.println("增加多个键值对:"+jedis.mset("key01","value01","key02","value02","key03","value03"));
System.out.println("获取多个键值对:"+jedis.mget("key01","key02","key03"));
System.out.println("获取多个键值对:"+jedis.mget("key01","key02","key03","key04"));
System.out.println("删除多个键值对:"+jedis.del("key01","key02"));
System.out.println("获取多个键值对:"+jedis.mget("key01","key02","key03"));
jedis.flushDB();
System.out.println("===========新增键值对防止覆盖原先值==============");
System.out.println(jedis.setnx("key1", "value1"));
System.out.println(jedis.setnx("key2", "value2"));
System.out.println(jedis.setnx("key2", "value2-new"));
System.out.println(jedis.get("key1"));
System.out.println(jedis.get("key2"));
System.out.println("===========新增键值对并设置有效时间=============");
System.out.println(jedis.setex("key3", 2, "value3"));
System.out.println(jedis.get("key3"));
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(jedis.get("key3"));
System.out.println("===========获取原值,更新为新值==========");
System.out.println(jedis.getSet("key2", "key2GetSet"));
System.out.println(jedis.get("key2"));
System.out.println("获得key2的值的字串:"+jedis.getrange("key2", 2, 4));
}
}执行结果:
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===========增加数据===========
OK
OK
OK
删除键key2:1
获取键key2:null
修改key1:OK
获取key1的值:value1Changed
在key3后面加入值:9
key3的值:value3End
增加多个键值对:OK
获取多个键值对:[value01, value02, value03]
获取多个键值对:[value01, value02, value03, null]
删除多个键值对:2
获取多个键值对:[null, null, value03]
===========新增键值对防止覆盖原先值==============
1
1
0
value1
value2
===========新增键值对并设置有效时间=============
OK
value3
null
===========获取原值,更新为新值==========
value2
key2GetSet
获得key2的值的字串:y2GJedis测试对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
32
33
34
35public class TestList {
public static void main(String[] args) {
Jedis jedis = new Jedis("127.0.0.1", 6379);
jedis.flushDB();
System.out.println("===========添加一个list===========");
jedis.lpush("collections", "ArrayList", "Vector", "Stack", "HashMap", "WeakHashMap", "LinkedHashMap");
jedis.lpush("collections", "HashSet");
jedis.lpush("collections", "TreeSet");
jedis.lpush("collections", "TreeMap");
System.out.println("collections的内容:"+jedis.lrange("collections", 0, -1));//-1代表倒数第一个元素,-2代表倒数第二个元素,end为-1表示查询全部
System.out.println("collections区间0-3的元素:"+jedis.lrange("collections",0,3));
System.out.println("===============================");
// 删除列表指定的值 ,第二个参数为删除的个数(有重复时),后add进去的值先被删,类似于出栈
System.out.println("删除指定元素个数:"+jedis.lrem("collections", 2, "HashMap"));
System.out.println("collections的内容:"+jedis.lrange("collections", 0, -1));
System.out.println("删除下表0-3区间之外的元素:"+jedis.ltrim("collections", 0, 3));
System.out.println("collections的内容:"+jedis.lrange("collections", 0, -1));
System.out.println("collections列表出栈(左端):"+jedis.lpop("collections"));
System.out.println("collections的内容:"+jedis.lrange("collections", 0, -1));
System.out.println("collections添加元素,从列表右端,与lpush相对应:"+jedis.rpush("collections", "EnumMap"));
System.out.println("collections的内容:"+jedis.lrange("collections", 0, -1));
System.out.println("collections列表出栈(右端):"+jedis.rpop("collections"));
System.out.println("collections的内容:"+jedis.lrange("collections", 0, -1));
System.out.println("修改collections指定下标1的内容:"+jedis.lset("collections", 1, "LinkedArrayList"));
System.out.println("collections的内容:"+jedis.lrange("collections", 0, -1));
System.out.println("===============================");
System.out.println("collections的长度:"+jedis.llen("collections"));
System.out.println("获取collections下标为2的元素:"+jedis.lindex("collections", 2));
System.out.println("===============================");
jedis.lpush("sortedList", "3","6","2","0","7","4");
System.out.println("sortedList排序前:"+jedis.lrange("sortedList", 0, -1));
System.out.println(jedis.sort("sortedList"));
System.out.println("sortedList排序后:"+jedis.lrange("sortedList", 0, -1));
}
}执行结果:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23===========添加一个list===========
collections的内容:[TreeMap, TreeSet, HashSet, LinkedHashMap, WeakHashMap, HashMap, Stack, Vector, ArrayList]
collections区间0-3的元素:[TreeMap, TreeSet, HashSet, LinkedHashMap]
===============================
删除指定元素个数:1
collections的内容:[TreeMap, TreeSet, HashSet, LinkedHashMap, WeakHashMap, Stack, Vector, ArrayList]
删除下表0-3区间之外的元素:OK
collections的内容:[TreeMap, TreeSet, HashSet, LinkedHashMap]
collections列表出栈(左端):TreeMap
collections的内容:[TreeSet, HashSet, LinkedHashMap]
collections添加元素,从列表右端,与lpush相对应:4
collections的内容:[TreeSet, HashSet, LinkedHashMap, EnumMap]
collections列表出栈(右端):EnumMap
collections的内容:[TreeSet, HashSet, LinkedHashMap]
修改collections指定下标1的内容:OK
collections的内容:[TreeSet, LinkedArrayList, LinkedHashMap]
===============================
collections的长度:3
获取collections下标为2的元素:LinkedHashMap
===============================
sortedList排序前:[4, 7, 0, 2, 6, 3]
[0, 2, 3, 4, 6, 7]
sortedList排序后:[4, 7, 0, 2, 6, 3]Jedis测试对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
25
26
27
28
29
30
31
32
33
34
35
36
37public class TestSet {
public static void main(String[] args) {
Jedis jedis = new Jedis("127.0.0.1", 6379);
jedis.flushDB();
System.out.println("============向集合中添加元素(不重复)============");
System.out.println(jedis.sadd("eleSet", "e1","e2","e4","e3","e0","e8","e7","e5"));
System.out.println(jedis.sadd("eleSet", "e6"));
System.out.println(jedis.sadd("eleSet", "e6"));
System.out.println("eleSet的所有元素为:"+jedis.smembers("eleSet"));
System.out.println("删除一个元素e0:"+jedis.srem("eleSet", "e0"));
System.out.println("eleSet的所有元素为:"+jedis.smembers("eleSet"));
System.out.println("删除两个元素e7和e6:"+jedis.srem("eleSet", "e7","e6"));
System.out.println("eleSet的所有元素为:"+jedis.smembers("eleSet"));
System.out.println("随机的移除集合中的一个元素:"+jedis.spop("eleSet"));
System.out.println("随机的移除集合中的一个元素:"+jedis.spop("eleSet"));
System.out.println("eleSet的所有元素为:"+jedis.smembers("eleSet"));
System.out.println("eleSet中包含元素的个数:"+jedis.scard("eleSet"));
System.out.println("e3是否在eleSet中:"+jedis.sismember("eleSet", "e3"));
System.out.println("e1是否在eleSet中:"+jedis.sismember("eleSet", "e1"));
System.out.println("e1是否在eleSet中:"+jedis.sismember("eleSet", "e5"));
System.out.println("=================================");
System.out.println(jedis.sadd("eleSet1", "e1","e2","e4","e3","e0","e8","e7","e5"));
System.out.println(jedis.sadd("eleSet2", "e1","e2","e4","e3","e0","e8"));
System.out.println("将eleSet1中删除e1并存入eleSet3中:"+jedis.smove("eleSet1", "eleSet3", "e1"));//移到集合元素
System.out.println("将eleSet1中删除e2并存入eleSet3中:"+jedis.smove("eleSet1", "eleSet3", "e2"));
System.out.println("eleSet1中的元素:"+jedis.smembers("eleSet1"));
System.out.println("eleSet3中的元素:"+jedis.smembers("eleSet3"));
System.out.println("============集合运算=================");
System.out.println("eleSet1中的元素:"+jedis.smembers("eleSet1"));
System.out.println("eleSet2中的元素:"+jedis.smembers("eleSet2"));
System.out.println("eleSet1和eleSet2的交集:"+jedis.sinter("eleSet1","eleSet2"));
System.out.println("eleSet1和eleSet2的并集:"+jedis.sunion("eleSet1","eleSet2"));
System.out.println("eleSet1和eleSet2的差集:"+jedis.sdiff("eleSet1","eleSet2"));//eleSet1中有,eleSet2中没有
jedis.sinterstore("eleSet4","eleSet1","eleSet2");//求交集并将交集保存到dstkey的集合
System.out.println("eleSet4中的元素:"+jedis.smembers("eleSet4"));
}
}执行结果:
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============向集合中添加元素(不重复)============
8
1
0
eleSet的所有元素为:[e5, e1, e4, e0, e7, e2, e3, e8, e6]
删除一个元素e0:1
eleSet的所有元素为:[e7, e2, e3, e8, e4, e1, e5, e6]
删除两个元素e7和e6:2
eleSet的所有元素为:[e8, e4, e1, e5, e2, e3]
随机的移除集合中的一个元素:e5
随机的移除集合中的一个元素:e2
eleSet的所有元素为:[e8, e4, e1, e3]
eleSet中包含元素的个数:4
e3是否在eleSet中:true
e1是否在eleSet中:true
e1是否在eleSet中:false
=================================
8
6
将eleSet1中删除e1并存入eleSet3中:1
将eleSet1中删除e2并存入eleSet3中:1
eleSet1中的元素:[e8, e5, e4, e0, e7, e3]
eleSet3中的元素:[e2, e1]
============集合运算=================
eleSet1中的元素:[e8, e5, e4, e0, e7, e3]
eleSet2中的元素:[e4, e1, e3, e8, e0, e2]
eleSet1和eleSet2的交集:[e4, e3, e8, e0]
eleSet1和eleSet2的并集:[e8, e1, e5, e4, e0, e2, e7, e3]
eleSet1和eleSet2的差集:[e7, e5]
eleSet4中的元素:[e8, e4, e3, e0]Jedis测试对Hash的操作:
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
29public class TestHash {
public static void main(String[] args) {
Jedis jedis = new Jedis("127.0.0.1", 6379);
jedis.flushDB();
Map<String,String> map = new HashMap<String,String>();
map.put("key1","value1");
map.put("key2","value2");
map.put("key3","value3");
map.put("key4","value4");
//添加名称为hash(key)的hash元素
jedis.hmset("hash",map);
//向名称为hash的hash中添加key为key5,value为value5元素
jedis.hset("hash", "key5", "value5");
System.out.println("散列hash的所有键值对为:"+jedis.hgetAll("hash"));//return Map<String,String>
System.out.println("散列hash的所有键为:"+jedis.hkeys("hash"));//return Set<String>
System.out.println("散列hash的所有值为:"+jedis.hvals("hash"));//return List<String>
System.out.println("将key6保存的值加上一个整数,如果key6不存在则添加key6:"+jedis.hincrBy("hash", "key6", 6));
System.out.println("散列hash的所有键值对为:"+jedis.hgetAll("hash"));
System.out.println("将key6保存的值加上一个整数,如果key6不存在则添加key6:"+jedis.hincrBy("hash", "key6", 3));
System.out.println("散列hash的所有键值对为:"+jedis.hgetAll("hash"));
System.out.println("删除一个或者多个键值对:"+jedis.hdel("hash", "key2"));
System.out.println("散列hash的所有键值对为:"+jedis.hgetAll("hash"));
System.out.println("散列hash中键值对的个数:"+jedis.hlen("hash"));
System.out.println("判断hash中是否存在key2:"+jedis.hexists("hash","key2"));
System.out.println("判断hash中是否存在key3:"+jedis.hexists("hash","key3"));
System.out.println("获取hash中的值:"+jedis.hmget("hash","key3"));
System.out.println("获取hash中的值:"+jedis.hmget("hash","key3","key4"));
}
}执行结果:
1
2
3
4
5
6
7
8
9
10
11
12
13
14散列hash的所有键值对为:{key1=value1, key2=value2, key5=value5, key3=value3, key4=value4}
散列hash的所有键为:[key1, key2, key5, key3, key4]
散列hash的所有值为:[value2, value1, value3, value4, value5]
将key6保存的值加上一个整数,如果key6不存在则添加key6:6
散列hash的所有键值对为:{key1=value1, key2=value2, key5=value5, key6=6, key3=value3, key4=value4}
将key6保存的值加上一个整数,如果key6不存在则添加key6:9
散列hash的所有键值对为:{key1=value1, key2=value2, key5=value5, key6=9, key3=value3, key4=value4}
删除一个或者多个键值对:1
散列hash的所有键值对为:{key1=value1, key5=value5, key6=9, key3=value3, key4=value4}
散列hash中键值对的个数:5
判断hash中是否存在key2:false
判断hash中是否存在key3:true
获取hash中的值:[value3]
获取hash中的值:[value3, value4]Jedis测试事务:
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
26public class TestTx {
public static void main(String[] args) {
Jedis jedis = new Jedis("127.0.0.1", 6379);
JSONObject jsonObject = new JSONObject();
jsonObject.put("hello", "world");
jsonObject.put("name", "zhu");
//开启事务
Transaction multi = jedis.multi();
String result = jsonObject.toJSONString();
try {
multi.set("k1", result);
multi.set("k2", result);
//执行事务
multi.exec();
} catch (Exception e) {
//放弃事务
multi.discard();
e.printStackTrace();
} finally {
System.out.println(jedis.get("k1"));
System.out.println(jedis.get("k2"));
//关闭连接
jedis.close();
}
}
}执行结果:
1
2{"name":"zhu","hello":"world"}
{"name":"zhu","hello":"world"}