一、NoSQL的四大分类
1.KV键值对
- Redis(做缓存和日志)
2.文档型数据库(bson格式和json一样)
- MongDB(一般必须要掌握)
- MongoDB是一个基于分布式文件存储的数据库,C++编写,主要用来处理大量的文档!
- MongoDB是一个基于关系型数据库和非关系型数据库的中间产品,MongoDB是非关系型数据库中功能最丰富,最像关系型数据库的
- ConthDB
3.列存储数据库
- HBase
- 分布式文件系统
4.图关系型数据库
- 不是存图形的,放的是关系,比如:朋友圈社交网络,广告推荐
- Neo4j,InfoGrid;(社交网络和推荐系统专注于构建关系图谱)
二.Redis入门
1.概念:
Redis是什么?
Redis(Remote Dictionary Server),即远程字典服务。
一次性读可以读100000次,写可以写80000次。
Redis能干嘛?
1.内存存储,持久化,内存是断电即失,所以说持久化很重要(rdb,aof)
2.效率高,可以用于高速缓存
3.发布订阅系统
4.地图信息分析
5.计时器,计数器(浏览量!)
特性:
- 多样的数据类型
- 持久化
- 集群
- 事务
下载:
http://download.redis.io/releases/
https://github.com/microsoftarchive/redis/releases/tag/win-3.2.100
2、redis-benchmark测试
前提要开启redis服务。
测试:100个并发连接,每个连接100000请求
redis -benchmark -h localhost -c 100 -n 100000
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-A6puO6YT-1684541589747)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\1675655106913.png)]
3.基础的知识
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-srE7uYuR-1684541589752)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\1675731978258.png)]
总共有16个数据库,默认使用的是第0个数据库
可以用select进行切换数据库!
基本的操作命令:
127.0.0.1:6379> select 3 #切换数据库
OK
127.0.0.1:6379[3]> dbsize #查看db大小
(integer) 0
127.0.0.1:6379[3]> set name xiaofeng
get name
keys *
flushdb 删除当前库
flushall 删除所有库
Redis特点:
-
redis是单线程的!!
-
明白redis是很快的,官方表示,Redis是基于内存操作,CPU不是redis的性能瓶颈,redis的瓶颈是根据机器的内存和网络带宽,既然可以使用单线程来实现,就使用单线程。
-
Redis是C语言写的,官方提供的数据为100000+的QPS,完全不比同样是使用key-value的Memecache差!
Redis为什么单线程还这么快?
1.误区1:高性能的服务器一定是多线程的?
2.误区2:多线程(CPU上下文会切换!)一定比单线程效率高?
核心:redis是将所有的数据全部放在内存中的,所以说使用单线程去操作效率是最高的,多线程(CPU上下文会切换:耗时操作!!!),对于内存系统来说,没有上下文切换,效率是最高的。多次读写都是在CPU上,在内存情况下,这个就是最佳的方案。
4.五大数据类型
Redis-Key
单点登录,过期时间,热点数据-----> 使用expire name 10 过期时间设置
keys *
expire name 10 #设置删除时间
ttl name #查看还是多久的时间删除完
move name 1 #移除key
type name
若有不清楚的命令,就上官网查看。
String(字符串)
set view 0
set key1 v
incr view #view++ 1
decrview #view-- 0
incrby view 10 #view 加10 10
decrby view 10 #view 减10 0
append key1 "hello" # 在原先的值上加上字符串“hello” (vhello)
##############################
set key1 "hello,kuangshen"
getrange key1 0 3 #"hell" ==subString方法
getrange key1 0 -1 #"hello,kuangshen"(输出全部)
##############################
set key2 abcdefg
setrange key2 1 XX #axxdefg ==replace()方法,替换指定位置的字符串
##############################
setex key3 30 "hello" #设置key3为hello,30秒后过期
setnx mykey "redis" # 如果mykey不存在,创建成功。“redis”
setnx mykey "mongdb" #如果mykey存在,创建失败 “redis"
##############################
mset k1 v1 k2 v2 k3 v3 #设置多个值
msetnx k1 v1 k4 v4 #不能设置k4,原子性操作,要么成功,要么失败(因为k1存在)
##############################
#对象
set user:1 {name:zhangsan,age:3} #设置一个user:1对象,值为json字符来保存一个对象!
mset user:1:name kuangshen user:1:age 22
mget user:1:name user:1:age #"kuangshen" "22"
##############################
getset #先get后set
127.0.0.1:6379> getset db redis
(nil)
127.0.0.1:6379> get db
"redis"
127.0.0.1:6379> getset db mongdb
"redis"
127.0.0.1:6379> get db
"mongdb"
数据结构是相同的。
String类似的使用场景:value除了是我们的字符串,还可以是我们的数字
- 计数器
- 统计多单元的数量;uid: follow: incr
- 粉丝数
- 对象缓存存储
List
在redis中,我们可以把list玩成,栈,队列,阻塞队列!
往list中插入:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RRQtMzbK-1684541589755)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\1675937545630.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1TOpuQYZ-1684541589759)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\1675937405157.png)]
双端队列,两端都可以插入
lpush list one
lrange list 0 -1 #获取list中的值
rpush list right #将一个值或者多个值,插入到列表尾部(右)
range list 0 -1 #通过区间获取具体的值
往list中移除:
lpop list #移除list的第一个元素
rpop list #移除list的最后一个元素
lrange list 0 -1
#移除指定的值
取关 UID
lrem list 2 three #移除list中的两个three值
llen list #获取list中的值得个数
trim修剪
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hc0x85Ip-1684541589765)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\1675937137595.png)]
rpoplpush # 移除列表的最后一个元素并将它移动到最后一个列表中!(移动)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GA2yp71e-1684541589768)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\1675937804320.png)]
lset #将列表中指定下标的值替换为另外一个值(更新)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dU9nTYUS-1684541589771)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\1675938097492.png)]
linsert #将某个具体的value插入到列表中某个元素的前面或者后面
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ARcsaNd9-1684541589778)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\1675938311502.png)]
总结:
- 它实际上是一个链表,before Node 或者after Node都可以插入值
- 如果key值不存在,需要创建新的列表
- 如果key存在,新增内容
- 如果移除了所有值,空链表,也代表不存在。
- 在两边插入或改动值,效率d最高,中间元素,效率会相对低一点。
消息排队!消息队列 Lpush Rpop
Lpush Lpop(栈)
Set(集合)
set中的值不可以重复且无序
向set集合中存值,取值,判断某个值是否在set中
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uwmkJbnz-1684541589802)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\1675939036008.png)]
sadd myset "hello" #set集合中添加值
smembers myset #查看指定set集合中所有的值
sismember myset hello #判断某个值是否在set集合中
srem myset "hello" #移除set集合中指定的元素
scard myset # 获取set元素中内容元素个数
sdiff myset myset1 #获取myset和myset1中的差集
sinter myset myset1 #获取myset和myset1中的交集
sunion myset myset1 #获取myset和myset1中的并集
Hash(哈希)
Map集合,key-value
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Mmj06x6m-1684541589805)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\1676882996376.png)]
hset
hget
hmset
hmget
hmgetall
hdel
5.通过Jedis操作redis
SpringBoot 2.4.5版本
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
插件的添加
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>8</source>
<target>8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.4.5</version>
</plugin>
</plugins>
</build>
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.5</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.itheima</groupId>
<artifactId>jedis_demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>jedis_demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.8.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>8</source>
<target>8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.4.5</version>
</plugin>
</plugins>
</build>
</project>
编写测试类,连接本地redis数据库,实现相应的操作。
package com.itheima.test;
import org.junit.Test;
import redis.clients.jedis.Jedis;
import java.util.Set;
/**
* 使用Jedis操作Redis
*/
public class JedisTest {
@Test
public void testRedis(){
//获取连接
Jedis jedis = new Jedis("localhost",6379);
//执行其他的操作
jedis.set("username","xiaomeng");
String value = jedis.get("username");
System.out.println(value);
// jedis.del("username");
jedis.hset("myhash","addr","bj");
String hget = jedis.hget("myhash", "addr");
System.out.println(hget);
Set<String> keys = jedis.keys("*");
for (String key : keys) {
System.out.println(key);
}
//关闭连接
jedis.close();
}
}
6.通过SpringDataRedis操作redis
SpringBoot 2.4.5版本
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
插件的添加
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>8</source>
<target>8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.4.5</version>
</plugin>
</plugins>
</build>
6.1、pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.5</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.itheima</groupId>
<artifactId>springdataredis_demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springdataredis_demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.8.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>8</source>
<target>8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
6.2、application.yml
spring:
application:
name: springdataredis_demo
#redis相关配置
redis:
host: localhost
port: 6379
#password:123456
database: 0 #操作的是0号数据库
jedis:
#redis连接池配置
pool:
max-active: 8 #最大连接数
max-wait: 1ms #连接池最大阻塞连接时间
max-idle: 4 #连接池中的最大空闲连接
min-idle: 0 #连接池中的最小空闲连接
6.3、编写测试单元
package com.itheima;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.*;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
@SpringBootTest
@RunWith(SpringRunner.class)
class SpringdataredisDemoApplicationTests {
@Autowired
private RedisTemplate redisTemplate;
/**
* 操作Sting类型数据
*/
@Test
public void testString(){
ValueOperations valueOperations = redisTemplate.opsForValue();
valueOperations.set("city","beijing");
String value = (String)redisTemplate.opsForValue().get("city");
System.out.println(value);
redisTemplate.opsForValue().set("key1","value1",10l, TimeUnit.SECONDS);
System.out.println(redisTemplate.opsForValue().setIfAbsent("city", "nanjing"));
}
/**
* 操作hash类型数据
*/
@Test
public void testHash(){
HashOperations hashOperations = redisTemplate.opsForHash();
//存值
hashOperations.put("001","name","xiaoming");
hashOperations.put("001","age","29");
hashOperations.put("001","address","nanjing");
//取值
String name = (String) hashOperations.get("001", "name");
System.out.println(name);
//获取hash结构中的所有字段
Set<String> keys = hashOperations.keys("001");//hashMap中的getKeys()方法相似
for (String key : keys) {
System.out.println(key);
}
//获取hash结构中的所有值
List<String> values = hashOperations.values("001");//hashMap中的getValues()方法相似
for (String value : values) {
System.out.println(value);
}
}
/**
* 操作list类型的数据(有序,可重复)
*/
@Test
public void testlist(){
ListOperations listOperations = redisTemplate.opsForList();
//存值
listOperations.leftPush("mylist","a");
listOperations.leftPushAll("mylist","b","c","d");
//取值
List<String> mylist = listOperations.range("mylist", 0, -1);
for (String value : mylist) {
System.out.println(value);
}
//获取队列长度llen
Long size = listOperations.size("mylist");
int iSize = size.intValue();
for (int i = 0; i < iSize; i++) {
//出队列
String element = (String) listOperations.rightPop("mylist");
System.out.println(element);
}
}
/**
* 操作Set类型数据(无序,无重复元素)
*/
@Test
public void testSet(){
SetOperations setOperations = redisTemplate.opsForSet();
//存值
setOperations.add("myset","a","b","c","a");
//取值
Set<String> myset = setOperations.members("myset");
for (String s : myset) {
System.out.println(s);
}
//删除成员
setOperations.remove("myset","a","b");
//取值
myset = setOperations.members("myset");
for (String s : myset) {
System.out.println(s);
}
}
/**
* 操作ZSet类型数据(有序无重复元素)排序规则:从小到大
*/
@Test
public void testZSet(){
ZSetOperations zSetOperations = redisTemplate.opsForZSet();
//存值
zSetOperations.add("myZSet","a",10.0);
zSetOperations.add("myZSet","b",12.0);
zSetOperations.add("myZSet","d",13.0);
zSetOperations.add("myZSet","a",14.0);
//取值
Set<String> myZSet = zSetOperations.range("myZSet", 0, -1);
for (String s : myZSet) {
System.out.println(s);
}
//修改分数
zSetOperations.incrementScore("myZSet","b",20.0);
//取值
myZSet = zSetOperations.range("myZSet", 0, -1);
for (String s : myZSet) {
System.out.println(s);
}
//删除成员
zSetOperations.remove("myZSet","a","b");
//取值
myZSet = zSetOperations.range("myZSet", 0, -1);
for (String s : myZSet) {
System.out.println(s);
}
}
/**
* 通用操作,针对不同的数据类型都可以操作
*/
@Test
public void testCommon(){
//获取redis中所有的key
Set<String> keys = redisTemplate.keys("*");
for (String key : keys) {
System.out.println(key);
}
//判断某个key是存在
Boolean itcast = redisTemplate.hasKey("itcast");
System.out.println(itcast);
//删除指定的key
redisTemplate.delete("myZSet");
//获取指定key对应的value的数据类型
DataType dataType = redisTemplate.type("myset");
System.out.println(dataType);
}
}
7.缓存穿透(查不到)
用户想要查询一个数据,发现redis内存数据库没有,也就是缓存没有命中,于是向持久层数据库查询,发现也没有,于是本次查询失败,当用户很多的时候,缓存都没有命中(秒杀系统!)于是都去请求了持久层数据库,这会给持久层数据库造成很大的压力,就相当于出现了缓存穿透。
解决办法:
布隆过滤器
缓存空对象
8.缓存击穿(量太大,缓存过期!)
是指一个key非常热点,在不停的扛着大并发,大并发集中对这一个点进行访问,当这个key在过期的瞬间,有大量的请求并发访问,这个数据一般是热点数据,由于缓存过期,会同时访问数据库来查询最新数据,并且回写缓存,会导致数据库瞬间压力过大。
解决办法:
热点数据不过期
加分布式锁
9.缓存雪崩
指在某一个时间段,缓存集中过期失效,redis宕机。
三.三级缓存定义
intln(key);
}
//判断某个key是存在
Boolean itcast = redisTemplate.hasKey("itcast");
System.out.println(itcast);
//删除指定的key
redisTemplate.delete("myZSet");
//获取指定key对应的value的数据类型
DataType dataType = redisTemplate.type("myset");
System.out.println(dataType);
}
}
## 7.缓存穿透(查不到)
用户想要查询一个数据,发现redis内存数据库没有,也就是缓存没有命中,于是向持久层数据库查询,发现也没有,于是本次查询失败,当用户很多的时候,缓存都没有命中(秒杀系统!)于是都去请求了持久层数据库,这会给持久层数据库造成很大的压力,就相当于出现了缓存穿透。
解决办法:
布隆过滤器
缓存空对象
## 8.缓存击穿(量太大,缓存过期!)
是指一个key非常热点,在不停的扛着大并发,大并发集中对这一个点进行访问,当这个key在过期的瞬间,有大量的请求并发访问,这个数据一般是热点数据,由于缓存过期,会同时访问数据库来查询最新数据,并且回写缓存,会导致数据库瞬间压力过大。
解决办法:
热点数据不过期
加分布式锁
## 9.缓存雪崩
指在某一个时间段,缓存集中过期失效,redis宕机。
# 三.三级缓存定义
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-q0Rv4sJs-1684541589812)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\1679795535806.png)]