目录
一、NOSQL
NOSQL [Not only SQL]不仅仅是sql. 指的是非关系型数据库。
二、NOSQL和 RDBMS 区别
RDBMS
- 高度组织化结构化数据。 表--结构【列[数据类型] 列[数据类型] ....】
- 结构化查询语言(SQL) sql数据
- 数据和关系都存储在单独的表中。【class student】
- 数据操纵语言DML,数据定义语言DDL DML DDL DQL
- 严格的一致性. 事务.---JDBC 事务。
- 基于事务
NoSQL
- 代表着不仅仅是SQL
- 没有声明性查询语言。 DQL
- 键 - 值对存储。 key value. 底层就是map
- 非结构化和不可预知的数据 字符串 对象 队列 集合
- 高性能,高可用性和可伸缩性。 适合搭建集群。
- 大多数NOSQL都是基于内存运算。
三、常用的NOSQL产品
Redis--基于KEY-VALUE模式
mongodb:--基于文档存储。而且它的语法和关系型数据库很相似。【掌握】
四、概述redis
被数百万开发人员用作数据库、缓存、流引擎和消息代理的开源内存数据存储。
Redis是一种开放源代码(BSD许可)的内存中数据结构存储,用作数据库,缓存和消息代理。Redis提供数据结构,例如字符串,哈希,列表,集合,带范围查询的排序集合,位图,超日志,地理空间索引和流。Redis具有内置的复制,Lua脚本,LRU驱逐,事务和不同级别的磁盘持久性,并通过Redis Sentinel和Redis Cluster自动分区提供了高可用性。
五、redis的优点
5.0的版本--
1.Redis读取的速度是110000次/s,写的速度是81000次/s
2.原子 。Redis的所有操作都是原子性的,同时Redis还支持对几个操作全并后的原子性执行。
3.支持多种数据结构:string(字符串);list(列表);hash(哈希),set(集合);zset(有序集合)
4.持久化--磁盘,主从复制(集群)
5.官方不支持window系统,但是又第三方版本。 linux系统。---有人搞了window的版本4.的版本
六、redis应用场景
实时数据存储
缓存和会话存储-------划重点
重点流媒体和消息传递
七、安装redis
下载redis 并且解压
进入redis解压目录
make 编译命令
make install 安装命令
开启redis服务器
redis-server redis的配置文件 # 按照配置文件启动redis服务器。
客户端连接redis服务
如果想连接其他redis服务
redis-cli -h ip地址 -p 端口号
八、redis的客户端软件
这两款redis客户端软件 安装其一即可 要想使用该软件必须redis开启
默认redis服务器开启时独立占用了一个窗口 所以我们再开一个
8.1 设置后台启动
我们在这里设立redis后台自动启动,编辑redis 在259行修改
8.2 修改端口号
默认为6379
8.3 设置redis服务允许远程连接
安装客户端软件 并能够连接到redis服务端。 设置redis后台启动
九、Redis中常用的命令
redis存储数据的模式: key value模式
9.1 redis操作key的命令
set k1 v1 往redis中存储字符串元素。
1.keys * : 查看redis中所有的key.
2.del key key...: 删除指定的key
3.exists key: 判断指定的key是否存在 存在返回大于0 不存在返回0
4.expire key seconds: 为指定的key设置过期时间。单位为秒
5.ttl key: 查看key的过期时间剩余多少。返回-2表示key不存在 返回-1永久不过期
9.2 操作redis的数据库
redis默认帮你创建了16库。
select n: 选择指定的库。
flushdb: 清空当前所在的库内容
flushall: 清空所有的库内容
9.3 redis支持的数据类型以及命令
我们主要学习的是前五种。String字符串类型 Lists队列类型 Sets集合类型 Hash哈希类型 Sorted sets有序集合。 其他类型建议有时间看看论坛
上面所说的类型是指value的数据类型。redis的key永远都是string类型
9.3.1 字符串类型
value类型为字符串类型, 字符串是最基本的 Redis 数据类型。它们通常用于缓存
关于操作redis字符串的命令:
set key value: 存储字符串数据
set key value ex seconds: 存储key时为其设置过期时间。
get key: 获取指定key的字符串内容。
getex key EX seconds: 获取key的值并设置过期时间: 登录时。
setnx key value: 如果指定的key存在,则不存储,如果不存在则存储。
incr key: 为指定的key进行递增---点赞 收藏数
decr key: 为指定的key进行递减--取消收藏 取消点赞
9.3.2 hash类型
hash类型: redis中的value为hash类型。 hash类型的结构是field-value字段
hset key field value [field value field value....]: 存储hash数据
hget key field: 获取指定key对应的field的值。
hkeys key: 返回哈希表 key 中的所有field
hvals key: 返回哈希表 key 中所有field的值
hgetall key: 返回哈希表 key 中,所有的域和值。
9.3.3 list队列类型
redis中value值为list对象类型
1. lpush key value......: 从左边存储队列类型
2. lpop key: 移除并返回列表 key 的头元素。
3. LRANGE 返回列表 key 中指定区间内的元素,区间以偏移量 start 和 stop 指定
9.3.4 Set集合类型
它和我们上面讲解的list类型比较相似,但是它是无序而且不允许重复
sadd key value value...: 添加set集合元素。
sismember key
member
: 判断member
元素是否集合key
的成员。SPOP key: 随机从集合中获取一个元素并移除该元素
smembers key: 获取集合中所有的元素
sunion key1 key2: 获取指定key1和key2集合的并集
sinter key1 key2: 获取指定key1和key2集合的交集
9.3.5 sorted set集合类型
与上面的类型比较相似,它的特点也是无序,不可重复,但是它允许带分数。用于排行榜
zadd key values score value score value score....往redis中添加有序集合元素。
zrange key start end: 获取指定key中指定范围的元素按照从低到高。
zrevrange key start end: 获取指定key中指定范围的元素按照从高到低。
十、Redis的持久化
什么是持久化?
把内存中的数据保存到磁盘获取网盘上,这个过程就是持久化。
redis持久化?
redis的运算都是基于内存执行的,但是为了包装数据不丢失,可以把数据保存到磁盘中。
当redis启动时,从磁盘中把数据在读取到内存中参与运算。
10.1 : redis的持久化方式
redis持久化方式有两种:
(1)RDB:快照模式
(2)AOF: 文件追加模式
10.2 : RDB持久化方式
其实就是把数据以快照的形式保存在磁盘上,什么是快照呢,你可以理解成把当前时刻的数据拍成一张照片保存下来。
默认保存在dump.rdb文件中
RDB的触发机制有哪些?
第一种:手动触发
第二种:自动触发
手动触发的方式也有两种:1.save命令 2.bgsave命令
Save命令和bgsave命令的区别:
Sava:该命令会阻塞当前Redis服务器,执行save命令期间,Redis不能处理其他命令,直到RDB过程完成为止。具体流程如下
执行完成时候如果存在老的RDB文件,就把新的替代掉旧的。我们的客户端可能都是几万或者是几十万,这种方式显然不可取
bgsave:执行该命令时,Redis会在后台异步进行快照操作,快照同时还可以响应其他客户端请求。具体流程如下:
上面这两种方式都是需要客户手动触发。而实际我们应该让redis自动触发。
如何自动触发:只需要修改reids的配置就可以了
从上面的演示我们就可以知道RDB持久化的优缺点
优点:
持久化速度比较快,而且数据恢复也比较快
缺点:
由于是某个时间断触发RDB,这样会导致丢失一个时间断的数据。数据完整性比较差。
10.3: AOF持久化方式
AOF:日志追加 记录服务器接受的每个写入操作,当服务器启动时再次加载该日志,会把日志中的命令重新执行一遍。
该模式默认不启动,而且这个日志文件的名字appendonly.aof
优点:数据完整性比较高。最多丢失一条命令。
缺点:随着写操作越来越大,日志文件也越来越大。数据恢复非常慢。
十一、redis的应用场景
1.热点数据的缓存: 减少对数据库的访问频率和减轻数据库的压力。
2.限时业务的运用: 秒杀 存储登录者用户信息 存储短信验证码. 可以设置key的过期时间
3.计数器相关问题: 点赞数 收藏数 播放量 incr decr.
4.update 表名 set num=num+1 where id=?
5.排行榜相关问题: sort set
6.分布式锁:
十二、redis的集群模式
作用: 为了解决redis单点故障。提高redis的可用性
12.1 : redis的主从模式
如何搭建redis的主从模式: 配从不配主。
一主两从: 三台服务器【这个三台之间可以相互访问】。 我这里为了操作方便,就在一台服务器启动三个redis服务。 只需要修改端口号即可
创建三个redis配置文件
1. 修改端口号
2. 修改rdb文件的名称。
3. 修改aof的文件名启动上面三个redis服务--根据上面的配置
客户端连接到redis三台服务器
redis-cli -p 端口号
配置三者之间的主从关系: 6380为主节点 6381和6382为从节点。 配从不配主
我们在客户端输入info replication 可以查看每台redis服务器的主从关系
从上面的命令可以查看到三台redis服务器之间没有任何主从关系。
我们可以使用 slaveof 主节点ip 主节点port
主节点写入的数据会同步到所有从节点
从节点只能负责读操作,不能负责写操作
思考:
1.如果新加入了一个从节点,那么该从节点会不会把主节点之前的数据同步? 会
2.如果主节点挂掉,从节点会不会上位? 不会
12.2 :redis的哨兵模式
我们从上面的主从模式 我们知道该模式,如果主节点宕机后,需要人为的选举主节点。 从节点不会主动变成主节点。 哨兵模式 会在剩下的从节点中选举一个作为主节点。
修改哨兵的配置文件 并启动哨兵服务。
启动哨兵服务
演示主节点宕机
思考: 原来的主节点恢复了,是否可以当老大 不可以
12.3 redis集群模式
从上面的两种模式 我们知道它只有一个主节点,如果后期写操作也比较多,势必会操作主节点压力比较大。而且上面主节点要把数据同步到所有从节点。所以出现了多主多从模式
搭建一个三主三从的redis集群。7001~7006
端口号
rdb文件名
aof文件名[必须开启aof模式]
开启集群模式。
启动上面6台redis服务
分配槽以及主从关系
redis-cli --cluster create --cluster-replicas 1 192.168.223.158:7001 192.168.223.158:7002 192.168.223.158:7003 192.168.223.158:7004 192.168.223.158:7005 192.168.223.158:7006
测试集群环境
十三、java连接redis服务
redis提高很多语言的api接口。这些语言都可以操作redis服务器。 redis提高了对Java语言操作的接口的jar,它就是jedis. 这jar中封装了一个Jedis类,该类中包含了所有对redis操作的方法
13.1 引入依赖
<dependencies>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.6.0</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.9</version>
<scope>test</scope>
</dependency>
</dependencies>
13.2 测试Key的API方法
//对应redis中key的操作接口
@Test
public void test02(){
Jedis jedis=new Jedis("192.168.223.158",6379);
//获取所有的key
Set<String> keys = jedis.keys("*");
System.out.println(keys);
//删除指定的key
Long c = jedis.del("k1","k2","k5");
System.out.println("删除key的个数:"+c);
//判断指定的key是否存在
Boolean exists = jedis.exists("k2");
System.out.println("判断key是否存在:"+exists);
}
13.3 测试字符串类型的api方法
//对应字符串操作的方法
@Test
public void test03(){
Jedis jedis=new Jedis("192.168.223.158",6379);
//存放字符串数据
jedis.set("k1","1");
jedis.set("k2","v2");
//获取指定key的内容
String value1 = jedis.get("k1");
System.out.println("k1对应的内容:"+value1);
//如果指定的key存在则不存入,不存在则存入redis
Long aLong = jedis.setnx("k8", "ldh");
System.out.println("是否存入:"+aLong);
//存入时设置过期时间
String str = jedis.setex("k4", 30l, "v4");
System.out.println("存入的内容:"+str);
//用于点赞和收藏
Long incr = jedis.incr("k1");
System.out.println("递增后的结果:"+incr);
}
13.4 测试hash类型的api方法
//对应hash类型的操作----可以存放对象。
@Test
public void test04(){
Jedis jedis=new Jedis("192.168.223.158",6379);
Map<String,String> map=new HashMap();
map.put("name","刘德华");
map.put("age","18");
map.put("sex","男");
Long k1 = jedis.hset("k1", map);
String name = jedis.hget("k1", "name");
System.out.println("获取hash中name对应的值:"+name);
Map<String, String> map1 = jedis.hgetAll("k1");
System.out.println("获取指定key对应的内容:"+map1);
Set<String> k11 = jedis.hkeys("k1");
System.out.println("获取k1对应的所有field:"+k11);
List<String> values = jedis.hvals("k1");
System.out.println("获取k1对应的所有field的值:"+values);
}
13.5 jedis提高了连接池
为了减少频繁的创建和销毁jedis对象,提高了jedis的连接池,以提高连接效率。
JedisPool
import org.junit.Test;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
public class Test02 {
@Test
public void test01(){
//创建连接池的配置类
JedisPoolConfig config=new JedisPoolConfig();
config.setMinIdle(5); //设置空闲的个数
config.setMaxIdle(10);
config.setMaxTotal(2000); //设置最多的数量
config.setMaxWaitMillis(6000);//设置最大的等待时长
config.setTestOnBorrow(true); //是否检验池子中的jedis对象可用
//创建jedis连接池对象.
JedisPool jedisPool = new JedisPool(config,"192.168.223.158",6379);
//获取jedis对象---从池子中获取。
Long start = System.currentTimeMillis();
for(int i=0;i<10000;i++) {
Jedis jedis = jedisPool.getResource();
String ping = jedis.ping();
jedis.close();
}
Long end = System.currentTimeMillis();
System.out.println("耗时:"+(end-start));
}
}
十四、springboot整合redis
(2) 配置application配置文件。
#指定redis服务的地址和端口号哦
spring.redis.host=192.168.223.158
spring.redis.port=6379
# 最大的活跃数
spring.redis.jedis.pool.max-active=10
spring.redis.jedis.pool.max-wait=5000
spring.redis.jedis.pool.max-idle=5
(3)在springboot中封装了两个类,RedisTemplate和StringRedisTemplate. 而这两个类也被redis自动装配类加载spring容器中管理,所以我们可以直接在代码中注入这两个类对象
【1】StringRedisTemplate
package com.ykq.qy158springbootredis;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
@SpringBootTest
class Qy158SpringbootRedisApplicationTests {
//该类中封装了很多api方法--redis服务
@Autowired
private StringRedisTemplate redisTemplate;
//关于key操作的方法。
@Test
void contextLoads() {
//删除指定的key
Boolean aBoolean = redisTemplate.delete("k1");
System.out.println("是否删除成功:"+aBoolean);
//判断指定的key是否存在
Boolean hasKey = redisTemplate.hasKey("k1");
System.out.println("判断指定的key是否存在:"+hasKey);
}
//关于对字符串的操作。
//在redisTemplate类中,对于每种数据类型,都封装了相应的类对象,得到相应的类对象,即可对相应的数据类型操作。
@Test
public void test02(){
//获取对String类型操作的类对象。
ValueOperations<String, String> forValue = redisTemplate.opsForValue();
//使用上面的类对象即可对String类型操作
forValue.set("k1","张三爱吃水果");
forValue.set("k4","10");
forValue.set("k2","李四爱吃榴莲",15, TimeUnit.MINUTES);
//如果存在,则不存入,不存在则存入
Boolean aBoolean = forValue.setIfAbsent("k3", "王五爱吃米饭", 25, TimeUnit.SECONDS);
System.out.println("是否存入成功:"+aBoolean);
//获取对于的值
String value1 = forValue.get("k1");
System.out.println("获取k1对于的数据:"+value1);
Long aLong = forValue.increment("k4");
System.out.println("递增后的结果:"+aLong);
}
//关于对Hash类型的操作
@Test
public void test03(){
HashOperations<String, Object, Object> forHash = redisTemplate.opsForHash();
forHash.put("k1","name","刘德华");
forHash.put("k1","age","18");
forHash.put("k1","address","香港");
Map<String,String> map=new HashMap<>();
map.put("name","张学友");
map.put("age","25");
map.put("address","北京");
forHash.putAll("k2",map);
Object o = forHash.get("k1", "name");
System.out.println("获取指定key对于的name的值:"+o);
Map<Object, Object> k2 = forHash.entries("k2");
System.out.println("获取k2对于的map对象:"+k2);
Set<Object> keys = forHash.keys("k2");
System.out.println("获取k2对于的所以field:"+keys);
List<Object> values = forHash.values("k2");
System.out.println("获取k2对于的所有filed的值:"+values);
}
}
[2]RedisTemplate
该类型其实是StringRedisTemplate的父类
public class StringRedisTemplate extends RedisTemplate<String, String> {
我们知道StringRedisTemplate它存储的key和value field必须都是String类型,不能存放对象类型。 而RedisTemplate的key和value可以自己指定数据类型,所有它的key和value可以是任意类型
public class RedisTemplate<K, V>
@Test
public void test01(){
ValueOperations valueOperations = redisTemplate.opsForValue();
valueOperations.set("k1",new Student(1,"张三","男"));
}
表示: 存入的对象需要序列化。 默认使用的是JDK序列化。必须要求你的类实现序列化接口。
解决办法:
package com.ykq.qy158springbootredis;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
@SpringBootTest
class Qy158SpringbootRedisApplicationTests02 {
//该类中封装了很多api方法--redis服务
@Autowired
private RedisTemplate redisTemplate;
@Test
public void test01(){
//默认RedisTemplate它的key和value的序列化都是使用的JdkSerializationRedisSerializer方式,该序列化要求类必须实现Serializable接口。
//我们在实际开发中,我们的key都是String类型,我们应该指定String序列化方式
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
ValueOperations valueOperations = redisTemplate.opsForValue();
valueOperations.set("k1",new Student(1,"张三","男"));
System.out.println(valueOperations.get("k1"));
}
}