第一章=>
1、Redis概述
2、Mysql的演进
3、当今企业的架构分析
4、到底什么是nosql
5、阿里巴巴数据架构演进
第二章=>
6、NoSql四大分类
7、Redis概述
8、Windows安装redis
9、Linux安装Redis
10、Redis-benchmark性能测试
第三章=>
11、Redis基本说明
12、RedisKey命令
13、String字符串类型
14、List列表类型
15、Set集合类型
第四章=>
16、Hash类型
17、Zset有序集合
18、Geospatial地理位置
19、Hyperloglog技术统计
20、Bitmap位图场景
第五章=>
21、Redis事务
22、Redis乐观锁
23、Jedis操作Redis
24、Jedis事务
25、Springboot整合Redis
第六章=>
26、RedisTemlate
27、Redis配置文件
28、持久化RDB
29、持久化AOF
30、Redis订阅发布
第七章=>
31、Redis集群
32、主从复制
33、宕机后手动配置主机
34、哨兵模式
35、缓存穿透和雪崩
36、小结与拓展
**************************************************************************************************************************************************************
1、Redis概述
【1】学习方式:上手就用。基本的理论学习,融会贯通(推荐这个)。
【2】兴趣是最好的老师
【3】Nosql、五大数据类型、Redis配置和持久化
**************************************************************************************************************************************************************
2、单机Mysql的演进
【1】为什么要用Nosql?
2020年大数据时代,一般的数据库已经无法处理
【2】适者生存,学习一个永久的技术
【3】单机Mysql的年代
【4】APP---DAL---Mysql
【5】静态网页,Hao123,Html,服务器根本没有太多的压力
【6】瓶颈在哪里?
数据量太大,一个机器放不下
*****************************
数据的索引B+Tree,一个机器的内存也放不下
*****************************
访问量(读写混合),一个服务器承受不了~
*****************************
只要你开始出现以上三种情况之一,那么你就必须要晋级!!!!!
【7】缓存+mysql+垂直拆分(读写分离)
网站80%的情况都是在读,如果去查询数据库就会比较麻烦!!!
我们希望减轻服务器眼里,我们可以使用缓存来保证效率!!!!
【8】Cache
发展过程:优化数据库结构和索引--->文件缓存(IO)--->Memcached(当时最热门的技术)
**************************************************************************************************************************************************************
3、当今的企业架构分析
【1】分库分表+水平拆分+Mysql集群
一主二从
*****************************
后端越来越牛批
技术和业务发展的同时,对人的要求也越来越高!
【2】本质:数据库的读写
早些年,MyISAM:表锁
转战Innodb:行锁
慢慢使用分库分表来解决写的压力
Mysql推出了表分区(并没有多少公司用)
mysql集群,很好的满足了年代的所有需求
【3】最近的年代
2010-2020十年之间,世界发生了翻天覆地的变化;
定位也是一种数据、音乐、热榜
mysql等数据库就不够用了,数据量大,变化很快!
图型、JSON
mysql来存储一些比较大的文件,博客,图片!数据库表很大,效率就低!
如果有一种数据库来专门处理这种数据,mysql的压力就变小了
研究如何处理这些问题。
大数据IO压力下,表几乎更大,表几乎没法更大。
【4】互联网的架构模型 狂神还是可以的
【5】为什么用nosql
用户个人信息、社交网络、地理位置。用户自己产生的数据,用户日志等爆发式增长!
这时候我们就需要使用nosql数据库了,nosql可以很好的处理以上的情况。
**************************************************************************************************************************************************************
4、到底什么是nosql
【1】nosql 是指not only sql 不仅仅是sql
泛指非关系型数据库,随着web2.0的诞生,传统的数据库很难对付web2.0时代!
尤其是超大规模的高并发的社区!站长!
暴露出来很多问题,nosql在当今大数据环境下发展的十分迅速。
很多数据类型的结构不固定,存储不需要一个固定的格式,不需要多个操作就可以横向
扩展!Map<String,Object>
【2】nosql的特点
方便扩展,数据之间没有关系,很好扩展!
大数据量高性能(reids一秒写8万次,读取11万)!nosql的缓存记录级别是一种细粒度
的缓存,性能会比较高!
数据类型是多样型的,不需要事先设计数据库,随取随用,如果数据量十分大的表,很多人
就无法设计了。
传统的RDBMS和nosql。
nosql:不仅仅是数据、没有固定的查询语言、键值对存储、文档存储、图形数据库、
最终一致性、CAP定理和BASE(异地多活)、高性能、高可用、高可扩展。
【3】3V+3高
3V:
海量Volume、多样Variety、实时Velocity
**********************
3高
高并发、高可扩、高性能
【4】真正在公司中的实践:nosql+rdbms一起使用才是最强的。
技术没有高低之分,就看你如何去使用!!!提升内功,思维的提高!!!
**************************************************************************************************************************************************************
5、阿里巴巴数据架构演进
【1】淘宝的数据难道都是在一个数据库里的吗?
【2】阿里架构历程概述
1999年 Perl CGI Oracle
2000年进入java时代
2001-2004 EJB
2005-2007 spring+ibatis+webx
2008-2009 Memcached集群,mysql+数据切分,分布式存储Hadoop
2010 安全镜像,nosql 秒杀 SSD
技术急不得,越是慢慢学,才能越扎实
【3】第五代网站架构的使命
敏捷开发、开放、体验
开源才是技术的王道
大量的公司做同样的业务,随着这样的竞争,业务是越来越完善的
【4】没有什么是加一层解决不了的
商品的基本信息,可以用关系话数据库
商品的描述、评论(文字比较多):文档型数据库、MongDB
图片:分布式软件系统 FastDFS 阿里云的OSS
商品的关键字:搜索引擎 solr elasticsearch Isearch
商品的热门的波段信息:内存数据库、Redis Tair、Memache...
商品的交易,外部的支付接口:三方应用
【5】大型互联网应用问题:
数据类型太多了
数据源繁多,经常重构
数据要改造,大面积改造?
【6】解决问题:所有的经验来源于实战!!!!!!!!!!!太对了,卧槽!!!!
统一的数据服务层UDSL
UDSL:热点缓存 设计
**************************************************************************************************************************************************************
6、NoSql四大分类
【1】KV键值对
新浪Redis
美团Redis+Tair
阿里百度 Redis+Memecache
【2】文档型数据库(bson格式和json一样)
MongoDB基于分布式文件存储的数据库,用来处理大量的文档!
MongoDB是一个介于关系型数据库和非关系型数据库中间的产品!
ConthDB
【3】列存储数据库
HBase
分布式文件系统
【4】图形关系数据库
存关系的数据库,它并不是存图形。比如社交网络、广告推荐
Neo4j InfoGrid
【5】敬畏之心可以使人进步,宇宙是无限的,科幻是玄妙的!
努力学习、不断学习、才能跟上社会的步伐
**************************************************************************************************************************************************************
7、Redis概述
【1】Remote Dictionary Server远程字典服务,简称Redis
【2】能干什么?
内存存储、持久化(rdb aof)
效率高,可以用于高速缓存
发布订阅系统
地图信息分析
计时器、计数器(浏览量)
【3】特性
多样的数据类型
持久化
集群
事务
【4】所需材料
Redis官网 https://redis.io
Redis都推荐在Linux服务器上搭建,我们是基于Linux学习
**************************************************************************************************************************************************************
8、Windows安装redis
【1】Redis很小5M
【2】双击运行Redis服务即可
【3】端口是6379
**************************************************************************************************************************************************************
9、Linux安装Redis
【1】下载安装包redis-6.0.6.tar.gz
【2】解压,进入解压后的文件redis.conf
【3】基本环境安装yum install gcc-c++
【4】make && make install(如果报错)[root@localhost redis-6.0.1]# gcc -v # 查看gcc版本
[root@localhost redis-6.0.1]# yum -y install centos-release-scl # 升级到9.1版本
[root@localhost redis-6.0.1]# yum -y install devtoolset-9-gcc devtoolset-9-gcc-c++ devtoolset-9-binutils
[root@localhost redis-6.0.1]# scl enable devtoolset-9 bash
以上为临时启用,如果要长期使用gcc 9.1的话:
[root@localhost redis-6.0.1]# echo "source /opt/rh/devtoolset-9/enable" >>/etc/profile
------------------------------------------------
版权声明:本文为CSDN博主「InTheWind1115」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_44927585/article/details/108119203
【5】redis默认安装路径在/usr/目录下
和windows的目录一样,/usr/local/bin
【6】把redis.conf拷贝到当前目录
cp /opt/redis/redis-6.0.6/redis.conf a_my_config
之后就使用这个配置文件
【7】启动,redis默认不是后台启动
daemonize yes即可
【8】通过指定的配置文件启动服务
redis-server a_my_config/redis.conf #启动redis
进行连接测试,set a b即可设置key value
redis-cli -p 6379进行访问
redis-cli shutdown # 停止redis
**************************************************************************************************************************************************************
10、Redis-benchmark性能测试
【1】测试100并发10万个请求
redis-benchmark -h localhost -p 6379 -c 100 -n 100000
【2】能够还看到多少秒内处理完毕所有请求
**************************************************************************************************************************************************************
11、Redis基本说明
【1】Redis默认是16个数据库,默认使用的是第0个
select xxx 可以切换数据库
DBSIZE 查看数据库大小
keys * 查看所有的key
【2】为什么redis端口号是6379
【3】Redis是单线程的,Redis是根据机器的内存和网络带宽决定瓶颈的,可以用单线程来做。
【4】为什么单线程还更快?
redis是C语言写的,官方提供的数据为10Wqps/秒,不比Memechche差!
********************************
误区一:高性能的服务器一定是多线程的
误区二:多线程一定比单线程效率高
CPU>内存>硬盘速度
核心:redis所有的数据全部放在内存中,所以使用单线程是效率最高的。
多线程CPU会有上下文切换,耗时的操作!对于内存系统,没有CPU上下文切换,效率是最高的!
**************************************************************************************************************************************************************
12、RedisKey命令
【1】五大数据类型(他可以用于数据库、缓存、中间件)
【2】keys *
EXISTS a
EXISTS b
move xxx 1
EXPIRE xxx 10 秒10后自动过期
**************************************************************************************************************************************************************
13、String字符串类型
【1】90%的JAVA程序员使用redis,仅用string类型
【2】常用命令
STRLEN xxx 长度
APPEN xxx YYY 拼接字符串
get xxx查看key值
set xxx yyy 设置值
incr xxx 给指定给key+1
decr xxx 给指定的key-1
INCRBY xxx 10
DECRBY xxx 10
***********************************************
字符串范围
GETRANGE xxx 0 3 获取指定key的和指定范围
GETRANGE xxx 0 -1全部的字符串
SETRANGE xxx 1 yyy 从指定位置修改
setex 设置过期时间
setnx 不存在则设置
***********************************************
mset k1 v1 k2 v2 可以一次性设置多个值
mget k1 k2 k3
msetnx ... 批量设置,不存在的key存储
***********************************************
set user:1 {xxxx:xxx,xxx:xxx} 设置usre:1对象
***********************************************
getset 先获取再设置
【3】作用设置计数器统计多单位的数量,根据uid统计关注数量粉丝数对象缓存的存储
**************************************************************************************************************************************************************
14、List列表类型
【1】基本的数据列表
redis里,list可以完成栈、队列、阻塞队列!
LPUSH list 1
LPUSH list 2
LPUSH list 3
LRANGE list 0 -1 这个范围可以指定
RPUSH 从右侧添加
【2】redis不区分大小写命令
LPOP 从左侧移除
RPOP 从右侧移除
*******************************************
ltrim mylist 1 2通过下标截取list长度,会改变原有key的值
lset
linsert
lrange
【3】list实际上是一个链表,before node after left right都可以插入值
如果key不存在,创建新的链表
如果key存在,新增内容
如果移除了所有值,空链表也代表不存在!
两遍插入或者改动值,效率最高
消息队列,从左边进,右边拿。
**************************************************************************************************************************************************************
15、Set集合类型
【1】set中的值是不能重复的
【2】常用set命令
sadd myset hello
SMEMBERS myset
SISMEMBER myset hello
SISMEMBER myset world
SRANDOMEMBER myset 随机抽取一个元素
【3】删除指定key,随机删除key
【4】共同关注(并集)
**************************************************************************************************************************************************************
16、Hash类型
【1】Map集合,key-Map集合
flushdb
【2】常见命令
hset myhash key1 value1
hget myhash key1
hmset 批量设置
hmget 批量获取
hgetall myhash
【3】hash本质和string类型没有太多区别,还是一个简单的key value
**************************************************************************************************************************************************************
17、Zset有序集合
【1】在set基础上,加了一个值
zset k1 v1 score1
ZRANGESCORE salary -inf +inf withscores 显示全部用户带成绩
【2】TopN
**************************************************************************************************************************************************************
18、Geospatial地理位置
【1】朋友圈距离、附近的人、打车距离
GEOADD china:city 117 31 hefei
GEOADD china:city 121 31 shanghai
GEODIST chian:city hefei shanghai
geohash china:city xxx
**************************************************************************************************************************************************************
19、Hyperloglog技术统计
【1】什么是基数
A {1 3 5 7 8 7}
B {1 3 5 7 8}
总计不重复的元素是5个
【2】数据结构,统计网页UV
传统的方式:set保存用户的id,这种方式存储大量id,会比较麻烦!
Hyperloglog有0.81%的错误率
PFadd xxx xxx
PFCOUNT
**************************************************************************************************************************************************************
20、Bitmap位图场景
【1】位存储
统计疫情人数:0 1 0 1 0
统计用户信息:活跃,不活跃。登录、未登录!打卡365打卡
setbit sign 0 1
setbit sign 1 0
**************************************************************************************************************************************************************
21、Redis事务
【1】原子性:要么同时成功,要么同时失败
Redis是单条命令是保证原子性的
但是Redis事务是不保证原子性的
【2】事务的本质是:一组命令的集合!!!
一次性、顺序性、排他性!!!
【3】Redis事务分为3个阶段
开启事务(multi)
命令入队(set k1 v1...)
执行事务(exec)
锁:Redis可以实现乐观锁
【4】异常处理情况
编译型异常(代码有错、命令有错),事务中所有的命令都不会执行!
运行时异常(1/0),事务队列中存在语法错误,那么执行命令的时候其他正常命令是可以执行的!
**************************************************************************************************************************************************************
22、Redis乐观锁
【1】监控
悲观锁:很悲观,认为什么时候都会出问题,很谨慎,无论做什么都加锁
********************************
乐观锁:很乐观,认为什么时候都不会出问题,所以不会上锁
获取version
更新的时候比较version
watch money
DECRBY in 20
incrby out 20
exec
测试多线程修改时,使用watch可以当做redis的乐观锁操作
【2】redis-cli 连接本地redis客户端
auth [password] 输入redis密码
**************************************************************************************************************************************************************
23、Jedis操作Redis
【1】使用Java操作Redis
<!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>4.2.3</version>
</dependency>
【2】连接redis
package com.black.util;
import redis.clients.jedis.Jedis;
public class ZTest {
public static void main(String[] args) throws Exception {
Jedis jedis = new Jedis("wdfgdzx.top", 6379);
jedis.auth("s19911009!");
System.out.println(jedis.ping());
}
}
**************************************************************************************************************************************************************
24、Jedis事务
【1】开启个事务,体验下
package com.black.util;
import org.json.JSONObject;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Transaction;
public class ZTest {
public static void main(String[] args) throws Exception {
Jedis jedis = new Jedis("wdfgdzx.top", 6379);
jedis.auth("s19911009!");
//****************************************************
System.out.println(jedis.ping());
jedis.flushDB();
Transaction transaction = jedis.multi(); // 开启事务
JSONObject jsonObject = new JSONObject();
jsonObject.put("name", "zs");
jsonObject.put("age", 26);
try {
transaction.set("key1", jsonObject.toString());
transaction.set("key2", jsonObject.toString());
// int i = 1 / 0;
transaction.exec();
} catch (Exception e) {
transaction.discard();
e.printStackTrace();
} finally {
System.out.println(jedis.get("key1"));
System.out.println(jedis.get("key2"));
jedis.close();
}
}
}
**************************************************************************************************************************************************************
25、Springboot整合Redis
【1】SpringData JDBC redis...
<!--Redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
【2】jedis替换了lettuce,为什么呢?
jedis采用的是直连,多线程操作是不安全的,如果要避免,需要使用jedis pool连接池
【3】自动配置类
package com.black.util;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.RedisTemplate;
import javax.annotation.Resource;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class ZTest {
@Resource
private RedisTemplate redisTemplate;
@Test
public void one() {
redisTemplate.opsForValue().set("k1", "v1");
System.out.println(redisTemplate.opsForValue().get("k1"));
}
}
【4】自己可以做个性化定制配置RedisConfig
**************************************************************************************************************************************************************
26、RedisTemlate
【1】实操存储可知所有的对象都需要序列化
User user = new User();
user.setName("张三");
user.setNick("我的风格独自秀");
redisTemplate.opsForValue().set("user1", user);
System.out.println(redisTemplate.opsForValue().get("user1")); // 获取会报错
【2】用Redis.config配置具体的序列化方式
package com.black.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@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;
}
}
【3】然后再进行一波测试,User都不用实现序列化了
User user = new User();
user.setName("张三");
user.setNick("我的风格独自秀");
redisTemplate.opsForValue().set("user1", user);
System.out.println(redisTemplate.opsForValue().get("user1"));
可以发现正常了
【4】企业开发中,尽量分装utils,用原生的操作有点麻烦
package com.black.util;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
//在我们真实的开发中, 或者你们在公司, 一般都可以看到一个公司自己封装redis的工具
@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((Collection<String>) 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);
return 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;
}
}
}
【5】最后的测试代码为:
package com.black.util;
import com.black.pojo.User;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.RedisTemplate;
import javax.annotation.Resource;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class ZTest {
@Resource
private RedisUtil redisUtil;
@Test
public void one() throws Exception {
User user = new User();
user.setName("张三");
user.setNick("我的风格独自秀");
redisUtil.set("user2", user);
System.out.println(redisUtil.get("user2"));
}
}
OK全部完成!!!!!!!!!!
**************************************************************************************************************************************************************
27、Redis配置文件
【1】Redis.conf
units are case insensitive so 1GB 1Gb 1gB are all the same.
对大小写不敏感
*****************************
include /path/to/local.conf
include /path/to/other.conf
可以包含其他配置文件(nginx也是这样)
*****************************
loadmodule /path/to/my_module.so
可以加载别的so库文件
*****************************
NETWORK网络
protected-mode xxx保护模式
port 6379 端口
daemonize yes 是否后台运行
pidfile /var/run/redis_6379.pid 如果以后台方式运行,就需要指定一个pid进程文件
loglevel notice 默认就是notice生产环境
logfile "" 日志的文件位置名字
databases 16 数据库数量,默认16个
*****************************
SNAPSHOTTING 快照
规定的时间内操作了多少,才会触发持久化!!!!!!
save 900 1 如果900秒内至少有1个key修改,则进行持久化操作
save 300 10 如果300秒内,有10个key操作过,则进行持久化
save 60 10000 如果60秒内,操作了1万次,则进行持久化
*****************************
stop-writes-on-bgsave-error yes 如果持久化出错,是否继续工作?
rdbcompression yes 是否压缩rdb文件,需要消耗一些CPU资源
rdbchecksum yes 保存rdb文件,是否进行错误检查校验
*****************************
REPLICATION 主从复制的
*****************************
SECURITY 安全,可以设置密码
requirepass s19911009! 在这里进行实际设置。
config get requirepass
config set requirepass xxx
auth xxx
*****************************
CLIENTS 客户端限制
*****************************
MEMORY MANAGEMENT 内存管理
六大配置策略
*****************************
APPEND ONLY MODE aof模式,默认是使用rdb的
appendonly yes
appendfilename "appendonly.aof"持久化文件名字
appendfilename "appendonly.rdb"
appendfsync everysec 每秒执行一次
**************************************************************************************************************************************************************
28、持久化RDB
【1】Redis DataBase RDB
因为Redis是存在内存中,所以一定需要持久化
是有个子进程专门负责这部分工作,不影响主进程性能
【2】可以修改reids.conf
save 60 5 这是60秒修改5次可以触发快照
ps -ef|grep redis
flushall是默认生成rdb文件的
*****************************
所以save 60 5 或 flushall 或退出redis 都会产生rdb文件!!!
备份会自动生成dump.rdb文件
*****************************
如果恢复rdb文件,只需要放到redis启动目录下即可,redis会自动检查恢复
【3】优缺点
优点:
适合大规模数据恢复!
对数据的完整性要求不高!
*****************************
缺点:
需要一定的时间间隔,如果reids以外宕机,最后一次修改的数据丢失
fork进程的时候会占用一定的内容空间
**************************************************************************************************************************************************************
29、持久化AOF
【1】将我们的所有的命令记录下来,类似history命令
appendonly yes 默认是不开启的,需要手动配置
appendonly.aof就是对命令的记录,这样就能通过命令回复数据,牛批
cd /opt/redis/redis-6.0.6
我启动的默认是这个目录下的
【2】优缺点
优点:
每次修改都同步,文件的完整性更佳
每秒同步一次,可能会丢失1秒的数据
从不同步,效率是最高的
缺点:
相对数据文件来说,aof远远大于rdb,修复速度比rdb慢
aof运行效率比rdb慢,所以redis默认的是rdb而不是aof
【3】在主从复制中,rdb就是备用的,在从机上面!
【4】扩展
rdb对数据进行快照存储
aof持久化存储每次记录对服务器写的操作
只做缓存,如果只希望在服务器运行的时候存在,不需要做持久化
同事开启两个持久化性能消耗会大点
rdb建议15分钟备份一次 save 900 1
aof 2秒写一次
**************************************************************************************************************************************************************
30、Redis订阅发布
【1】pub/sub是一种消息通信模式
发送者publisher发送消息
订阅者sub接受消息
微信、微博、关注系统等!
******************************************
第一个:消息发送者
第二个:频道!!!!!!!!!!!!!channel
第三个:消息订阅者
【2】相关命令
PUBSCREIBE xxx
SUBSCREIBE xxx
PUBLISH xxx
和微信公众号发送文章,有人接收一样
可以实现即时聊天、群聊功能!
【3】功能能用于的系统
实时消息系统!
实时聊天!
订阅、关注系统!
稍微复杂的场景,我们会使用消息中间件RMQ
**************************************************************************************************************************************************************
31、Redis集群
【1】主从复制
Master
Slave1
Slave2
【2】数据的复制都是单向的,读写分离
主机负责写,从机负责读,可以减轻服务器压力
【3】单台Redis最大使用内存不应该超过20G,否则应该立刻使用集群
【4】环境配置:只配置从库,不用配置主库
info replication查看当前库的信息
# Replication
role:master
connected_slaves:0
master_replid:e084b9df28e88fb235b452a195920eba5a95266c
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:0
second_repl_offset:-1
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
【5】拷贝多个配置文件
cp redis.conf redis63791.conf
cp redis.conf redis63792.conf
cp redis.conf redis63793.conf
***********************************
修改的地方
port 63791
pidfile /var/run/redis_63791.pid
logfile "63791.log"
dbfilename dump63791.rdb
***********************************
port 63792
pidfile /var/run/redis_63792.pid
logfile "63792.log"
dbfilename dump63792.rdb
***********************************
port 63793
pidfile /var/run/redis_63793.pid
logfile "63793.log"
dbfilename dump63793.rdb
【6】修改信息总结
端口号
pid名字
日志文件名字
dump.rdb名字
【7】依次启动,可以开到三个服务都启动了!!!
redis-server redis63791.conf
ls
redis-server redis63792.conf
ls
redis-server redis63793.conf
ls
*************************************
root 9846 1 0 16:01 ? 00:00:00 redis-server 0.0.0.0:6379
root 16531 1 0 16:16 ? 00:00:00 redis-server 0.0.0.0:63791
root 16934 1 0 16:17 ? 00:00:00 redis-server 0.0.0.0:63792
root 16965 1 0 16:17 ? 00:00:00 redis-server 0.0.0.0:63793
root 17109 3317 0 16:18 pts/0 00:00:00 grep --color=auto redis
**************************************************************************************************************************************************************
32、主从复制
【1】配置一主二从
1是主 2 3是从
SLAVEOF 192.168.100.205 63791(找谁当自己的老大)
info replication 再次查看就变成从机了
【2】主机就能看到从机的信息
info replication
"# Replication
role:master
connected_slaves:2
slave0:ip=192.168.100.205,port=63792,state=online,offset=84,lag=1 从机1
slave1:ip=192.168.100.205,port=63793,state=online,offset=84,lag=0 从机2
master_replid:844773fa614927f6cc88661e76379223be1f89d8
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:84
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:84
【3】真实的应该修改配置文件,命令配置是临时的
redis.conf中操作配置
################################# REPLICATION
# replicaof <masterip> <masterport>
# masterauth <master-password> 如果主机有密码还需要配置密码
【4】细节知识:主机负责写,从机负责读
主机中所有的数据,都会自动被从机保存
set k2 v2 从机不能写
"READONLY You can't write against a read only replica. "
【5】如果主机断开了
这时候配置还是在的。如果主机又来了,则关系会自动恢复!!!!!!
【6】如果从机断开了(命令行配置的主从)
如果从机又回来了...从机默认就变成了主机了,emmm
如果手动让它变成从机,则它就能拿到主机的数据了。
说明只要变成了从机,就会立刻同步数据。
【7】复制原理:全量复制、增量复制。
slave启动成功连接到master会发送一个sync同步命令
master将整个数据同步到salve,并完成一次完全同步。
master后续将继续桨新手机的修改命令,依次传给slave,完成同步。
只要是重新连接,全量复制就会被执行一次!!!!!
**************************************************************************************************************************************************************
33、宕机后手动配置主机
【1】一主二从模式
【2】M63791-SM63792-S63793
63792用info replication查看角色是从节点
层层链路模式
【3】如果M63791宕机了
SM63792执行SLAVEOF no one 手动执行这个命令,就自动成为master
如果M63791回来了,就要重新配置了!!!
**************************************************************************************************************************************************************
34、哨兵模式!!!
【1】这才是重点!!!
【2】就是选主的自动版
哨兵(哨兵也可能有多个,多哨兵模式)
63791
63792
63793
哨兵之间投票,决定谁是主
【3】配置哨兵
vim mysentinel.conf
# 下年是配置的内容
sentinel monitor myredis 192.168.100.205 63791 1(代表主机宕机,slave投票)
***********************************************
启动哨兵
redis-sentinel mysentinel.conf
***********************************************
如果master节点断开,就会从从机中随机选择一个作为主,随机63793被选中了
info replication
"# Replication
role:master
connected_slaves:1
slave0:ip=192.168.100.205,port=63792,state=online,offset=14302,lag=0
master_replid:4aaf00f57e7d45b77a03453e6e63480ae2ec6c44
master_replid2:844773fa614927f6cc88661e76379223be1f89d8
master_repl_offset:14447
second_repl_offset:13396
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:43
repl_backlog_histlen:14405
"
***********************************************
如果主机再回来,主机就自动成为从机了
【4】优缺点
优点:
哨兵集群,基于主从复制模式,所有的主从配置有点,全部拥有
主从可以切换,故障可以转移,系统的可用性就回更好
哨兵模式就是主从模式的升级,手动到自动,更加健壮!、
***********************************************
缺点:
Redis不好在线扩容,集群容量一旦达到上限,在线扩容就十分麻烦!
实现哨兵模式的配置其实是很麻烦的,里面会有很多选择!
如果有哨兵集群,还需要配置多个哨兵端口!
**************************************************************************************************************************************************************
35、缓存穿透和雪崩
【1】面试高频,工作常用
读的请求先去缓存中查询,如果没有去查询数据库
【2】穿透(查不到)
如果redis内存没有,去数据库查询,如果是秒杀,同一时间大量请求去查询mysql,直接就奔了
两种方式:第一种加个过滤器,第二种缓存null对象
【3】缓存击穿(量太大,缓存过期)
某一个热点,不停地扛着大并发
当这个key在失效的瞬间,持续的大并发就穿破缓存,查询到数据库
加互斥锁:保证只有一个线程进去,其余的进行等待
【4】雪崩
缓存集中过期,或者redis宕机
双十一:会停掉一些服务,保证主要的服务可用!
解决方案:
reids集群
限流降级
数据预热
**************************************************************************************************************************************************************
36、小结与拓展
【1】reids概述
【2】安装
【3】实战场景
【4】与JAVA结合Jedis SpringBoot结合
【5】redis配置:持久化
【6】发布订阅
【7】主从复制
【8】redis集群、哨兵模式
【9】缓存场景问题与解决方案