学习视频:【狂神说Java】Redis最新超详细版教程通俗易懂_哔哩哔哩_bilibili
目录
1.为什么用NoSQL
1.1单机MySQL的美好年代
1.2 Memcached(缓存)+ MySQL + 垂直拆分(读写分离)
1.3分表分库 + 水平拆分 + Mysql 集群
1.4现今
1.5为什么用NoSQL?
2.认识NoSQL
NoSQL的特点
NoSQL四大分类
3.认识Redis
Redis能做什么?
4.Redis的基本说明
使用Redis客户端进行连接
为什么redis是单线程
Redis为什么这么快?
5.关于RedisKey的基本命令
6.String字符串类型
7.列表List类型
8.集合Set类型详解
9.哈希Hash集合详解
10.有序集合Zset
11.GEO地理位置详解
12.HyperLogLog
13.BitMap
14.Redis基本的事务操作
15.Redis实现乐观锁
16.Jedis
对key操作的命令
对String操作的命令
对List操作命令
对Set的操作命令
对Hash的操作命令
17.SpringBoot整合Redis
18.自定义RedisTemplate
写一个Redis工具类(直接用RedisTemplate操作Redis,需要很多行代码,因此直接封装好一个 RedisUtils,这样写代码更方便点。这个RedisUtils交给Spring容器实例化,使用时直接注解注入。)
19.Redis.conf配置文件详解
20.持久化之RDB操作
21.持久化之AOF操作
22.Redis发布订阅
23.Redis主从复制
24.哨兵模式详解
25.缓存穿透和雪崩
1.为什么用NoSQL
1.1单机MySQL的美好年代
1.2 Memcached(缓存)+ MySQL + 垂直拆分(读写分离)
1.3分表分库 + 水平拆分 + Mysql 集群
1.4现今
1.5为什么用NoSQL?
今天我们可以通过第三方平台(如:Google , FaceBook 等)可以很容易的访问和抓取数据。用户的个人信息,社交网络,地理位置,用户生成的数据和用户操作日志已经成倍的增加、我们如果要对这些用 户数据进行挖掘,那SQL 数据库已经不适合这些应用了,而 NoSQL 数据库的发展却能很好的处理这些大的数据
2.认识NoSQL
NoSQL的特点
1、易扩展
2、大数据量高性能
3、多样灵活的数据模型
NoSQL四大分类
3.认识Redis
Redis:REmote DIctionary Server(远程字典服务器),是一个基于内存的键值型NoSQL数据库。是完全开源免费的,用C语言编写的,遵守BSD协议,是一个高性能的(Key/Value)分布式内存数据 库,基于内存运行,并支持持久化的NoSQL数据库,是当前最热门的NoSQL数据库之一,也被人们称为数据结构服务器
特征:
- 键值(key-value)型,value支持多种不同数据结构,功能丰富
- 单线程,每个命令具备原子性
- 低延迟,速度快(基于内存、IO多路复用、良好的编码)。
- 支持数据持久化
- 支持主从集群、分片集群
- 支持多语言客户端
-
支持数据的备份
Redis能做什么?
- 发布、订阅消息系统
- 地图信息分析
- 定时器、计数器
4.Redis的基本说明
中间配环境搞了我一上午又回去看linux,我就不展示配置过程了
使用Redis客户端进行连接
redis默认有16个数据库
默认使用的是第0个
可以使用 select 进行切换
清楚当前数据库
为什么redis是单线程
Redis很快!官方表示,因为 Redis 是基于内存的操作, CPU 不是 Redis 的瓶颈, Redis的瓶颈最有可能是机器内存的大小或者网络带宽。既然单线程容易实现,而且 CPU 不会成为瓶颈,那就 顺理成章地采用单线程的方案了!Redis采用的是基于内存的采用的是单进程单线程模型的 KV 数据库,由 C 语言编写,官方提供的数据是可以达到100000+ 的 QPS (每秒内查询次数)。这个数据不比采用单进程多线程的同样基于内存的 KV 数据库 Memcached 差
Redis为什么这么快?
5.关于RedisKey的基本命令
-
keys * 查看所有的 key
-
exists key 的名字,判断某个 key 是否存在
-
move key db ---> 当前库就没有了,被移除了
-
expire key 秒钟:为给定 key 设置生存时间,当 key 过期时 ( 生存时间为 0 ) ,它会被自动删
-
ttl key 查看还有多少秒过期,-1 表示永不过期,-2 表示已过期
6.String字符串类型
127.0.0.1:6379> set key1 v1 # 设置值
OK
127.0.0.1:6379> get key1 # 获得key
"v1"
127.0.0.1:6379> keys * # 查看全部的key
1) "key1"
127.0.0.1:6379> exists key1 # 确保 key1 不存在
(integer) 1
127.0.0.1:6379> append key1 "hello" # 对不存在的 key 进行 APPEND ,等同于 SET
key1 "hello"
(integer) 7
127.0.0.1:6379> get key1
"v1hello"
127.0.0.1:6379> strlen key1 # 获取字符串的长度
(integer) 7
127.0.0.1:6379> append key1 ",kuangshen"
(integer) 17
127.0.0.1:6379> strlen key1
(integer) 17
127.0.0.1:6379> get key1
"v1hello,kuangshen"
127.0.0.1:6379>
127.0.0.1:6379> set views 0 # 设置浏览量为0
OK
127.0.0.1:6379> incr views # 浏览 + 1
(integer) 1
127.0.0.1:6379> incr views # 浏览 + 1
(integer) 2
127.0.0.1:6379> decr views # 浏览 - 1
(integer) 1
127.0.0.1:6379> incrby views 10 # +10
(integer) 11
127.0.0.1:6379> decrby views 10 # -10
(integer) 1
最上面的含义是:设置一个user:1 对象,值为 json字符来保存一个对象!
下面的是 user:{id}:{filed}
就比如 article:1000:views 第1000篇文章的游览量
String数据结构是简单的 key-value 类型, value 其实不仅可以是 String ,也可以是数字。常规 key-value 缓存应用: 常规计数:微博数,粉丝数等。
7.列表List类型
注:lpush是将值放在列表的头部
还有一个 rpush是将值放在列表的尾部
总结:
-
它是一个字符串链表, left , right 都可以插入添加
-
如果key不存在,创建新的链表
-
如果key已存在,新增内容
-
如果值全移除,对应的键也就消失了
-
链表的操作无论是头和尾效率都极高,但假如是对中间元素进行操作,效率就很惨淡了
8.集合Set类型详解
9.哈希Hash集合详解
10.有序集合Zset
11.GEO地理位置详解
官方文档:https://www.redis.net.cn/order/3685.html
语法:geoadd key longitude latitude member ...
# 将给定的空间元素 (纬度、经度、名字) 添加到指定的键里面。# 这些数据会以有序集 he 的形式被储存在键里面,从而使得 georadius 和 georadiusbymember 这样的命令可以在之后通过位置查询取得这些元素。# geoadd 命令以标准的 x,y 格式接受参数 , 所以用户必须先输入经度 , 然后再输入纬度。# geoadd 能够记录的坐标是有限的 : 非常接近两极的区域无法被索引。# 有效的经度介于 -180-180 度之间,有效的纬度介于 -85.05112878 度至 85.05112878 度之间。,当用户尝试输入一个超出范围的经度或者纬度时 ,geoadd 命令将返回一个错误。
(来自官方文档)
geopos
语法:geopos key member [member...]
从key里返回所有给定位置元素的位置(经度和纬度)
语法:geodist key member1 member2 [unit]
返回两个给定位置之间的距离,如果两个位置之间的其中一个不存在 , 那么命令返回空值。# 指定单位的参数 unit 必须是以下单位的其中一个:# m 表示单位为米# km 表示单位为千米# mi 表示单位为英里# ft 表示单位为英尺# 如果用户没有显式地指定单位参数 , 那么 geodist 默认使用米作为单位 。#geodist 命令在计算距离时会假设地球为完美的球形 , 在极限情况下 , 这一假设最大会造成 0.5% 的误差。
语法:georadius key longitude latitude radius m|km|ft|mi [withcoord][withdist]
# 找出位于指定范围内的元素,中心点是由给定的位置元素决定
语法:geohash key member [member...]
12.HyperLogLog
什么是基数:
命令:[PFADD key element [element ...]
13.BitMap
位存储
14.Redis基本的事务操作
Redis单条命令保存原子性,但是事务不保证原子性!
Redis事务的本质:一组命令的集合!一个事务中的所有命令都会被序列化,在事务执行过程中,会按照顺序执行!
--------队列 set set set 执行--------(一次性、顺序性、排他性,执行一些命令)
Redis事务没有隔离级别的概念
所有的命令在事务中,并没有直接被执行!只有发起执行命令的时候才会执行!Exec
Redis的事务:
- 开启事务 multi
- 命令入队
- 执行事务
正常执行:
15.Redis实现乐观锁
测试:
如果开了2个窗口
exec操作之前在第二个窗口
这时候会修改失败(执行之前,另外一个线程,修改了我们的值,这个时候,就会导致事务执行失败!
注:一但执行 EXEC 开启事务的执行后,无论事务使用执行成功, WARCH 对变量的监控都将被取消。 故当事务执行失败后,需重新执行WATCH命令对变量进行监控,并开启新的事务进行操作。
小结:
16.Jedis
我们要使用java来操作Redis
jedis是Redis官方推荐的 java 连接开发工具,使用java操作Redis中间件.
<dependencies>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.2.0</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.58</version>
</dependency>
</dependencies>
public class Ping {
public static void main(String[] args) {
Jedis jedis = new Jedis("127.0.0.1",6379);
System.out.println("连接成功");
//查看服务是否运行
System.out.println("服务正在运行: "+jedis.ping());
}
}
对key操作的命令
public 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','kuangshen'>的键值对:"+jedis.set("username", "kuangshen"));
System.out.println("新增<'password','password'>的键值对:"+jedis.set("password", "password"));
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());
}
}
对String操作的命令
public 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));
}
}
对List操作命令
public 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));
}
}
对Set的操作命令
public 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"));
}
}
对Hash的操作命令
public 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<>();
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"));
}
}
17.SpringBoot整合Redis
spring.redis.host=127.0.0.1
spring.redis.port=6379
@SpringBootTest
class RedisSpringboot01ApplicationTests {
@Autowired
private RedisTemplate redisTemplate;
@Test
void contextLoads() {
//opsForValue 操作字符串 类似于String (ForList 操作list)
redisTemplate.opsForValue().set("key", "value");
System.out.println(redisTemplate.opsForValue().get("key"));
}
}
18.自定义RedisTemplate
@Configuration
public class RedisConfig {
//编写我们自己的redisTemplate
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
//配置具体的序列化方式
template.setKeySerializer(new StringRedisSerializer());
template.setConnectionFactory(redisConnectionFactory);
return template;
}
}
@Configuration
public class RedisConfig {
//自己定义了一个 RedisTemplate
@Bean
@SuppressWarnings("all")public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory
factory) {
//为了开发方便一般直接使用<String,Object>
RedisTemplate<String, Object> template = new RedisTemplate<String,
Object>();
template.setConnectionFactory(factory);
//Json序列化配置
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new
Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
//String 的序列化
StringRedisSerializer stringRedisSerializer = new
StringRedisSerializer();
// key采用String的序列化方式
template.setKeySerializer(stringRedisSerializer);
// hash的key也采用String的序列化方式
template.setHashKeySerializer(stringRedisSerializer);
// value序列化方式采用jackson
template.setValueSerializer(jackson2JsonRedisSerializer);
// hash的value序列化方式采用jackson
template.setHashValueSerializer(jackson2JsonRedisSerializer);
template.afterPropertiesSet();
return template;
}
}
写一个Redis工具类(直接用RedisTemplate操作Redis,需要很多行代码,因此直接封装好一个 RedisUtils,这样写代码更方便点。这个RedisUtils交给Spring容器实例化,使用时直接注解注入。)
@Component
public final class RedisUtil {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
// =============================common============================
/**
* 指定缓存失效时间
* @param key 键
* @param time 时间(秒)
*/
public boolean expire(String key, long time) {
try {if (time > 0) {
redisTemplate.expire(key, time, TimeUnit.SECONDS);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 根据key 获取过期时间
* @param key 键 不能为null
* @return 时间(秒) 返回0代表为永久有效
*/
public long getExpire(String key) {
return redisTemplate.getExpire(key, TimeUnit.SECONDS);
}
/**
* 判断key是否存在
* @param key 键
* @return true 存在 false不存在
*/
public boolean hasKey(String key) {
try {
return redisTemplate.hasKey(key);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 删除缓存
* @param key 可以传一个值 或多个
*/
@SuppressWarnings("unchecked")
public void del(String... key) {
if (key != null && key.length > 0) {
if (key.length == 1) {
redisTemplate.delete(key[0]);
} else {
redisTemplate.delete(CollectionUtils.arrayToList(key));
}
}
}
// ============================String=============================
/**
* 普通缓存获取
* @param key 键
* @return 值
*/public Object get(String key) {
return key == null ? null : redisTemplate.opsForValue().get(key);
}
/**
* 普通缓存放入
* @param key 键
* @param value 值
* @return true成功 false失败
*/
public boolean set(String key, Object value) {
try {
redisTemplate.opsForValue().set(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 普通缓存放入并设置时间
* @param key 键
* @param value 值
* @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期
* @return true成功 false 失败
*/
public boolean set(String key, Object value, long time) {
try {
if (time > 0) {
redisTemplate.opsForValue().set(key, value, time,
TimeUnit.SECONDS);
} else {
set(key, value);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 递增
* @param key 键
* @param delta 要增加几(大于0)
*/
public long incr(String key, long delta) {
if (delta < 0) {
throw new RuntimeException("递增因子必须大于0");
}
return redisTemplate.opsForValue().increment(key, delta);
}/**
* 递减
* @param key 键
* @param delta 要减少几(小于0)
*/
public long decr(String key, long delta) {
if (delta < 0) {
throw new RuntimeException("递减因子必须大于0");
}
return redisTemplate.opsForValue().increment(key, -delta);
}
// ================================Map=================================
/**
* HashGet
* @param key 键 不能为null
* @param item 项 不能为null
*/
public Object hget(String key, String item) {
return redisTemplate.opsForHash().get(key, item);
}
/**
* 获取hashKey对应的所有键值
* @param key 键
* @return 对应的多个键值
*/
public Map<Object, Object> hmget(String key) {
return redisTemplate.opsForHash().entries(key);
}
/**
* HashSet
* @param key 键
* @param map 对应多个键值
*/
public boolean hmset(String key, Map<String, Object> map) {
try {
redisTemplate.opsForHash().putAll(key, map);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* HashSet 并设置时间
* @param key 键
* @param map 对应多个键值
* @param time 时间(秒)
* @return true成功 false失败
*/
public boolean hmset(String key, Map<String, Object> map, long time) {try {
redisTemplate.opsForHash().putAll(key, map);
if (time > 0) {
expire(key, time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 向一张hash表中放入数据,如果不存在将创建
*
* @param key 键
* @param item 项
* @param value 值
* @return true 成功 false失败
*/
public boolean hset(String key, String item, Object value) {
try {
redisTemplate.opsForHash().put(key, item, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 向一张hash表中放入数据,如果不存在将创建
*
* @param key 键
* @param item 项
* @param value 值
* @param time 时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间
* @return true 成功 false失败
*/
public boolean hset(String key, String item, Object value, long time) {
try {
redisTemplate.opsForHash().put(key, item, value);
if (time > 0) {
expire(key, time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 删除hash表中的值
*
* @param key 键 不能为null* @param item 项 可以使多个 不能为null
*/
public void hdel(String key, Object... item) {
redisTemplate.opsForHash().delete(key, item);
}
/**
* 判断hash表中是否有该项的值
*
* @param key 键 不能为null
* @param item 项 不能为null
* @return true 存在 false不存在
*/
public boolean hHasKey(String key, String item) {
return redisTemplate.opsForHash().hasKey(key, item);
}
/**
* hash递增 如果不存在,就会创建一个 并把新增后的值返回
*
* @param key 键
* @param item 项
* @param by 要增加几(大于0)
*/
public double hincr(String key, String item, double by) {
return redisTemplate.opsForHash().increment(key, item, by);
}
/**
* hash递减
*
* @param key 键
* @param item 项
* @param by 要减少记(小于0)
*/
public double hdecr(String key, String item, double by) {
return redisTemplate.opsForHash().increment(key, item, -by);
}
// ============================set=============================
/**
* 根据key获取Set中的所有值
* @param key 键
*/
public Set<Object> sGet(String key) {
try {
return redisTemplate.opsForSet().members(key);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}/**
* 根据value从一个set中查询,是否存在
*
* @param key 键
* @param value 值
* @return true 存在 false不存在
*/
public boolean sHasKey(String key, Object value) {
try {
return redisTemplate.opsForSet().isMember(key, value);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将数据放入set缓存
*
* @param key 键
* @param values 值 可以是多个
* @return 成功个数
*/
public long sSet(String key, Object... values) {
try {
return redisTemplate.opsForSet().add(key, values);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 将set数据放入缓存
*
* @param key 键
* @param time 时间(秒)
* @param values 值 可以是多个
* @return 成功个数
*/
public long sSetAndTime(String key, long time, Object... values) {
try {
Long count = redisTemplate.opsForSet().add(key, values);
if (time > 0)
expire(key, time);
return count;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 获取set缓存的长度*
* @param key 键
*/
public long sGetSetSize(String key) {
try {
return redisTemplate.opsForSet().size(key);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 移除值为value的
*
* @param key 键
* @param values 值 可以是多个
* @return 移除的个数
*/
public long setRemove(String key, Object... values) {
try {
Long count = redisTemplate.opsForSet().remove(key, values);
return count;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
// ===============================list=================================
/**
* 获取list缓存的内容
*
* @param key 键
* @param start 开始
* @param end 结束 0 到 -1代表所有值
*/
public List<Object> lGet(String key, long start, long end) {
try {
return redisTemplate.opsForList().range(key, start, end);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 获取list缓存的长度
*
* @param key 键
*/
public long lGetListSize(String key) {
try {
return redisTemplate.opsForList().size(key);} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 通过索引 获取list中的值
*
* @param key 键
* @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0
时,-1,表尾,-2倒数第二个元素,依次类推
*/
public Object lGetIndex(String key, long index) {
try {
return redisTemplate.opsForList().index(key, index);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 将list放入缓存
*
* @param key 键
* @param value 值
*/
public boolean lSet(String key, Object value) {
try {
redisTemplate.opsForList().rightPush(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将list放入缓存
* @param key 键
* @param value 值
* @param time 时间(秒)
*/
public boolean lSet(String key, Object value, long time) {
try {
redisTemplate.opsForList().rightPush(key, value);
if (time > 0)
expire(key, time);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}}
/**
* 将list放入缓存
*
* @param key 键
* @param value 值
* @return
*/
public boolean lSet(String key, List<Object> value) {
try {
redisTemplate.opsForList().rightPushAll(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将list放入缓存
*
* @param key 键
* @param value 值
* @param time 时间(秒)
* @return
*/
public boolean lSet(String key, List<Object> value, long time) {
try {
redisTemplate.opsForList().rightPushAll(key, value);
if (time > 0)
expire(key, time);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 根据索引修改list中的某条数据
*
* @param key 键
* @param index 索引
* @param value 值
* @return
*/
public boolean lUpdateIndex(String key, long index, Object value) {
try {
redisTemplate.opsForList().set(key, index, value);
ren true;
} catch (Exception e) {
e.printStackTrace();
return false;}
}
/**
* 移除N个值为value
*
* @param key 键
* @param count 移除多少个
* @param value 值
* @return 移除的个数
*/
public long lRemove(String key, long count, Object value) {
try {
Long remove = redisTemplate.opsForList().remove(key, count,
value);
return remove;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
}
19.Redis.conf配置文件详解
最后一行:对大小写不敏感
20.持久化之RDB操作
触发机制:
1.save的规则满足的情况下,会自动触发rdb规则
2.执行 flushall 命令,也会触发
3.退出redis,也会产生 rdb 文件!
备份就自动生成一个 dump.rdb
几乎它自己默认的配置就够用了,但是我们还是需要学习
21.持久化之AOF操作
将所有命令都执行下来,history,恢复的时候就把这个文件全部在执行一遍.只许追加文件
默认是不开启的,我们需要手动进行配置
1.每一次修改都同步,文件的完整会更加好
2.每秒同步一次,可能会丢失一秒的数据
3.从不同步,效率最高的!
缺点:
1.相对于数据文件来说,aof远远大于rdb,修复的速度也比rdb慢
2.Aof运行效率也要比rdb慢,所以我们redis默认的配置就是rdb持久化
如果 aof 文件大于64m,太大了!fork一个新的进程来将我们的文件进行重写!
扩展:
1 、 RDB 持久化方式能够在指定的时间间隔内对你的数据进行快照存储2 、 AOF 持久化方式记录每次对服务器写的操作,当服务器重启的时候会重新执行这些命令来恢复原始的数据,AOF 命令以 Redis 协议追加保存每次写的操作到文件末尾, Redis还能对AOF文件进行后台重 写,使得AOF 文件的体积不至于过大。3 、只做缓存,如果你只希望你的数据在服务器运行的时候存在,你也可以不使用任何持久化4 、同时开启两种持久化方式在这种情况下,当 redis 重启的时候会优先载入 AOF 文件来恢复原始的数据,因为在通常情况下 AOF文件保存的数据集要比 RDB 文件保存的数据集要完整。RDB 的数据不实时,同时使用两者时服务器重启也只会找 AOF 文件,那要不要只使用 AOF 呢?建议不要,因为RDB 更适合用于备份数据库( AOF 在不断变化不好备份),快速重启,而且不会有AOF可能潜在的 Bug ,留着作为一个万一的手段。5 、性能建议因为 RDB 文件只用作后备用途,建议只在 Slave 上持久化 RDB 文件,而且只要 15 分钟备份一次就够了,只保留 save 900 1 这条规则。如果 Enable AOF ,好处是在最恶劣情况下也只会丢失不超过两秒数据,启动脚本较简单只 load 自己的AOF 文件就可以了,代价一是带来了持续的 IO ,二是 AOF rewrite 的最后将 rewrite 过程中产 生的新数据写到新文件造成的阻塞几乎是不可避免的。只要硬盘许可,应该尽量减少AOF rewrite的频率, AOF 重写的基础大小默认值 64M 太小了,可以设到 5G 以上,默认超过原大小 100% 大小重 写可以改到适当的数值。如果不 Enable AOF ,仅靠 Master-Slave Repllcation 实现高可用性也可以,能省掉一大笔 IO ,也减少了rewrite 时带来的系统波动。代价是如果 Master/Slave 同时断点,会丢失十几分钟的数据, 启动脚本也要比较两个 Master/Slave 中的 RDB 文件,载入较新的那个,微博就是这种架构。
22.Redis发布订阅
比如发布公众号的信息,这个信息到队列里,然后订阅公众号的人都能接收
微信:
23.Redis主从复制
概念:主从复制,是指将一台Redis服务器的数据,复制到其他的Redis服务器。前者称为主节点
主从复制,读写分离!80%的情况下都是在进行读操作!减缓服务器的压力!架构中经常使用!一主二从!
环境配置
默认情况下,每台Redis服务器都是主节点
测试:主机断开连接,从机依旧连接到主机,但是没用写操作,这个时候,主机如果回来了,从机依旧可以直接获取到主机写的信息!
如 果是使用命令行,来配置的主从,这个时候如果重启了,就会变回主机!只要变回从机,立马就会从主机中获取值
层层链路
24.哨兵模式详解
概述:主从切换技术的方法是:当主服务器宕机后,需要手动把一台从服务器切换为主服务器,这就需要人工干预,费事费力,还会造成一段时间内服务不可用。这不是一种推荐的方式,更多时候,我们优先考虑哨兵模式。Redis从2.8开始正式提供了Sentinel(哨兵) 架构来解决这个问题。
- sentinel monitor 被监控主机名字 127.0.0.1 6379 1
- 上面最后一个数字1,表示主机挂掉后slave投票看让谁接替成为主机,得票数多少后成为主机
25.缓存穿透和雪崩
概念
加油,共勉!