简介
NOSQL(No Only SQL)仅仅是一个概念,泛指非关系型的数据库,区别于关系数据库,它们不保证关系数据的ACID特性。
NOSQL的四大类型
键值(Key-Value)存储数据库
相关产品:Redis、Riak、SimpleDB、Chordless、Scalaris、Memcached
应用:内容缓存,处理大量数据的高负载访问,也用于系统日志
优点:扩展性好、灵活性好、大量写操作时性能高
缺点:无法存储结构化信息、条件查询效率较低
使用者:百度云(Redis)、GitHub(Riak)、BestBuy(Riak)、Twitter(Ridis和Memcached)
列存储数据库
相关产品:BigTable、HBase、Cassandra、HadoopDB、GreenPlum、PNUTS
应用:分布式数据存储与管理
优点:查找速度快、可扩展性强、容易进行分布式扩展、复杂性低
缺点:写入次数多,速度慢,消耗cpu
使用者:Ebay(Cassandra)、Instagram(Cassandra)、NASA(Cassandra)、Facebook(HBase)
文档型数据库
相关产品:MongoDB、CouchDB、ThruDB、CloudKit、Perservere、Jackrabbit
应用:存储、索引并管理面向文档的数据或者类似的半结构化数据
优点:性能好、灵活性高、复杂性低、数据结构灵活
缺点:缺乏统一的查询语言
使用者:百度云数据库(MongoDB)、SAP(MongoDB)
图形(Graph)数据库
相关产品:Neo4J、OrientDB、InfoGrid、GraphDB
应用:大量复杂、互连接、低结构化的图结构场合,如社交网络、推荐系统等
优点:灵活性高、支持复杂的图形算法、可用于构建复杂的关系图谱
缺点:复杂性高、只能支持一定的数据规模
使用者:Adobe(Neo4J)、Cisco(Neo4J)、T-Mobile(Neo4J)
不同分类特点对比
分类 | Examples举例 | 典型应用场景 | 数据模型 | 优点 | 缺点 |
键值(key-value) | Tokyo Cabinet/Tyrant, Redis, Voldemort, Oracle BDB | 内容缓存,主要用于处理大量数据的高访问负载,也用于一些日志系统等等。 | Key 指向 Value 的键值对,通常用hash table来实现 | 查找速度快 | 数据无结构化,通常只被当作字符串或者二进制数据 |
列存储数据库 | Cassandra, HBase, Riak | 分布式的文件系统 | 以列簇式存储,将同一列数据存在一起 | 查找速度快,可扩展性强,更容易进行分布式扩展 | 功能相对局限 |
文档型数据库 | CouchDB, MongoDb | Web应用(与Key-Value类似,Value是结构化的,不同的是数据库能够了解Value的内容) | Key-Value对应的键值对,Value为结构化数据 | 数据结构要求不严格,表结构可变,不需要像关系型数据库一样需要预先定义表结构 | 查询性能不高,而且缺乏统一的查询语法。 |
图形(Graph)数据库 | Neo4J, InfoGrid, Infinite Graph | 社交网络,推荐系统等。专注于构建关系图谱 | 图结构 | 利用图结构相关算法。比如最短路径寻址,N度关系查找等 | 很多时候需要对整个图做计算才能得出需要的信息,而且这种结构不太好做分布式的集群方案。 |
优点:易扩展,NoSQL数据库种类繁多,但是一个共同的特点都是去掉关系数据库的关系型特性。数据之间无关系,这样就非常容易扩展。无形之间也在架构的层面上带来了可扩展的能力。(数据类型多样,不需要事先设计数据库)大数据量,高性能,NoSQL数据库都具有非常高的读写性能,尤其在大数据量下,同样表现优秀。这得益于它的无关系性,数据库的结构简单。
Redis
Redis,英文全称是Remote Dictionary Server(远程字典服务),是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。
与MySQL数据库不同的是,Redis的数据是存在内存中的。它的读写速度非常快,每秒可以处理超过10万次读写操作。因此redis被广泛应用于缓存,另外,Redis也经常用来做分布式锁。除此之外,Redis支持事务、持久化(RDB,AOF)、LUA 脚本、LRU 驱动事件、多种集群方案(主从集群,分片集群)。
安装Redis
linux服务器部署Redis (linux版本 CentOS 7)
Reids是基于C语言编写的,因此需要安装redis的依赖gcc
yum install -y gcc tcl
下载Redis
redis官网: https://redis.io/download
版本: https://download.redis.io/releases/
上传安装包并解压
现在大多数Linux服务器都带有lrzsz程序,方便上传下载文件,如果没有,可以直接下载rpm包安装下就好。
lrzsz安装包链接: http://down.51cto.com/data/2322755
1、rz(笔记本上传给服务器)
rz
选择笔记本上文件传给服务器
2、sz(服务器上的文件拷贝给笔记本)
sz test.tar
把服务器的test.tar拷贝给笔记本
如下图,输入 rpm -qa |grep sz 和 rpm -qa |grep rz 命令查看是否安装该程序。
如果没有安装,可以输入 yum -install sz yum -install rz命令进行安装
把安装包上传到linux
输入 命令 rz ,如下图所示弹出一个文本框,选择要上传的文件上传即可
一般情况下,把安装包上传到 usr/local/src路径下.
解压安装包
tar -zxvf redis-7.0.9.tar.gz
解压成功后
运行编译命令,默认安装在/usr/local/bin目录下
进入到 cd ./redis-7.0.9目录下运行编译命令
make && make install
安装成功后
redis-cli:redis提供的命令行客户端
redis-server:redis的服务端启动脚本
redis-sentinel:redis的哨兵启动脚本
reis-benchmark: redis的性能测试
性能测试语法:
redis-benchmark [参数] [参数值]
-h 指定服务器主机名 默认值为 127.0.0.1
-p 指定服务器端口 默认值为 6379
-s 指定服务器socket
-c 指定并发连接数 默认值为 50
-n 指定请求数 默认值为 10000
-d 以字节的形式指定 SET/GET 值的数据大小 默认值为 2
-k 1=keep alive 0=reconnect 默认值为 1
-r SET/GET/INCR 使用随机 key, SADD 使用随机值
-P 通过管道传输 请求 默认值为 1
-q 强制退出 redis。仅显示 query/sec 值
–csv 以 CSV 格式输出
-l 生成循环,永久执行测试
-t 仅运行以逗号分隔的测试命令列表。
-I Idle 模式。仅打开 N 个 idle 连接并等待。
默认启动(前台启动)redis
在任意目录输入redis-server命令即可启动Redis:
这种启动属于前台启动,会阻塞整个会话窗口,窗口关闭或者按下CTRL + C则Redis停止。不推荐使用
Redis后台启动需要修改 安装包usr/local/src/redis-7.0.9下的redis.conf配置文件
防止修改配置文件时出错,建议先进行备份
文件备份: cp redis.conf redis.conf.bck
修改配置文件
允许访问的地址 bind 127.0.0.1 -::1 这个地址只能在本地访问,设置为0.0.0.0可以在任意IP访问,生产地址不要这样设置
bind 0.0.0.0
# 守护进程,修改为yes后即可后台运行
daemonize yes
# 密码,设置后访问Redis必须输入密码 我这里密码设置为redis
requirepass redis
。。。。。。。。。。。。。。。。。。。下面的也可以不修改。。。。。。。。。。。。。。
# 监听的端口
port 6379
# 工作目录,默认是当前目录,也就是运行redis-server时的命令,日志、持久化等文件会保存在这个目录
dir .
# 数据库数量,设置为1,代表只使用1个库,默认有16个库,编号0~15
databases 1
# 设置redis能够使用的最大内存
maxmemory 512mb
# 日志文件,默认为空,不记录日志,可以指定日志文件名
logfile "redis.log"
第16和10配合使用更方便查询日志
修改完之后启动配置文件
在安装包下启动
redis-server redis.conf
可以利用进程查看Redis是否启动
关闭进程
kill -9 7362
Redis还可以做到开机自启
创建一个配置文件
vi /etc/systemd/system/redis.service
把下面的内容复制进去
[Unit]
Description=redis-server
After=network.target
[Service]
Type=forking
ExecStart=/usr/local/bin/redis-server /usr/local/src/redis-7.0.9/redis.conf
PrivateTmp=true
[Install]
WantedBy=multi-user.target
配置好之后,重载系统服务,这部操作,只是把redis交给系统管理了,还没有开机自启
systemctl daemon-reload
Redis自启需要一下几个命令
启动Redis
systemctl start redis
停止redis
systemctl stop redis
开机自启命令
systemctl enable redis
重新启动redis
systemctl restart redis
查看redis状态
systemctl status redis
查看进程,自启已经开始
Redis客户端
安装完之后,操作redis需要用redis客户端,包括
命令行客户端
图形化界面客户端
编程客户端
命令行客户端 redis-cli:
redis安装完成之后就包含了命令行客户端 : redis-cli
语法格式:
redis-cli [options] [commonds]
常见的options有:
-h 127.0.0.1 | 指定要连接的redis节点的IP地址 默认为127.0.0.1 |
-p 6379 | 指定要连接的redis节点端口 默认为6379 |
-a redis | 指定访问的redis密码 |
其中commonds就是redis的操作命令,例如:
ping | 与redis服务端做心跳测试 |
不指定commonds时,会进入redis-cli的交互控制台
连接redis
连接redis时,使用127.0.0.1和IP地址都可以,如下图所示
上面的警告信息是提示-a这种方式指定密码不安全,所以我们用Auth命令指定用户名和密码
图形化界面客户端
图形化界面客户端不是官方提供,是Github编写的redis的图形化界面客户端,地址:GitHub - RedisInsight/RedisDesktopManager,不过该仓库提供了RedisDesktopManager的源码,没有提供windows安装包
安装包地址:Releases · lework/RedisDesktopManager-Windows (github.com)
下载解压后如图所示
运行.exe文件进行安装,傻瓜式安装
连接redis服务器
连接成功可以在界面上操作redis,建议初学者,使用命令行操作redis
Redis数据结构
redis是一个key-value数据库,key一般是String类型,
value类型多种多样 ,例如:
(String,hash,set,list,sortedset,GEO,bitMap,HyperLog)
Redis通用命令
通过 help[command] 可以查看一个命令的基本用法
keys 查看所有符合的key值 ,因为是模糊查询性能差,不建议在生产环境使用
例:
DEL 删除指定的key
例:
Exists 判断key是否存在
EXPIRE 给一个key设置有效期,有效期到期时该key会被自动删除
TTL 查看key的剩余有效时间
Redis的String类型
String类型 Redis中最简单的存储类型。value是字符串。
value是字符串,根据字符串的类型不同又可以分为三类
String 普通字符串
int 整数类型 可以做自增、自减操作
float 浮点类型,可以做自增、自减操作
不管那种类型,底层都是字节数组形式存储,只不过编码方式不同,字符串类型最大空间不超过512M
String类型的常见命令
SET:添加或者修改已经存在的一个
String类型的键值对
GET:根据key获取
String类型的value
MSET:批量添加多个String类型的键值对
MGET:根据多个key获取多个String类型的value
INCR:让一个整型的key自增1
INCRBY:让一个整型的key自增并指定步长,例如:incrby num 2 让num值自增2
INCRBYFLOAT:让一个浮点类型的数字自增并指定步长
SETNX:添加一个String类型的键值对,前提是这个key不存在,否则不执行
SETEX:添加一个String类型的键值对,并且指定有效期
Redis的key允许有多个单词形成层级结构,多个单词之间用“:”隔开 格式如下图所示
Redis的Hash类型
Hash类型,也叫散列,其value是一个无序字典,类似于Java中的HashMap结构。
String结构是将对象序列化为JSON字符串后存储,当需要修改对象某个字段时很不方便
Hash结构可以将对象中的每个字段独立存储,可以针对单个字段做CRUD
HSET key field value:添加或者修改hash类型key的field的值
HGET key field:获取一个hash类型key的field的值
HMSET:批量添加多个hash类型key的field的值
HMGET:批量获取多个hash类型key的field的值
HGETALL:获取一个hash类型的key中的所有的field和value
HKEYS:获取一个hash类型的key中的所有的field
HVALS:获取一个hash类型的key中的所有的value
HINCRBY:让一个hash类型key的字段值自增并指定步长
HSETNX:添加一个hash类型的key的field值,前提是这个field不存在,否则不执行
示例:
Redis的List类型
Redis中的List类型与Java中的LinkedList类似,可以看做是一个双向链表结构,既可以支持正向检索,也支持反向检索。常用来存储一些有序数据
特点:
有序
元素可以重复
插入和删除快,查询慢
List常见命令:
LPUSH key element ... :向列表左侧插入一个或多个元素
LPOP key:移除并返回列表左侧的第一个元素,没有则返回nil
RPUSH key element ... :向列表右侧插入一个或多个元素
RPOP key:移除并返回列表右侧的第一个元素
LRANGE key star end:返回一段角标范围内的所有元素
BLPOP和BRPOP:与LPOP和RPOP类似,只不过在没有元素时等待指定时间,而不是直接返回nil
如何利用List结构模拟一个栈?
入口和出口在同一边
如何利用List结构模拟一个队列?
入口和出口在不同边
如何利用List结构模拟一个阻塞队列?
入口和出口在不同边
出队时采用BLPOP或BRPOP
Redis的set类型
Redis的Set结构与Java中的HashSet类似,可以看做是一个value为null的HashMap。
因为也是一个hash表,因此具备与HashSet类似的特征:
无序
元素不可重复
查找快
支持交集、并集、差集等功能
Set常见命令:
SADD key member ... :向set中添加一个或多个元素
SREM key member ... : 移除set中的指定元素
SCARD key: 返回set中元素的个数
SISMEMBER key member:判断一个元素是否存在于set中
SMEMBERS:获取set中的所有元素
SINTER key1 key2 ... :求key1与key2的交集
SDIFF key1 key2 ... :求key1与key2的差集
SUNION key1 key2 ..:求key1和key2的并集
Redis的SortedSet类型:
Redis的SortedSet是一个可排序的set集合,与Java中的TreeSet有些类似,但底层数据结构却差别很大。
SortedSet中的每一个元素都带有一个score属性,可以基于score属性对元素排序,底层的实现是一个跳表(SkipList)加 hash表。
SortedSet具备下列特性:
可排序
元素不重复
查询速度快
因为SortedSet的可排序特性,经常被用来实现排行榜这样的功能。
ZADD key score member:添加一个或多个元素到sorted set ,如果已经存在则更新其score值
ZREM key member:删除sorted set中的一个指定元素
ZSCORE key member : 获取sorted set中的指定元素的score值
ZRANK key member:获取sorted set 中的指定元素的排名
ZCARD key:获取sorted set中的元素个数
ZCOUNT key min max:统计score值在给定范围内的所有元素的个数
ZINCRBY key increment member:让sorted set中的指定元素自增,步长为指定的increment值
ZRANGE key min max:按照score排序后,获取指定排名范围内的元素
ZRANGEBYSCORE key min max:按照score排序后,获取指定score范围内的元素
ZDIFF、ZINTER、ZUNION:求差集、交集、并集
注意:所有的排名默认都是升序,如果要降序则在命令的Z后面添加REV即可
Redis的JAVA客户端
搭建redis-java项目
Jedis
Jedis快速入门
导入依赖
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>4.3.2</version>
</dependency>
编写测试类
package com.test.jedis;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import redis.clients.jedis.Jedis;
import java.util.List;
/**
* @author
* @version 1.0
* @description: TODO
* @date 2023/4/25 16:41
*/
public class ProjectTest {
private Jedis jedis;
@Before
public void connectRedis(){
//连接redis
jedis=new Jedis("192.168.9.128",6379);
//输入redis密码
jedis.auth("redis");
//设置redis数据库
jedis.select(0);
}
@Test
public void RedisTest(){
//存储数据
jedis.set("name","张三");
jedis.mset("age", "12","sex","男");
List<String> mget = jedis.mget("name", "age", "sex");
//获取数据
System.out.println(mget.toString());
}
@After
public void stopRedis(){
//关闭连接
if(jedis!=null){
jedis.close();
}
}
}
@Before注释函数将在具有@Test注释的类中的每个测试函数之前执行 =用法= @beforeEach
@After注释函数将在类中具有@Test批注的每个测试函数之后执行 =用法= aftereach
测试结果
Jedis连接池
Jedis本身是线程不安全的,并且频繁的创建和销毁连接会有性能损耗,因此推荐使用Jedis连接池代替Jedis的直连方式
连接池配置 (也可以在yml文件中配置 利用@Value获取)
package com.redis.test.util;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
/**
* @author
* @version 1.0
* @description: TODO
* @date 2023/4/26 11:20
*/
public class RedisConnectionFactory {
private static final JedisPool jedisPool;
static {
//连接池配置
JedisPoolConfig config = new JedisPoolConfig();
//创建连接池最大连接数
config.setMaxTotal(8);
//设置最大空闲连接
config.setMaxIdle(8);
//设置最小空闲连接 空闲连接一直空着占用内存,所以超过一段时间以后一直没有连接,空闲连接就会被释放
config.setMinIdle(0);
//设置连接等待时长 毫秒 连接池没有可用的空闲连接时,最多等待多长时间 默认值为 -1 无限制等待,直到有连接池可用
config.setMaxWaitMillis(1000);
//minEvictableIdleTimeMillis 连接池中连接,在时间段内一直空闲, 被逐出连接池的时间
config.setMinEvictableIdleTimeMillis(2000);
//配置连接池对象 ms
jedisPool=new JedisPool(config,"192.168.9.128",6379,1000,"redis");
}
public static Jedis getJedis(){
return jedisPool.getResource();
}
}
快速入门里测试类的连接方式修改一下
这里测试时有一个问题,
Jedis测试时,因为我用的版本是<version>4.3.2</version>,报如下错误
加上setMinEvictableIdleTimeMillis参数还是不行,最后换了一个版本<version>3.3.0</version>测试成功。
还有一个需要注意一下:
在关闭连接时,如果有连接池,close()方法是归还到连接池中而不是销毁
源码如下:
SpringDataRedis
SpringData是Spring中数据操作的模块,包含对各种数据库的集成,其中对Redis的集成模块就叫做SpringDataredis
官网地址:http://spring.io/projects/spring-data-redis
提供了对不同Redis客户端的整合(Lettuce和Jedis)
提供了Redistemplate统一API来操作Redis
支持Redis的发布订阅模型
支持Redis哨兵和Redis集群
支持基于Lettuce的响应式编程
支持基于JDK、JSON、字符串、Spring对象的数据序列化及反序列化
支持基于Redis的JDKCollection实现
SpringDataRedis中提供了RedisTemplate工具类,其中封装了各中对Redis的操作。并且将不同数据类型的操作API封装到了不同的类型中:
导入依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- 连接池依赖-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
编写yml文件
spring:
redis:
host: 192.168.9.128
port: 6379
password: redis
lettuce:
pool:
max-active: 8
max-idle: 8
min-idle: 0
max-wait: 1000ms
测试RedisTemplate连接Redis
package com.redis.test;
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.RedisTemplate;
/**
* @author
* @version 1.0
* @description: TODO
* @date 2023/4/25 16:41
*/
@SpringBootTest
public class ProjectTest {
@Autowired
private RedisTemplate redisTemplate;
@Test
public void redisConnection(){
redisTemplate.opsForValue().set("name","历史");
Object name = redisTemplate.opsForValue().get("name");
System.out.println(name);
}
}
测试结果:
RedisTemplate序列化问题
使用RedisTemplate用一个问题,RedisTemplate是使用序列化写入值
方法可以存储任何类型的数据 如下
SpringDataRedis会把储存的数据转化为Redis可以处理的字节
源码如下:
RedisTemplate默认提供了四种序列化方式
如果不指定序列化方式,那么默认是采用JDK序列化
把java对象转化为字节,造成可读性差,内存占用比较大(存储的数据类似乱码的情况)
解决这种情况,需要改变redisTemplate序列化对象RedisSerializer
StringRedisSerializer序列只能处理String类型数据
GenericJackson2JsonRedisSerializer序列可以处理对象类型数据
修改Redistemplate序列后,代码如下:
package com.redis.test.config;
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.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.nio.charset.Charset;
/**
* @author
* @version 1.0
* @description: TODO
* @date 2023/4/27 17:18
*/
@Configuration
public class RedisConfig {
//解决redisTemplate存储的数据乱码问题
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
//创建RedisTemplate对象
RedisTemplate<String, Object> template = new RedisTemplate<>();
//设置连接工厂
template.setConnectionFactory(factory);
//设置key的序列化
template.setKeySerializer(RedisSerializer.string());
template.setHashKeySerializer(RedisSerializer.string());
//设置value的序列化
template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
return template;
}
}
Redis缓存
缓存(Cache)就是数据交换的缓冲区,是存储数据的临时地方,一般读写性能较高。
添加Redis缓存
原始的是,客户端发起请求访问数据库,数据库查到信息响应给客户端;
添加redis缓存后,客户端发起请求,先去redis缓存中查找数据,如果有数据,redis就返回给客户端,没有数据,客户端去数据库中查找,数据库返回给客户端,并且把数据存储到redis缓存,下次请求直接在redis缓存中获取。
示例: