今日内容

1. redis
	1. 概念
	2. 下载安装
	3. 命令操作
		1. 数据结构
	4. 持久化操作
	5. 使用Java客户端操作redis

1. Redis介绍

1.1Redis概念

Redis是用C语言开发的一个开源的高性能键值对(key-value)数据库,官方提供测试数据,50个并发执行100000个请求,读的速度是110000次/s,写的速度是81000次/s ,且Redis通过提供多种键值数据类型来适应不同场景下的存储需求,是一款高性能的NOSQL系列的非关系型数据库

1.2NoSql介绍

NoSQL(NoSQL = Not Only SQL),意即“不仅仅是SQL”,是一项全新的数据库理念,泛指非关系型的数据库。
	随着互联网web2.0网站的兴起,传统的关系数据库在应付web2.0网站,特别是超大规模和高并发的SNS类型的web2.0纯动态网站已经显得力不从心,暴露了很多难以克服的问题,而非关系型的数据库则由于其本身的特点得到了非常迅速的发展。NoSQL数据库的产生就是为了解决大规模数据集合多重数据种类带来的挑战,尤其是大数据应用难题。

为什么要使用NoSQL

背景:早起的网站使用的都是单机版MySQL,不需要考虑太多的数据量问题;目前大数据时代,数据量急增,单机版数据库就出现了瓶颈:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7yKOhTU0-1683468202346)(./assets/nosql01.png)]

1.数据量太大,一台机器放不下

2.数据的索引(B+Tree),一个机器的内存也放不下

3.访问货并发量太大,一个服务器承受不了

解决方案:

缓存+垂直拆分

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yTG2J9NE-1683468202348)(./assets/nosql02.png)]

1.3NoSql和关系型数据库的比较

1.3.1优缺点

优点:

1)成本:nosql数据库简单易部署,基本都是开源软件,不需要像使用oracle那样花费大量成本购买使用,相比关系型数据库价格便宜。
2)查询速度:nosql数据库将数据存储于缓存之中,关系型数据库将数据存储在硬盘中,自然查询速度远不及nosql数据库。
3)存储数据的格式:nosql的存储格式是key,value形式、文档形式、图片形式等等,所以可以存储基础类型以及对象或者是集合等各种格式,而数据库则只支持基础类型。
4)扩展性:关系型数据库有类似join这样的多表查询机制的限制导致扩展很艰难。

缺点:

1)维护的工具和资料有限,因为nosql是属于新的技术,不能和关系型数据库10几年的技术同日而语。
2)不提供对sql的支持,如果不支持sql这样的工业标准,将产生一定用户的学习和使用成本。
3)不提供关系型数据库对事务的处理。

1.3.2 非关系型数据库的优势

1)性能NOSQL是基于键值对的,可以想象成表中的主键和值的对应关系,而且不需要经过SQL层的解析,所以性能非常高

2)可扩展性同样也是因为基于键值对,数据之间没有耦合性,所以非常容易水平扩展

1.3.3 关系型数据库的优势

1)复杂查询可以用SQL语句方便的在一个表以及多个表之间做非常复杂的数据查询

2)事务支持使得对于安全性能很高的数据访问要求得以实现。对于这两类数据库,对方的优势就是自己的弱势,反之亦然

1.3.4 应用场景

缓存(数据查询、短连接、新闻内容、商品内容等等)
• 在线好友列表
• 任务队列。(秒杀、抢购、12306等等)
• 应用排行榜
• 网站访问统计
• 数据过期处理(可以精确到毫秒)
• 分布式集群架构中的session分离

1.3.5 总结

1.关系型数据库与NoSQL数据库并非对立而是互补的关系,即通常情况下使用关系型数据库,在适合使用NoSQL的时候使用NoSQL数据库,
让NoSQL数据库对关系型数据库的不足进行弥补。
2.一般会将数据存储在关系型数据库中,在nosql数据库中备份存储关系型数据库的数据

1.4 主流NoSql产品

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eUfOZiEb-1683468202350)(./assets/nosql03.png)]

  1. 键值(Key-Value)存储数据库

相关产品: Tokyo Cabinet/Tyrant、Redis、Voldemort、Berkeley DB

典型应用: 内容缓存,主要用于处理大量数据的高访问负载

数据模型: 一系列键值对

优势: 快速查询

劣势: 存储的数据缺少结构化

  1. 列存储数据库

相关产品:Cassandra, HBase, Riak

典型应用:分布式的文件系统

数据模型:以列簇式存储,将同一列数据存在一起

优势:查找速度快,可扩展性强,更容易进行分布式扩展

劣势:功能相对局限

  1. 文档型数据库

相关产品:CouchDB、MongoDB

典型应用:Web应用(与Key-Value类似,Value是结构化的)

数据模型: 一系列键值对

