Java笔记-----(7)缓存技术 Redis
缓存的使用可以减少网络请求或者查询数据库的次数,有效提高访问速度。
(1)redis 概述
redis(Remote Dictionary Server远程字典服务),是一款高性能的(key/value)分布式内存数据库,基于内存运行并支持持久化的NoSQL数据库。因为数据都在内存中,所以运行速度快。redis支持丰富的数据类型并且支持事务,事务中的所有命令会被序列化、按顺序执行,在执行的过程中不会被其他客户端发送来的命令打断。
NoSQL(NoSQL = Not Only SQL),意即“不仅仅是SQL”,是一项全新的数据库理念,泛指非关系型的数据库。
NoSQL数据库的产生就是为了解决大规模数据集合、多重数据种类带来的挑战,尤其是大数据应用难题。
(1.1)redis 相比 memcached 有哪些优势?(掌握)
Redis 与 Memcached 两者都是非关系型内存键值数据库,主要有以下不同:
数据类型
Memcached 仅支持字符串类型,而 Redis 支持五种不同的数据类型,可以更灵活地解决问题。
value大小不同,redis最大可以达到512MB,而memcache只有1MB。
数据持久化
Redis 支持两种持久化策略:RDB 快照和 AOF 日志,而 Memcached 不支持持久化。
分布式
Memcached 不支持分布式,只能通过在客户端使用一致性哈希来实现分布式存储,这种方式在存储和查询时都需要
先在客户端计算一次数据所在的节点。而Redis Cluster 实现了分布式的支持。
内存管理机制
- Redis 支持数据的备份,即master-slave模式的数据备份。在 Redis 中,并不是所有数据都一直存储在内存中,可以将一些很久没用的 value 交换到磁盘,而 Memcached 的数据则会一直在内存中。
- Memcached 将内存分割成特定长度的块来存储数据,以完全解决内存碎片的问题。但是这种方式会使得内存的利用率不高,例如块的大小为 128 bytes,只存储 100 bytes 的数据,那么剩下的 28 bytes 就浪费掉了。
- 使用底层模型不同,它们之间底层实现方式 以及与客户端之间通信的应用协议不一样。redis自己构建了VM 管理机制。
(1.2)redis 支持的数据类型(掌握)
redis支持五种数据类型作为其Value,redis的Key都是字符串类型的。
键的类型只能为字符串,值支持五种数据类型:字符串、列表、集合、散列表、有序集合。
注意:使用redis缓存一些不经常发生变化的数据,数据库的数据一旦发生改变,则需要更新缓存。
- 数据库的表执行 增删改 的相关操作,需要将redis缓存数据情况,再次存入
- 在service对应的增删改方法中,将redis数据删除。
① 字符串类型 string
redis 中字符串 value 最大可为512M。可以用来做一些计数功能的缓存(也是实际工作中最常见的)。
1. 存储: set key value
127.0.0.1:6379> set username zhangsan
OK
2. 获取: get key
127.0.0.1:6379> get username
"zhangsan"
3. 删除: del key
127.0.0.1:6379> del age
(integer) 1
② 列表类型 list :linkedlist格式。支持重复元素
简单的字符串列表,按照插入顺序排序,可以添加一个元素到列表的头部(左边)或者尾部(右边),其底层实现是一个链表。可以实现一个简单消息队列功能,做基于redis的分页功能等。支持重复元素。
1. 添加:
1. lpush key value: 将元素加入列表左表
2. rpush key value:将元素加入列表右边
127.0.0.1:6379> lpush myList a
(integer) 1
127.0.0.1:6379> lpush myList b
(integer) 2
127.0.0.1:6379> rpush myList c
(integer) 3
2. 获取:
* lrange key start end :范围获取
127.0.0.1:6379> lrange myList 0 -1
1) "b"
2) "a"
3) "c"
3. 删除:
* lpop key: 删除列表最左边的元素,并将元素返回
* rpop key: 删除列表最右边的元素,并将元素返回
③ 集合类型 set :不允许重复元素
是一个字符串类型的无序集合。可以用来进行全局去重等。不允许重复元素。
1. 存储:sadd key value
127.0.0.1:6379> sadd myset a
(integer) 1
127.0.0.1:6379> sadd myset a
(integer) 0 //不允许重复元素
2. 获取:smembers key:获取set集合中所有元素
127.0.0.1:6379> smembers myset
1) "a"
3. 删除:srem key value:删除set集合中的某个元素
127.0.0.1:6379> srem myset a
(integer) 1
④ 有序集合类型 sorted set :不允许重复元素,且元素有顺序
是一个字符串类型的有序集合,给每一个元素一个固定的分数score来保持顺序。可以用来做排行榜应用或者进行范围查找等。不允许重复元素。
不允许重复元素,且元素有顺序。每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。
1. 存储:zadd key score value
127.0.0.1:6379> zadd mysort 60 zhangsan
(integer) 1
127.0.0.1:6379> zadd mysort 50 lisi
(integer) 1
127.0.0.1:6379> zadd mysort 80 wangwu
(integer) 1
2. 获取:zrange key start end [withscores]
127.0.0.1:6379> zrange mysort 0 -1
1) "lisi"
2) "zhangsan"
3) "wangwu"
127.0.0.1:6379> zrange mysort 0 -1 withscores
1) "zhangsan"
2) "60"
3) "wangwu"
4) "80"
5) "lisi"
6) "500"
3. 删除:zrem key value
127.0.0.1:6379> zrem mysort lisi
(integer) 1
⑤ 哈希类型 hash :map 格式
键值对集合,是一个字符串类型的 Key和 Value 的映射表,也就是说其存储的Value是一个键值对(Key-Value)。可以用来存放一些具有特定结构的信息。
1. 存储: hset key field value
127.0.0.1:6379> hset myhash username lisi
(integer) 1
127.0.0.1:6379> hset myhash password 123
(integer) 1
2. 获取:
* hget key field: 获取指定的field对应的值
127.0.0.1:6379> hget myhash username
"lisi"
* hgetall key:获取所有的field和value
127.0.0.1:6379> hgetall myhash
1) "username"
2) "lisi"
3) "password"
4) "123"
3. 删除: hdel key field
127.0.0.1:6379> hdel myhash username
(integer) 1
其实redis还支持三种特殊的数据类型,分别为BitMap、Geo和HyperLogLog
,自行了解
一般情况下,可以认为redis的支持的数据类型有上述五种,其底层数据结构包括:简单动态字符串,链表,字典,跳表,整数集合以及压缩列表。
(1.3)redis 使用场景
计数器
可以对 String 进行自增自减运算,从而实现计数器功能。
Redis 这种内存型数据库的读写性能非常高,很适合存储频繁读写的计数量。
缓存
将热点数据放到内存中,设置内存的最大使用量以及淘汰策略来保证缓存的命中率。
查找表
例如 DNS 记录就很适合使用 Redis 进行存储。
查找表和缓存类似,也是利用了 Redis 快速的查找特性。但是查找表的内容不能失效,而缓存的内容可以失效,因为
缓存不作为可靠的数据来源。
消息队列
List 是一个双向链表,可以通过 lpush 和 rpop 写入和读取消息
不过最好使用 Kafka、RabbitMQ 等消息中间件。
会话缓存
可以使用 Redis 来统一存储多台应用服务器的会话信息。
当应用服务器不再存储用户的会话信息,也就不再具有状态,一个用户可以请求任意一个应用服务器,从而更容易实
现高可用性以及可伸缩性。
分布式锁实现
在分布式场景下,无法使用单机环境下的锁来对多个节点上的进程进行同步。
可以使用 Redis 自带的 SETNX 命令实现分布式锁,除此之外,还可以使用官方提供的 RedLock 分布式锁实现。
其它
Set 可以实现交集、并集等操作,从而实现共同好友等功能。
ZSet 可以实现有序性操作,从而实现排行榜等功能。
(1.4)redis 通用命令
7. 通用命令
1. keys * : 查询所有的键
2. type key : 获取键对应的value的类型
3. del key : 删除指定的key value
(1.5)redis 的配置文件
redis下载解压后:
- redis.windows.conf:配置文件
- redis-cli.exe:redis的客户端
- redis-server.exe:redis服务器端
在配置文件中对redis进行了分模块配置,常用的模块如下:
- NETWORK:该模块可以配置一些redis服务器地址,端口以及超时时间等
- GENERAL:该模块可以对日志文件的路径和日志级别等进行配置
- SNAPSHOTTING:redis持久化配置信息等
- REPLICATION:redis集群配置等信息
- MEMORY MANAGEMENT:内存管理,包括数据过期删除策略信息的设置
- APPEND ONLY MODE:日志持久化方式信息设置
(2)redis是单线程的吗?为什么执行速度这么快?(重点掌握)
redis是单线程的,redis的单线程是指(网络请求模块)使用了一个线程,所以不需考虑并发安全性。但是对于需要依赖多个操作的复合操作来说,还是需要锁的,而且有可能是分布式锁。
那么单线程的redis为什么执行速度如此之快?
- 基于内存实现,完全内存计算
- 单线程操作,避免了线程上下文切换操作
- 多路I/O复用的线程模型,实现了一个线程监控多个IO流,及时响应请求
- redis对外部的依赖比较少,属于轻量级内存数据库
解析:
redis的线程模型多路I/O复用机制是一个比较重要并且常见的考察点。目前支持I/O多路复用的系统调用有select,pselect,poll,epoll
等函数。
I/O多路复用就是通过一种机制一个进程可以监视多个描述符,一旦某个描述符读就绪或者写就绪,其能够通知应用程序进行相应的读写操作。
多路I/O复用机制与多进程和多线程技术相比系统开销小,系统不必创建进程/线程,也不必维护这些进程/线程,从而大大减小了系统的开销。
关于常见函数的特点如下所示:
多路分离函数select(时间复杂度O(n))
- 会修改传入的参数数组,这个对于一个需要调用很多次的函数,是非常不友好的。
- 有最大监听连接数1024个的限制
- 如果任何一个
sock(I/O stream)
出现了数据,select没有返回具体是哪个返回了数据,需要采用轮询的方式去遍历获取。所以select具有O(n)的无差别轮询复杂度 - 线程不安全(当你在一个线程中已经监听该socket,另一个线程想要将该socket关闭,则结果会不可预知)
- “If a file descriptor being monitored by select() is closed in another thread, the result is unspecified”
poll函数(时间复杂度O(n))
- 去掉了1024的限制(使用链表搞定),没有最大连接数的限制
- 不再修改传入的参数数组
- 依然是线程不安全的
epoll函数(时间复杂度O(1))
- epoll 不仅返回socket组里面数据,还可以确定具体哪个socket有数据
- 线程安全
epoll可以理解为event poll
,不同于忙轮询和无差别轮询,epoll会把哪个流发生了怎样的I/O事件通知我们。所以我们说epoll实际上是事件驱动(每个事件关联上fd)的,此时我们对这些流的操作都是有意义的。(复杂度降低到了O(1))
(3)使用redis可能出现的问题(重点掌握)
这里主要介绍Redis可能出现的三个问题:
(3.1)缓存雪崩
举例:
缓存同一时间大面积的失效,这个时候又来的一波请求都到数据库上,导致数据库连接异常。
解决办法:
可以给缓存设置不同的缓存时间,更新数据使用互斥锁或者通过双缓存在避免缓存雪崩。
(3.2)缓存穿透
举例:
故意的去请求缓存中不存在的数据,导致请求都打到了数据库上,导致数据库异常。
解决办法:
可以使用互斥锁或者无论是否取到结果都将结果存入缓存,还可以使用有效的机制来拦截不合法的key值等。
(3.3)数据库和缓存的双写一致性问题
在高并发请求下很容易导致数据不一致的问题,如果你的业务需要保证数据的强一致性,那么建议不要使用缓存。在数据库中和缓存数据的删除或者写入过程中,如果有失败的情况,会导致数据的不一致。
解决办法:
- 双删延时的解决办法。可以先删除缓存数据,然后再更新数据库数据,最后再隔固定的时间再次删除缓存。
- 更新数据库产生的binlog订阅(使用canal)。将有变化的key记录下来,并且尝试去不断的去删除缓存(如果上次删除缓存失败)
(4)redis的持久化(重点掌握)
在redis中的介绍中,我们知道了redis不光是一个基于内存的缓存,同时还支持数据的持久化。在redis的配置文件模块介绍中,我们可以设置redis的持久化方式。redis的持久化方式有两种,即RDB和AOF的方式。
Redis 是内存型数据库,为了保证数据在断电后不会丢失,需要将内存中的数据持久化到硬盘上。
(4.1)RDB(快照方式 snapshotting)(全量持久化)
RDB:默认方式,不需要进行配置,默认就使用这种机制。
在一定的间隔时间中,检测key的变化情况,然后持久化数据。
将当前内存中的数据集快照写入磁盘,实现数据的持久化,恢复时可以将快照重新载入内存。
- 将某个时间点的所有数据都存放到硬盘上。
- 可以将快照复制到其它服务器从而创建具有相同数据的服务器副本。
- 如果系统发生故障,将会丢失最后一次创建快照之后的数据。
- 如果数据量很大,保存快照的时间会很长。
触发方式:
- 自动触发:在配置文件中,可以配置执行了多少次save就自动触发自动持久化。
- 手动触发:通过
bgsave命令
,在后台异步进行生成快照的操作,同时还可以响应客户端的请求。通过redis进程fork操作创建子进程,生成的快照由子进程负责,客户端请求只会在fork阶段被阻塞。
1. 编辑redis.windwos.conf文件
# after 900 sec (15 min) if at least 1 key changed
save 900 1
# after 300 sec (5 min) if at least 10 keys changed
save 300 10
# after 60 sec if at least 10000 keys changed
save 60 10000
2. 重新启动redis服务器,并指定配置文件名称
D:\redis\windows-64\redis-2.8.9>redis-server.exe redis.windows.conf
快照恢复:
将备份文件 (dump.rdb)
移动到 redis 安装目录并启动服务,redis会自动加载快照文件数据到内存。但是,redis 服务器在载入 RDB 文件期间,会一直处于阻塞状态,直到载入工作完成为止。
优缺点分析:
- RDB持久化方式存在数据的丢失,因为其没有办法实现实时持久化。因为bgsave每次运行都要执行fork操作创建子进程,属于重量级操作,频繁执行成本过高,会影响系统性能。自动触发也存在丢失部分数据的情况。
- 在恢复大数据集时候,RDB方式相对于AOF要快。
(4.2)AOF(append-only-file)(增量持久化)
AOF:日志记录的方式,可以记录每一条命令的操作,将写命令添加到 AOF 文件(Append Only File)
的末尾。可以每一次命令操作后,持久化数据 (对性能影响比较大)。
在 redis配置文件的 APPEND ONLY MODE
中,可以设置AOF持久化。通过记录redis服务器所执行的写命令来记录数据库状态。恢复时可以将AOF文件载入内存,并且可以通过 redis-check-aof --fix
进行修复AOF文件。
使用 AOF 持久化需要设置同步选项,从而确保写命令同步到磁盘文件上的时机。这是因为对文件进行写入并不会马
上将内容同步到磁盘上,而是先存储到缓冲区,然后由操作系统决定什么时候同步到磁盘。有以下同步选项:
- always 选项会严重减低服务器的性能;
- everysec 选项比较合适,可以保证系统崩溃时只会丢失一秒左右的数据,并且 Redis 每秒执行一次同步对服务
器性能几乎没有任何影响; - no 选项并不能给服务器性能带来多大的提升,而且也会增加系统崩溃时数据丢失的数量。
1. 编辑redis.windwos.conf文件
appendonly no(关闭aof) --> appendonly yes (开启aof)
# appendfsync always : 每一次操作都进行持久化
# appendfsync everysec : 每隔一秒进行一次持久化
# appendfsync no : 不进行持久化
随着服务器写请求的增多,AOF 文件会越来越大。Redis 提供了一种将 AOF 重写的特性,能够去除 AOF 文件中的冗
余写命令。
AOF 日志重写
- AOF文件会随着服务器运行的时间越来越大,可以通过AOF重写来控制AOF文件的大小。
- AOF重写会首先读取数据库中现有的键值对状态,然后根据类型使用一条命令来替代前面对键值对操作的多条命令。
- 使用
命令 bgrewriteaof
来实现AOF重写
redis 是单线程工作,当AOF文件较大时重写时间会比较长,在重写 AOF 期间,redis将长时间无法处理客户端请求。为了解决这个问题,可以将 AOF 重写程序放到子进程中执行,好处如下:
- 子进程进行 AOF 重写期间,服务器进程(父进程)可以继续处理其它客户端请求。
- 子进程带有父进程的数据副本,使用子进程而不是线程,可以在避免使用锁的情况下,保证数据的安全性。
子进程中AOF重写导致的问题:
- 子进程在进行 AOF 重写期间,服务器进程依然可以处理其它客户端请求,这就会导致数据库状态已经发生了改变,使得当前数据库数据状态和重写后的AOF 文件中的数据不一致。
- 也就是出现了AOF文件和数据库中数据不一致的问题。
数据状态不一致解决办法:(AOF重写缓存区)
- redis 服务器设置了一个 AOF重写缓冲区。这个缓冲区在创建子进程后开始使用,当redis服务器执行一个客户端的写请求命令,之后将这个写命令也发送到 AOF 重写缓冲区。
- 当子进程完成 AOF 日志重写之后,给父进程发送信号,父进程接收此信号后,将 AOF 重写缓冲区的内容写到新的 AOF文件中,保持数据的一致性。
优缺点分析:
- AOF文件可以做到秒级持久化,使用追加写的方式来写入,可读性强并且可以使用命令进行文件修复。
- 相比于RDB文件,同样数据下AOF文件体积要大。在redis负载较高时,秒级更新AOF文件会影响性能
(4.3)持久化策略选择
- RDB持久化,安全性较差,它是正常时期数据备份及 master-slave 数据同步的最佳手段,文件尺寸较小并且恢复速度较快。在恢复大数据集时候,RDB方式相对于AOF要快。
- AOF更安全,可将数据及时同步到文件中,但需要较多的磁盘IO,AOF文件尺寸较大,文件内容恢复相对较慢也更加完整。
(5)redis数据的过期回收策略与内存淘汰机制
(5.1)过期回收策略
redis中的数据过期回收策略使用了定期删除和惰性删除相结合的方式。
- 定期删除:
redis会每隔一定的时间去抽查一定量的数据判断其是否过期,过期则进行删除。 - 惰性删除:
在获取一个key的时候,redis会检查这个key是否已经过期,若过期,则会进行删除操作。
(5.2)内存淘汰机制
可以设置内存最大使用量,当内存使用量超出时,会施行数据淘汰策略。
在配置文件中,我们可以对内存淘汰机制进行配置。当内存使用达到最大值时,redis可以使用的清除策略如下:
volatile-lru
:利用LRU算法移除设置过过期时间的key (LRU:最近使用 Least Recently Used )
从已设置过期时间的数据集中挑选最近最少使用的数据淘汰allkeys-lru
:利用LRU算法移除任何key
从所有数据集中挑选最近最少使用的数据淘汰volatile-random
:移除设置过过期时间的随机key
从已设置过期时间的数据集中任意选择数据淘汰allkeys-random
:移除随机key
从所有数据集中任意选择数据进行淘汰volatile-ttl
:移除即将过期的key(minor TTL)
从已设置过期时间的数据集中挑选将要过期的数据淘汰noeviction
:不移除任何key,只是返回一个写错误 ,默认选项
禁止驱逐数据
LRU算法在Java笔记-----(2)Java容器有讲过,查看详情请跳转
作为内存数据库,出于对性能和内存消耗的考虑,Redis 的淘汰算法实际实现上并非针对所有 key,而是抽样一小部
分并且从中选出被淘汰的 key。
使用 Redis 缓存数据时,为了提高缓存命中率,需要保证缓存数据都是热点数据。可以将内存最大使用量设置为热点
数据占用的内存量,然后启用 allkeys-lru 淘汰策略
,将最近最少使用的数据淘汰。
Redis 4.0 引入了 volatile-lfu
和 allkeys-lfu
淘汰策略,LFU 策略通过统计访问频率,将访问频率最少的键值对淘汰。
(6)redis的主从复制机制
当项目比较大的时候,我们可以使用主从架构Master/Slave机制,Master 以写为主,Slave 以读为主,Master 主节点更新后根据配置,自动同步到从机Slave 节点。
主从复制的原理包括旧版同步和命令传播,主从复制的代价就是系统复制较重的时候会导致主从延迟,并且根据CAP理论,无法同时保证服务可用性和数据一致性。
(6.1)分布式CAP定理
SpringCloud-Eureka,Dubbo-Zookeeper
CAP理论是指 当网络分区发生时,一致性C和可用性A不可能同时保证。
- C:Consistent 数据一致性
- A:Availability 服务可用性
- P:Partition tolerance 分区容错性
- 网络分区:分布式系统的节点往往都是分布在不同的机器上进行网络隔离开的,这意味着必然会有网络断开的风险,网络断开也就意味着发生了网络分区。
- 最终一致性:Redis可以保证最终一致性,从节点会努力追赶主节点,最终从节点的状态会和主节点的状态将保持一致。
(7)redis对事务支持
一个事务包含了多个命令,服务器在执行事务期间,不会改去执行其它客户端的命令请求。
事务中的多个命令被一次性发送给服务器,而不是一条一条发送,这种方式被称为流水线,它可以减少客户端与服务器之间的网络通信次数从而提升性能。
Redis 最简单的事务实现方式是使用 MULTI
和 EXEC
命令将事务操作包围起来。
redis对事务的支持主要可以概括如下:
- 隔离性:redis是单进程的程序,保证在执行事务时,不会对事务进行中断,事务可以运行直到执行完所有事务队列中的命令为止。所以redis的事务支持隔离性。
- redis会将一个事务中的所有命令序列化,然后按顺序执行。redis不可能在一个事务的执行过程中插入执行另一个客户端发出的请求。可以保证Redis将这些命令作为一个单独的隔离操作执行。
redis操作事务的相关命令如下所示:
- MULTI:标记一个事务块的开始。
- EXEC:执行所有事务块内的命令。
- DISCARD:取消事务,放弃执行事务块内的所有命令。
- UNWATCH:取消 WATCH 命令对所有 key 的监视。
- WATCH key [key …]:监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断。
需要注意的是redis的事务不支持回滚操作,redis 以 MULTI
开始一个事务,然后将多个命令入队到事务中,最后由 EXEC
命令触发事务, 一并执行事务中的所有命令。
只有当被调用的redis命令有语法错误时,这条命令才会执行失败,或者对某个键执行不符合其数据类型的操作,但是应该在将命令入队列的时候就应该并且能够发现这些问题,所以redis的事务不支持进行回滚操作。
(8)Jedis
一款java操作redis数据库的工具,Jedis可以操作各种redis中的数据结构:
(8.1)Jedis操作字符串类型 string
set
get
//1. 获取连接
//如果使用空参构造,默认值 "localhost", 6379端口
Jedis jedis = new Jedis();
//2. 操作
//存储
jedis.set("username","zhangsan");
//获取
String username = jedis.get("username");
System.out.println(username);
//可以使用setex()方法存储可以指定过期时间的 key value
//将activecode:hehe键值对存入redis,并且20秒后自动删除该键值对
jedis.setex("activecode",20,"hehe");
//3. 关闭连接
jedis.close();
(8.2)Jedis操作哈希类型 hash :map格式
hset
hget
hgetAll
//1. 获取连接
//如果使用空参构造,默认值 "localhost",6379端口
Jedis jedis = new Jedis();
//2. 操作
// 存储hash
jedis.hset("user","name","lisi");
jedis.hset("user","age","23");
jedis.hset("user","gender","female");
// 获取hash
String name = jedis.hget("user", "name");
System.out.println(name);
// 获取hash的所有map中的数据
Map<String, String> user = jedis.hgetAll("user");
// keyset
Set<String> keySet = user.keySet();
for (String key : keySet) {
//获取value
String value = user.get(key);
System.out.println(key + ":" + value);
}
//3. 关闭连接
jedis.close();
(8.3)Jedis操作列表类型 list :linkedlist格式,支持重复元素
lpush / rpush
lpop / rpop
lrange start end : 范围获取
//1. 获取连接
//如果使用空参构造,默认值 "localhost",6379端口
Jedis jedis = new Jedis();
//2. 操作
// list 存储
jedis.lpush("mylist","a","b","c");//从左边存
jedis.rpush("mylist","a","b","c");//从右边存
// list 范围获取
List<String> mylist = jedis.lrange("mylist", 0, -1);
System.out.println(mylist);
// list 弹出
String element1 = jedis.lpop("mylist");//c
System.out.println(element1);
String element2 = jedis.rpop("mylist");//c
System.out.println(element2);
// list 范围获取
List<String> mylist2 = jedis.lrange("mylist", 0, -1);
System.out.println(mylist2);
//3. 关闭连接
jedis.close();
(8.4)Jedis操作集合类型 set : 不允许重复元素
sadd
smembers:获取所有元素
//1. 获取连接
//如果使用空参构造,默认值 "localhost",6379端口
Jedis jedis = new Jedis();
//2. 操作
// set 存储
jedis.sadd("myset","java","php","c++");
// set 获取
Set<String> myset = jedis.smembers("myset");
System.out.println(myset);
//3. 关闭连接
jedis.close();
(8.5)Jedis操作有序集合类型 sortedset:不允许重复元素,且元素有顺序
zadd
zrange
//1. 获取连接
//如果使用空参构造,默认值 "localhost",6379端口
Jedis jedis = new Jedis();
//2. 操作
// sortedset 存储
jedis.zadd("mysortedset",3,"亚瑟");
jedis.zadd("mysortedset",30,"后裔");
jedis.zadd("mysortedset",55,"孙悟空");
// sortedset 获取
Set<String> mysortedset = jedis.zrange("mysortedset", 0, -1);
System.out.println(mysortedset);
//3. 关闭连接
jedis.close();
(8.6)jedis连接池:JedisPool
//0.创建一个配置对象
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(50);
config.setMaxIdle(10);
//1.创建JedisPool连接池对象
JedisPool jedisPool = new JedisPool(config,"localhost",6379);
//2.调用方法 getResource()方法获取Jedis连接
Jedis jedis = jedisPool.getResource();
//3. 使用
jedis.set("hehe","heihei");
//4. 关闭 归还到连接池中
jedis.close();
连接池工具类:
public class JedisPoolUtils {
private static JedisPool jedisPool;
static{
//读取配置文件
InputStream is =
JedisPoolUtils.class.getClassLoader().getResourceAsStream("jedis.properties");
//创建Properties对象
Properties pro = new Properties();
//关联文件
try {
pro.load(is);
} catch (IOException e) {
e.printStackTrace();
}
//获取数据,设置到JedisPoolConfig中
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(Integer.parseInt(pro.getProperty("maxTotal")));
config.setMaxIdle(Integer.parseInt(pro.getProperty("maxIdle")));
//初始化JedisPool
jedisPool = new JedisPool(config,
pro.getProperty("host"),
Integer.parseInt(pro.getProperty("port")));
}
/**
* 获取连接方法
*/
public static Jedis getJedis(){
return jedisPool.getResource();
}
}
(9)Redis实现分布式锁,如何加锁以及解锁
关于分布式锁,为了使得加锁操作具有原子性,不可以使用多条命令来完成,我们可以使用带多个参数的set命令来完成,如下所示:jedis.set(String key, String value, String nxxx, String expx, int time)
- 第一个为
key
,我们使用key来当锁,因为key是唯一的。 - 第二个为
value
,我们传的是requestId,通过给value赋值为requestId,我们就知道这把锁是哪个请求加的了,在解锁的时候就可以有依据。 - 第三个为
nxxx
,这个参数我们填的是NX,意思是SET IF NOT EXIST,即当key不存在时,我们进行set操作;若key已经存在,则不做任何操作; - 第四个为
expx
,这个参数我们传的是PX,意思是我们要给这个key加一个过期的设置,具体时间由第五个参数决定。 - 第五个为
time
,与第四个参数相呼应,代表key的过期时间。
(10)Redis集群中的哨兵模式,主从复制等
待更新