Redis概述
Redis特点
- Redis:全称: Remote Dictionary Service:远程字典服务, Redis是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库[NoSQL],并提供多种语言的API。实际开发中,主要时用来做缓存,加快查询效率。
- 和Memcached类似,它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)、zset(sorted set –有序集合)和hash(哈希类型)。
- 这些数据类型都支持push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。
- 在此基础上,Redis支持各种不同方式的排序。
- 与memcached一样,为了保证效率,数据都是缓存在内存中。
- 区别的是Redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件。
- 并且在此基础上实现了master-slave(主从)同步。
Redis应用场景
- 高频次,热门访问的数据,降低数据库IO
- 分布式架构,做session共享
Redis安装
Docker安装
docker run -d --name redis -p 6379:6379 --restart always redis:7.2.1 |
Redis数据类型
Redis常用命令
命令 | 功能描述 |
---|---|
keys * |
查看当前库所有key |
exists key |
判断某个key是否存在 |
type key |
查看key的类型 |
del key |
删除指定的key数据 |
unlink key |
根据value选择非阻塞删除,仅将keys从keyspace元数据中删除,真正的删除会在后续异步操作。 |
expire key 10 |
为给定的key设置过期时间(10秒钟) |
ttl key |
查看还有多少秒过期,-1表示永不过期,-2表示已过期 |
select |
切换数据库 |
dbsize |
查看当前数据库的key的数量 |
flushdb |
清空当前库 |
flushall |
通杀全部库 |
Redis字符串
简介
String是Redis最基本的类型,你可以理解成与Memcached一模一样的类型,一个key对应一个value。
String类型是二进制安全的。意味着Redis的string可以包含任何数据。比如jpg图片或者序列化的对象。
String类型是Redis最基本的数据类型,一个Redis中字符串value最多可以是512M
常用命令
以下是Redis字符串常用命令使用Markdown表格展示:
命令 | 描述 | 语法 |
---|---|---|
SET |
将字符串值value设置到key中,如果key已存在,后放的值会把前放的值覆盖掉。 | SET key value |
DECR |
将 key 中储存的数字值减1,如果key不存在,则key的值先被初始化为0再执行decr操作。 | DECR key |
INCRBY |
将 key 所储存的值加上增量值,如果key不存在,则key的值先被初始化为0再执行INCRBY命令。 | INCRBY key offset |
GET |
获取存储在key中的字符串值;如果键不存在,返回一个空值nil。 | GET key |
MSET |
同时设置多个键值对;成功设置一个键值对后,返回OK;否则返回一个错误。 | MSET key1 value1 |
Redis列表
简介
Redis 列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)。
它的底层实际是个双向链表,对两端的操作性能很高,通过索引下标的操作中间的节点性能会较差。
常用命令
命令 | 描述 | 语法 |
---|---|---|
LPUSH |
将一个或多个值插入到列表的头部(左边) | LPUSH key value1 [value2 …] |
RPUSH |
将一个或多个值插入到列表的尾部(右边) | RPUSH key value1 [value2 …] |
LPOP |
移除并获取列表的第一个元素(头部) | LPOP key |
RPOP |
移除并获取列表的最后一个元素(尾部) | RPOP key |
LRANGE |
获取列表指定范围内的所有元素(子列表) | LRANGE key start stop [WITHSCORES] |
LSET |
设置列表中指定索引处的元素值(覆盖) | LSET key index value |
LREM |
移除列表中指定值的元素(数量) | LREM key count value |
Redis集合
简介
Redis set对外提供的功能与list类似是一个列表的功能,特殊之处在于set是可以自动排重的,当你需要存储一个列表数据,又不希望出现重复数据时,set是一个很好的选择,并且set提供了判断某个成员是否在一个set集合内的重要接口,这个也是list所不能提供的。
Redis的Set是string类型的无序集合。它底层其实是一个value为null的hash表,所以添加,删除,查找的复杂度都是O(1)。
一个算法,随着数据的增加,执行时间的长短,如果是O(1),数据增加,查找数据的时间不变
常用命令
命令 | 描述 | 语法 |
---|---|---|
SADD |
向集合中添加一个或多个成员 | SADD key member [member…] |
SMEMBERS |
获取集合中的所有成员 | SMEMBERS key |
SISMEMBER |
检查成员是否集合的成员 | SISMEMBER key member |
SCARD |
获取集合中的成员数 | SCARD key |
SREM |
从集合中移除一个或多个成员 | SREM key member [member…] |
Redis哈希
简介
Redis hash 是一个键值对集合。
Redis hash是一个string类型的field和value的映射表,hash特别适合用于存储对象。
类似Java里面的Map<String,Object>
用户ID为查找的key,存储的value用户对象包含姓名,年龄,生日等信息。
常用命令
以下是Redis哈希常用命令使用Markdown表格展示:
命令 | 描述 | 语法 |
---|---|---|
HSET |
在哈希表的 key 中设置一个键值对。如果 key 已经存在,那么将会覆盖旧的值。 | HSET key field value |
HGET |
获取哈希表 key 中的 field 的值。如果 field 不存在,则返回 nil。 | HGET key field |
HGETALL |
获取哈希表 key 中的所有字段和值。如果 key 不存在,返回空列表。 | HGETALL key |
Redis有序集合
简介
Redis有序集合zset与普通集合set非常相似,是一个没有重复元素的字符串集合。
不同之处是有序集合的每个成员都关联了一个评分(score),这个评分(score)被用来按照从最低分到最高分的方式排序集合中的成员。集合的成员是唯一的,但是评分可以是重复了 。
因为元素是有序的, 所以你也可以很快的根据评分(score)或者次序(position)来获取一个范围的元素。
访问有序集合的中间元素也是非常快的,因此你能够使用有序集合作为一个没有重复成员的智能列表。
常用命令
以下是Redis有序集合常用命令使用Markdown表格展示:
命令 | 描述 | 语法 |
---|---|---|
ZADD |
向有序集合添加一个或多个成员,或者更新已存在成员的分数。 | ZADD key score member [score member…] |
ZCARD |
获取有序集合的成员数。 | ZCARD key |
ZCOUNT |
计算在有序集合中指定区间分数的成员数。 | ZCOUNT key min max |
ZINCRBY |
对有序集合中的指定成员的分数加上增量 increment 。 | ZINCRBY key increment member |
Redis订阅和发布
什么是订阅和发布
Redis发布订阅pub/sub是一种消息通信模式:发送者pub发送消息,订阅者sub接收消息。Redis客户端可以订阅任意数量的频道。
订阅和发布的实现
打开第一个客户端设置为订阅
127.0.0.1:6379> SUBSCRIBE channel1 |
打开第二个客户端给channel1发布消息
127.0.0.1:6379> PUBLISH channel1 hello,world! |
在第一个客户端查看接受到的消息
127.0.0.1:6379> SUBSCRIBE channel1 |
Redis事务和锁机制
Redis事务
Redis事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。 Redis事务的主要作用就是串联多个命令防止别的命令插队。
从输入Multi命令开始,输入的命令都会依次进入命令队列中,但不会执行,直到输入Exec后,Redis会将之前的命令队列中的命令依次执行。 组队的过程中可以通过discard来放弃组队。
案例
组队成功,提交成功
127.0.0.1:6379> MULTI |
组队阶段报错,提交失败。组队中某个命令出现了报告错误,执行时整个的所有队列都会被取消。
127.0.0.1:6379(TX)> set m1 v1 |
Redis事务特性
单独的隔离操作
事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
没有隔离级别的概念
队列中的命令没有提交之前都不会实际被执行,因为事务提交前任何指令都不会被实际执行
不保证原子性
事务中如果有一条命令执行失败,其后的命令仍然会被执行,没有回滚
Redis锁
悲观锁
悲观锁(Pessimistic Lock), 顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。
乐观锁
乐观锁(Optimistic Lock), 顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量。Redis就是利用这种check-and-set机制实现事务的。
Watch Key
在执行multi之前,先执行watch key1 [key2]
,可以监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断。
Unwatch
取消 WATCH 命令对所有 key 的监视。 如果在执行 WATCH 命令之后,EXEC 命令或DISCARD 命令先被执行了的话,那么就不需要再执行UNWATCH 了。
Redis持久化存储
Redis是一个高性能的键值对(key-value)存储系统,它通常被用于缓存和会话管理等功能。然而,Redis并不提供持久化存储机制,这意味着在系统重启或发生故障时,所有存储的数据都会丢失。为了解决这个问题,Redis提供了两种持久化存储方法:RDB(Redis DataBase)和AOF(Append Only File)。
RDB
RDB持久化是通过生成数据快照(Snapshot)的方式来保存数据。Redis会在指定的时间间隔内,将内存中的数据生成一个二进制文件,通常是一个名为dump.rdb的文件。这个文件是一个完整的数据快照,可以用来备份和数据恢复。
优点
RDB文件紧凑,节省磁盘空间。
RDB恢复数据速度快,因为直接加载快照文件到内存。
RDB适合用于定期备份,以减少数据丢失的风险。
缺点
RDB是定时持久化,可能会导致一定程度的数据丢失。
在大数据量的情况下,生成快照会占用较多的CPU和内存资源。
AOF
AOF持久化是通过记录Redis的所有写操作命令到一个追加日志文件(Append Only File)的方式来保存数据。当Redis重启时,会通过回放这些写操作命令来恢复数据。AOF持久化可以配置不同的同步策略,如每秒同步、每写操作同步或根据需要同步。
优点
AOF持久化提供了更高的数据安全性,因为它记录了所有的写操作。
AOF文件易于理解和解析,方便数据恢复。
可以通过配置同步策略来平衡数据安全性和性能。
缺点
AOF文件通常比RDB文件更大,因为它记录了所有的写操作命令。
AOF恢复数据速度相对较慢,因为需要回放所有的写操作命令。
总结
- 如果对数据不敏感,可以选单独用RDB。
- 不建议单独用 AOF,因为可能会出现Bug。
- 如果只是做纯内存缓存,可以都不用。
Java使用Redis
搭建maven工程
创建一个测试maven工程,pom.xml中添加jar依赖创建Demo,代码如下<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>5.0.1</version>
</dependency>package com.acaiblog;
import redis.clients.jedis.Jedis;
public class Demo {
public static void main(String[] args){
Jedis jedis = new Jedis("127.0.0.1",6379);
String pong = jedis.ping();
System.out.printf(String.format("Redis连接成功: %s",pong));
}
}Jedis API
key
jedis.set("k1", "v1");
jedis.set("k2", "v2");
jedis.set("k3", "v3");
Set<String> keys = jedis.keys("*");
System.out.println(keys.size());
for (String key : keys) {
System.out.println(key);
}
System.out.println(jedis.exists("k1"));
System.out.println(jedis.ttl("k1"));
System.out.println(jedis.get("k1"));String
jedis.mset("str1","v1","str2","v2","str3","v3");
System.out.println(jedis.mget("str1","str2","str3"));List
List<String> list = jedis.lrange("mylist",0,-1);
for (String element : list) {
System.out.println(element);
}Set
jedis.sadd("orders", "order01");
jedis.sadd("orders", "order02");
jedis.sadd("orders", "order03");
jedis.sadd("orders", "order04");
Set<String> smembers = jedis.smembers("orders");
for (String order : smembers) {
System.out.println(order);
}
jedis.srem("orders", "order02");Hash
jedis.hset("hash1","userName","lisi");
System.out.println(jedis.hget("hash1","userName"));
Map<String,String> map = new HashMap<String,String>();
map.put("telphone","13810169999");
map.put("address","atguigu");
map.put("email","abc@163.com");
jedis.hmset("hash2",map);
List<String> result = jedis.hmget("hash2", "telphone","email");
for (String element : result) {
System.out.println(element);
}Zset
jedis.zadd("zset01", 100d, "z3");
jedis.zadd("zset01", 90d, "l4");
jedis.zadd("zset01", 80d, "w5");
jedis.zadd("zset01", 70d, "z6");
Set<String> zrange = jedis.zrange("zset01", 0, -1);
for (String e : zrange) {
System.out.println(e);
}