优势:数据结构要求不严格

劣势: 查询性能不高,而且缺乏统一的查询语法

  1. 图形(Graph)数据库

相关数据库:Neo4J、InfoGrid、Infinite Graph

典型应用:社交网络

数据模型:图结构

优势:利用图结构相关算法

劣势:需要对整个图做计算才能得出结果,不容易做分布式的集群方案

2. Redis入门

2.1下载安装

官网:https://redis.io

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ywQm3ffM-1683468202354)(./assets/redis.io.png)]

中文网:http://www.redis.net.cn/

解压直接可以使用:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YARBEZK0-1683468202354)(./assets/redis-file.png)]

redis.windows.conf:配置文件

redis-cli.exe:redis的客户端

redis-server.exe:redis服务器端

2.2 Redis数据类型

redis存储的是:key,value格式的数据,其中key都是字符串,value有5种不同的数据结构

2.2.1 字符串类型String

存储

# set key value
set username zhangsan

获取

# get key
get username

删除

# del key
del username

2.2.2 哈希类型hash

存储

# hset key field value
hset myhash username lisi
hset myhash password 123

获取

# hget key field 获取指定key
hget myhash username
# hgetall key 获取所有
hgetall myhash

删除

# hdel key field
hdel myhash username

2.2.3 列表类型 list

添加

# lpush key value 将元素加入列表左表
lpush myList a
lpush myList b
lpush myList c
# rpush key value:将元素加入列表右边
rpush myList q
rpush myList w
rpush myList e

获取

# lrange key start end :范围获取 -1代表获取全部
lrange myList 0 -1

删除

# lpop key: 删除列表最左边的元素,并将元素返回
lpop myList
# rpop key: 删除列表最右边的元素,并将元素返回
rpop myList

2.2.4 集合类型 set

存储 不允许重复元素

# add key value
sadd myset a

获取

# smembers key:获取set集合中所有元素
smembers myset

删除

# srem key value:删除set集合中的某个元素
srem myset a

2.2.5 有序集合类型 sortedset

存储

# zadd key score value
zadd mysort 60 zhangsan
zadd mysort 50 lisi
zadd mysort 80 wangwu

获取

# zrange key start end [withscores]
zrange mysort 0 -1
-->"lisi"
-->"zhangsan"
-->"wangwu"
zrange mysort 0 -1 withscores
-->"zhangsan"
-->"60"
-->"wangwu"
-->"80"
-->"lisi"
-->"500"

删除

# zrem key value
zrem mysort lisi

2.2.6 通用命令

# keys * : 查询所有的键
# type key : 获取键对应的value的类型
# del key:删除指定的key value

2.2.7设置生存时间

expire key time	#time单位为秒

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3y1Qndl2-1683468202355)(./assets/redis-expire.png)]

2.2.8清除生存时间

persist key		#注意:重置key的生存时间可以清除原有的生存时间

2.3 Redis持久化方案

redis是一个内存数据库,当redis服务器重启,获取电脑重启,数据会丢失,我们可以将redis内存中的数据持久化保存到硬盘的文件中

2.3.1 RDB机制

Redis持久化的默认方式,不需要进行配置

在一定的间隔时间中,检测key的变化情况,然后持久化数据

持久化时间间隔流程

  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
  1. 重新启动redis服务器,并指定配置文件名称
D:\JavaWeb2018\day23_redis\资料\redis\windows-64\redis-2.8.9>redis-server.exe redis.windows.conf

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EIV09N5q-1683468202356)(./assets/redis-rbd-option.png)]

Redis会根据key值的变化,自动生成一个.rdb的文件

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ARVmOkPH-1683468202357)(./assets/redis-rbd.png)]

  1. rdb文件生成的触发机制(默认使用其默认的配置即可)

a、满足save规则的情况下,会自动出发rdb规则

b、执行flushall命令,会触发rbd规则

c、退出redis,会触发rbd规则

4.RBD机制的优缺点

优点

a、适合大规模的数据恢复

b、对数据的完整性要求不高

缺点

a、需要一定的时间间隔操作,如果该时间段内redis宕机了,那么最后一次的数据会丢失

b、持久化的过程中会占用一定的内存资源

2.3.2 AOF(Append only file)机制

日志记录的方式,可以记录每一条命令的操作。可以每一次命令操作后,持久化数据(将所有的命令都记录【只记录写操作】下来,行程日志文件,恢复的时候把文件执行一遍)

# 编辑redis.windwos.conf文件
appendonly no(关闭aof) --> appendonly yes (开启aof)
# appendfsync always : 每一次操作都进行持久化
appendfsync everysec : 每隔一秒进行一次持久化
# appendfsync no	 : 不进行持久化

可手动修改生成的aof文件,破坏其完整行(重启redis时报错,无法启动),通过redis-check-aof.exe文件进行修复操作(最后一秒的数据可能会丢失),再次重启redis即可链接【此处不做演示,可自行操作】

AOF机制的优缺点

优点

a、每一次操作都同步,文件的完整性更高

b、每秒同步一次,可能会丢失最后一次的数据

c、从不同步,效率最高

缺点

a、aof无限制的追加文件,相对于数据文件来说,aof远大于rdb,因此修复的速度比rbd慢

b、aof数据同步的频率影响了起性能,相较rbd其运行效率较慢,因此默认使用了rbd机制进行持久化

3. Jedis客户端

Jedis是一款java操作redis数据库的工具.(类似jdbc的操作方式)

使用流程

添加pom依赖

<dependencies>
	    <dependency>
        	<groupId>com.fasterxml.jackson.core</groupId>
       	 	<artifactId>jackson-databind</artifactId>
        	<version>2.13.2.2</version>
    	</dependency>
		<!--redis依赖-->
		<dependency>
			<groupId>redis.clients</groupId>
			<artifactId>jedis</artifactId>
			<version>2.9.3</version>
		</dependency>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.13.1</version>
			<scope>test</scope>
		</dependency>
 		<dependency>
        	<groupId>org.projectlombok</groupId>
        	<artifactId>lombok</artifactId>
        	<version>1.18.10</version>
    	</dependency>
</dependencies>

创建连接

//1.下载jedis的jar包并关联
//2.获取连接,6379是Redis的默认端口号
Jedis jedis = new Jedis("localhost",6379);
//Jedis jedis = new Jedis(); //如果使用空参构造,默认值 "localhost",6379端口
//3.操作(curd)
jedis.set("username","zhangsan");
//4.关闭
jedis.close();

3.1 Jedis操作字符串

//1. 获取连接
Jedis jedis = new Jedis();//如果使用空参构造,默认值 "localhost",6379端口
//2. 设置值
jedis.set("username","zhangsan");
//3. 获取值
String username = jedis.get("username");
System.out.println(username);

//可以使用setex()方法存储可以指定过期时间的 key value
jedis.setex("activecode",20,"hehe");//将activecode:hehe键值对存入redis,并且20秒后自动删除该键值对

//3. 关闭连接
jedis.close();

3.2 Jedis操作哈希类型

//1. 获取连接
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中的数据
Set<String> keySet = user.keySet();
for (String key : keySet) {
    //获取value
    String value = user.get(key);
    System.out.println(key + ":" + value);
}
//3. 关闭连接
jedis.close();

3.3 Jedis操作列表类型

//1. 获取连接
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();

3.4 Jedis操作set集合类型

//1. 获取连接
Jedis jedis = new Jedis();
//2. 存储
jedis.sadd("myset","java","php","c++");
//3. 获取
Set<String> myset = jedis.smembers("myset");
System.out.println(myset);

//4. 关闭连接
jedis.close();

3.5 Jedis操作sortedset有序集合

//1. 获取连接
Jedis jedis = new Jedis();
//2. 存储
jedis.zadd("mysortedset",3,"亚瑟");
jedis.zadd("mysortedset",30,"后裔");
jedis.zadd("mysortedset",55,"孙悟空");

//获取
Set<String> mysortedset = jedis.zrange("mysortedset", 0, -1);

System.out.println(mysortedset);
//3. 关闭连接
jedis.close();

4. Jedis连接池

JedisPool是Jedis的连接池对象,用于Jedis连接对象的管理(类似jdbc的连接池)

4.1 使用流程

//创建连接池配置对象信息
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(50);
config.setMaxIdle(10);
//创建JedisPool连接池对象
JedisPool jedisPool = new JedisPool(config,"localhost",6379);
//调用方法 getResource()方法获取Jedis连接
Jedis jedis = jedisPool.getResource();
//使用jedis
jedis.set("hehe","heihei");
//关闭 归还到连接池中
jedis.close();

4.2 连接池工具类

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();
    }
}

5. Redis 集群

5.1 主从复制(读写分离)

概念:

主从复制是指,将一台Redis服务器的数据,复制到其他Redis服务器上,使两者达到数据一致,将第一台数据库作为主数据库(主节点),其他的当做从数据库(从节点)

注意:数据的复制是单向的,只能从主节点到从节点,主节点以写为主,从节点以读为主,从而减轻服务器的压力

优点:

1.避免Redis单点故障,导致服务器崩溃,当主节点宕机时可使用从节点替代,从而实现快速的故障回复

2.构建高可用的读写分离架构,满足读多写少的应用场景

主从架构图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XwneCJDX-1683468202358)(./assets/redis-master.png)]

架构图说明

1.主redis中的数据有两个副本(replication)即从redis1和从redis2,即使一台redis服务器宕机其它两台redis服务也可以继续提供服务

2.主redis中的数据和从redis上的数据保持实时同步,当主redis写入数据时通过主从复制机制会复制到两个从redis服务上

3.只有一个主redis,可以有多个从redis

4.主从复制不会阻塞master,在同步数据时,master 可以继续处理client 请求

5.2哨兵模式

哨兵模式本质是一种容灾方案

哨兵:实则是一个在特殊模式下的Redis服务器,里面存储的是自己本身的信息,主服务器的信息,从服务器的信息。

用一个或者多个哨兵来监视主服务器(也就是进行写操作的服务器)是否在正常执行任务,一旦哨兵发现主服务器不可用时,就找到一个合适的从服务器成为主服务器

单哨兵架构

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7qdfYzcP-1683468202359)(./assets/哨兵-one.png)]

多哨兵架构

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EJhsz0Tb-1683468202360)(./assets/哨兵-more.png)]

6. Redis常见问题

Redis缓存的使用可以极大的提高程序的性能和效率,但缓存的存在是无法避免数据一致性问题的,因此,对数据一致性 要求很高时,就不能使用缓存

常见的问题有:缓存穿透、缓存雪崩及缓存击穿

6.1缓存穿透

概念:用户想要查询一个数据,发现redis缓存没有命中(缓存中没有),就会去持久层数据库中查询,数据库中也不存在该信息,当该请求出现频率过高时,对数据库会造成很大的压力,这种情况称为缓存穿透

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vWjqncnc-1683468202360)(./assets/Redis缓存穿透.png)]

解决方案

1.缓存空对象,设置空值,从而减轻对服务器的压力

a、如果存储的空值过多就需要更多的空间进行存储

b、对空值设置过期时间从而解决空间,因存在时间差无法保证数据一致性

2.布隆过滤器(底层是一个过滤器,队请求进行校验,降低对服务器的压力)

6.2缓存击穿

概念:大量的请求同时在对某一个key进行访问,某一时刻,key过期时,所有的请求直接访问了数据库,将数据回写到缓存中,会导致数据库在某一瞬间压力过大,这种情况称为缓存击穿

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hrIy3kNz-1683468202361)(./assets/缓存击穿.png)]

解决方案

1.设置热点数据用不过期(不过期的数据过多时也会造成空间不够的问题)

2.加互斥锁:使用分布式锁,给每一个key单独加锁,保证只有一个线程去请求后台的服务

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DIZevhyP-1683468202362)(./assets/互斥锁.png)]

6.3缓存雪崩

概念:某一个时间段,缓存集中过期【缓存的雪崩很有可能导致某个服务器直接宕机,后果不可预知】

解决方案;

1.Redis高可用:搭建Redis集群,当某一个挂掉之后,其他的还可以工作

2.限流降级:控制访问量,其他的进行排队等待

3.数据预热:在部署之前,预先访问一遍,对热点数据提前缓存,设置不同的过期时间,让失效时间分布点尽量均匀

7. Redis常见面试题

1.Redis中常见的数据类型有哪几种

2.Redis的应用场景有哪些(你在项目中哪个模块使用了Redis)

3.Redis中的持久化方案有哪些

4.你在项目中使用Redis时有没有遇到问题

a)Redis的缓存雪崩

b)Redis的缓存穿透

5.上述的问题解决方案是什么

2.布隆过滤器(底层是一个过滤器,队请求进行校验,降低对服务器的压力)

6.2缓存击穿

概念:大量的请求同时在对某一个key进行访问,某一时刻,key过期时,所有的请求直接访问了数据库,将数据回写到缓存中,会导致数据库在某一瞬间压力过大,这种情况称为缓存击穿

[外链图片转存中…(img-hrIy3kNz-1683468202361)]

解决方案

1.设置热点数据用不过期(不过期的数据过多时也会造成空间不够的问题)

2.加互斥锁:使用分布式锁,给每一个key单独加锁,保证只有一个线程去请求后台的服务

[外链图片转存中…(img-DIZevhyP-1683468202362)]

6.3缓存雪崩

概念:某一个时间段,缓存集中过期【缓存的雪崩很有可能导致某个服务器直接宕机,后果不可预知】

解决方案;

1.Redis高可用:搭建Redis集群,当某一个挂掉之后,其他的还可以工作

2.限流降级:控制访问量,其他的进行排队等待

3.数据预热:在部署之前,预先访问一遍,对热点数据提前缓存,设置不同的过期时间,让失效时间分布点尽量均匀

7. Redis常见面试题

1.Redis中常见的数据类型有哪几种

2.Redis的应用场景有哪些(你在项目中哪个模块使用了Redis)

3.Redis中的持久化方案有哪些

4.你在项目中使用Redis时有没有遇到问题

a)Redis的缓存雪崩

b)Redis的缓存穿透

5.上述的问题解决方案是什么