NoSQL非关系型数据库
NoSQL概念
Not Only SQL:不仅仅是SQL,指的就是非关系型数据库,它是关系型数据库有益的补充。最终的数据还是保存在关系型数据库中。非关系型数据库主要是提升数据库的查询速度,一般做为数据的缓存来使用。
非关系型数据库
非关系型数据库严格上不是一种数据库,应该是一种数据结构化存储方法的集合,可以是文档或者键值对等。
优点
- 格式灵活:存储数据的格式可以是key,value形式、文档形式、图片形式等等,使用灵活,应用场景广泛,而关系型数据库则只支持基础类型。
- 速度快:nosql可以使用硬盘或者随机存储器作为载体,而关系型数据库只能使用硬盘;
- 成本低:nosql数据库部署简单,基本都是开源软件。
缺点
- 不提供sql支持,学习和使用成本较高;
- 数据结构相对复杂,复杂查询方面不方便。
为什么要使用NOSQL
具体表现为对如下三高问题的解决:
High Performance - 数据库高并发访问
在同一个时间点,同时有海量的用户并发访问。往往要达到每秒上万次读写请求。关系数据库应付上万次SQL查询还勉强顶得住,但是应付上万次SQL写数据请求,硬盘IO就已经无法承受了。
-
如天猫的双11,从凌晨0点到2点这段时间,每秒达到上千万次的访问量。
-
12306春运期间,过年回家买火车抢票的时间,用户不断查询有没有剩余票。
Huge Storage - 海量数据的存储
数据库中数据量特别大,数据库表中每天产生海量的数据。
类似QQ,微信,微博,每天用户产生海量的用户动态,每天产生几千万条记录。对于关系数据库来说,在一张几亿条记录的表里面进行SQL查询,效率是极其低下乃至不可忍受的。
High Scalability && High Availability- 高可扩展性和高可用性的需求
关系型数据库进行扩展和升级是比较麻烦的一样事,对于很多需要提供24小时不间断服务的网站来说,对数据库系统进行升级和扩展是非常痛苦的事情,往往需要停机维护和数据迁移。
非关系型数据库可以通过不断的添加服务器节点来实现扩展,而不需对原有的数据库进行维护。
Redis的目录文件
目录或文件 | 作用 |
---|---|
redis-benchmark.exe | 用于性能测试一个工具命令 |
redis-check-aof.exe | AOF文件的检查和修复工具 (AOF是它的一种文件存储格式) |
redis-check-dump.exe | RDB文件的检查和修改工具 (RDB是它的一种文件存储格式) |
redis-cli.exe | 客户端启动程序 |
redis-server.exe | 服务器端启动程序 (不会自动启动,默认每次都要手动开启) |
redis.window.conf | 服务器配置文件 |
string类型的操作命令
Redis的5种数据类型
redis是一种高级的key-value的存储系统,其中value支持五种数据类型,指的是它值的类型,键可以认为是字符串类型。redis不是用Java写的,是C语言写的。
值的数据类型 | 说明 |
---|---|
string类型 | 字符串 |
list类型 | 列表:元素可以重复,元素是有索引号,有先后顺序的 |
set类型 | 集合:元素是不可重复的,元素没有索引号,没有先后顺序的 |
hash类型 | 值由多个键值对组成 |
zset类型 | 集合:元素不可重复的,每个元素有索引号,还有一个分数值,可以根据分数进行排序 |
字符串类型string
在Redis中以二进制保存,没有编码和解码的过程。
无论存入的是字符串、整数、浮点类型都会以字符串写入。
在Redis中字符串类型的值最多可以容纳的数据长度是512M,这是以后最常用的数据类型。
常用命令
命令 | 功能 |
---|---|
set 键 值 | 存入字符串类型的键和值,如果键不存在就是添加,存在就是修改 |
setnx 键 值 | 键不存在就是添加,存在不做任何操作,不会覆盖以前的键和值 |
get 键 | 通过键获取值 |
del 键 | 通过键删除键和值 |
list类型的操作命令
概述
在Redis中,List类型是按照插入顺序排序的字符串链表。和数据结构中的普通链表一样,我们可以在其左部(left)和右部(right)添加新的元素。
在插入时,如果该键并不存在,Redis将为该键创建一个新的链表。
如果链表中所有的元素均被移除,那么该键也将会被从数据库中删除。
List中可以包含的最大元素数量是4G个(41亿个)
常用命令
命令 | 行为 |
---|---|
lpush 键 元素 元素 | 从左边添加1个或多个元素 |
rpush 键 元素 元素 | 从右边添加1个或多个元素 |
lpop 键 | 删除最左边的一个元素,并且返回 |
rpop 键 | 删除最右边的一个元素,并且返回 |
lrange 键 开始 结束 | 查找指定索引范围内元素返回,每个元素有2个索引号 索引号从左向右:0~length-1 索引号从右向左:-1~-length 如果要获取整个列表中所有的元素,索引号范围如何写?0~-1 |
llen 键 | 获取列表中有多少个元素 |
命令演示
一个Redis服务器可以包括多个数据库,客户端可以只连接Redis中某个数据库,就好比一个mysql服务器中创建多个数据库,客户端连接时指定连接到哪个数据库。
Redis中有db0-db15编号的16个数据库。我们不能创建新的数据库,也不能删除数据库。数据库中也没有表的结构,客户端默认连接第0个数据库。但可以通过配置文件设定有多少个数据库。
Jedis类常用方法
-
每个方法就是redis中的命令名,方法的参数就是命令的参数。
-
每个Jedis对象似于JDBC中Connection对象,获取一个Jedis对象本质上就是获取一个连接对象。
连接和关闭 | 功能 |
---|---|
new Jedis(String host, int port) | 创建一个连接对象 参数1:主机名 参数2:端口号 6379 |
void close() | 关闭连接 |
对string操作的方法 | 说明 |
---|---|
set(String key,String value) | 添加字符串类型的键和值 |
String get(String key) | 通过键获取值 |
del(String … keys) | 删除一个或多个键和值 |
对list操作的方法 | 说明 |
---|---|
lpush(String key,String…values) | 从左边添加1个或多个元素 |
List<String> lrange(String key,long start,long end) | 获取一个范围内所有的元素 |
代码
package com.itheima;
import redis.clients.jedis.Jedis;
/**
* Jedis的基本使用
*/
public class Demo1Base {
public static void main(String[] args) {
//1.创建Jedis连接对象
Jedis jedis = new Jedis("localhost", 6379);
//2.向服务器添加1个字符串类型的键和值
jedis.set("book","人鬼情喂鸟");
//3.从服务器中通过键获取值
String book = jedis.get("book");
//4.关闭连接
jedis.close();
//5.打印输出到控制台
System.out.println(book);
}
}
Jedis连接池的使用
jedis连接资源的创建与销毁是很消耗程序性能,所以jedis为我们提供了jedis的连接池技术,jedis连接池在创建时初始化一些连接对象存储到连接池中,使用jedis连接资源时不需要自己创建jedis对象,而是从连接池中获取一个资源进行redis的操作。使用完毕后,不需要销毁该jedis连接资源,而是将该资源归还给连接池,供其他请求使用。
Jedis连接池API
用于创建连接池的配置信息
JedisPoolConfig配置类 | 功能说明 |
---|---|
JedisPoolConfig() | 构造方法,创建一个配置对象 |
void setMaxTotal() | 连接池中最大连接数 |
void setMaxWaitMillis() | 设置最长等待时间,单位是毫秒 |
JedisPool连接池类 | 说明 |
---|---|
JedisPool(配置对象,服务器名,端口号) | 构造方法,创建连接池的类 参数1:上面的配置对象 参数2:服务器名 参数3:端口号 |
Jedis getResource() | 从连接池中获取一个创建好的连接对象,返回Jedis对象 |
JedisPool的基本使用
需求:
使用连接池优化jedis操作,从连接池中得到一个创建好的Jeids对象,并且使用这个Jedis对象。向Redis数据库写入一个set集合,并且取出集合。打印到控制台,并且查看数据库中信息。
开发步骤
- 创建连接池配置对象,设置最大连接数10,设置用户最大等待时间2000毫秒
- 通过配置对象做为参数,创建连接池对象
- 从连接池里面获取jedis连接对象,执行redis命令。
- 执行redis命令写入list集合
- 执行redis命令读取list集合
- 输出读取的数据
- 关闭连接对象(通常连接池不关闭)
执行代码
package com.itheima;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import java.util.List;
/**
* Jedis连接池的基本使用
*/
public class Demo2Pool {
public static void main(String[] args) {
//1.创建连接池的配置对象
JedisPoolConfig config = new JedisPoolConfig();
//2.设置连接池的参数
config.setMaxTotal(10); //最大连接数
config.setMaxWaitMillis(2000); //最长等待时间为2秒钟
//3.创建连接池,使用上面配置对象
JedisPool pool = new JedisPool(config,"localhost", 6379);
//4.从连接池中获取连接对象
Jedis jedis = pool.getResource();
//5.使用连接对象
jedis.lpush("students", "孙悟空", "猪八戒", "白骨精");
List<String> students = jedis.lrange("students", 0, -1);
System.out.println(students);
//6.关闭连接对象
jedis.close();
}
}
小结
JedisPool连接池类 | 作用 |
---|---|
JedisPool(配置对象,服务器名,端口号) | 创建连接池 参数1:配置对象 参数2:服务器名 参数3:端口号 |
Jedis getResource() | 从连接池中获取连接对象 |
void close() | 关闭连接池 |
ResourceBundle类的使用
代码
jedis.properties的内容
# 连接池的最大连接数
maxTotal=10
# 最长等待时间为2秒钟
maxWaitMillis=2000
# 服务器名字
host=localhost
# 端口号
port=6379
使用ResourceBundle类:
package com.itheima;
import java.util.ResourceBundle;
public class Demo3Resource {
public static void main(String[] args) {
//1. 通过静态方法读取属性文件,参数是:属性文件的主文件名,没有扩展名
ResourceBundle bundle = ResourceBundle.getBundle("jedis");
//2. 获取属性值,通过键获取值
String host = bundle.getString("host");
//3.输出值
System.out.println(host);
}
}
小结
java.util.ResourceBundle类 | 功能 |
---|---|
static ResourceBundle getBundle(“配置文件基名”) | 读取配置文件,得到对象。参数是主文件名 |
String getString(“键名”) | 通过键获取值 |
Jedis连接池工具类的实现
jedis.properties配置文件
# 主机名
host=localhost
# 端口号
port=6379
# 最大连接数
maxTotal=20
# 最长等待时间
maxWaitMillis=3000
JedisUtils.java
package com.itheima.utils;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import java.util.ResourceBundle;
/**
* Jedis连接池工具类
*/
public class JedisUtils {
private static JedisPool pool;
//在静态代码块中创建连接池
static {
//读取配置文件
ResourceBundle bundle = ResourceBundle.getBundle("jedis");
//读取属性值
int maxTotal = Integer.parseInt(bundle.getString("maxTotal"));
int maxWaitMillis = Integer.parseInt(bundle.getString("maxWaitMillis"));
int port = Integer.parseInt(bundle.getString("port"));
String host = bundle.getString("host");
//创建连接池配置对象
JedisPoolConfig config = new JedisPoolConfig();
//设置连接池的参数
config.setMaxTotal(maxTotal);
config.setMaxWaitMillis(maxWaitMillis);
//创建连接池
pool = new JedisPool(config, host, port);
}
/**
* 获取连接对象
*/
public static Jedis getJedis() {
return pool.getResource();
}
}
使用工具类
package com.itheima;
import com.itheima.utils.JedisUtils;
import redis.clients.jedis.Jedis;
//使用连接池工具类
public class Demo4Use {
public static void main(String[] args) {
//从连接池中获取连接对象
Jedis jedis = JedisUtils.getJedis();
//添加键和值
jedis.set("car", "BWM");
//取出
String car = jedis.get("car");
//输出
System.out.println(car);
//关闭连接
jedis.close();
}
}
持久化
利用永久性存储介质将数据进行保存,在特定的时间将保存的数据进行恢复的工作机制称为持久化 持久化用于防止数据的意外丢失,确保数据安全性
永久化介质保存数据,存在硬盘
RDB
直接拷贝数据从内存到磁盘上
RDB优点
-
RDB是一个紧凑压缩的二进制文件,存储效率较高
-
RDB内部存储的是redis在某个时间点的数据快照,非常适合用于数据备份,全量复制等场景
-
RDB恢复数据的速度要比AOF快很多
-
应用:服务器中每X小时执行bgsave备份,并将RDB文件拷贝到远程机器中,用于灾难恢复。
RDB缺点
-
RDB方式无论是执行指令还是利用配置,无法做到实时持久化,具有较大的可能性丢失数据
-
bgsave指令每次运行要执行fork操作创建子进程,要牺牲掉一些性能
-
Redis的众多版本中未进行RDB文件格式的版本统一,有可能出现各版本服务之间数据格式无法兼容现象
AOF
-
AOF(append only file)持久化:以独立日志的方式记录每次写命令,重启时再重新执行AOF文件中命令 达到恢复数据的目的。与RDB相比可以简单理解为由记录数据改为记录数据产生的变化
-
AOF的主要作用是解决了数据持久化的实时性,目前已经是Redis持久化的主流方式
AOF写数据三种策略(appendfsync)
-
always(每次):每次写入操作均同步到AOF文件中 数据零误差,性能较低,不建议使用。
-
everysec(每秒):每秒将缓冲区中的指令同步到AOF文件中,在系统突然宕机的情况下丢失1秒内的数据 数据准确性较高,性能较高,建议使用,也是默认配置
-
no(系统控制):由操作系统控制每次同步到AOF文件的周期 整体过程不可控
RDB VS AOF
RDB与AOF的选择之惑
-
对数据非常敏感,建议使用默认的AOF持久化方案
-
AOF持久化策略使用everysecond,每秒钟fsync一次。该策略redis仍可以保持很好的处理性能,当出 现问题时,最多丢失0-1秒内的数据。
-
注意:由于AOF文件存储体积较大,且恢复速度较慢
-
数据呈现阶段有效性,建议使用RDB持久化方案
-
数据可以良好的做到阶段内无丢失(该阶段是开发者或运维人员手工维护的),且恢复速度较快,阶段 点数据恢复通常采用RDB方案
-
注意:利用RDB实现紧凑的数据持久化会使Redis降的很低,慎重总结:
-
综合比对
-
RDB与AOF的选择实际上是在做一种权衡,每种都有利有弊 如不能承受数分钟以内的数据丢失,对业务数据非常敏感,选用AOF
-
如能承受数分钟以内的数据丢失,且追求大数据集的恢复速度,选用RDB
-
灾难恢复选用RDB
-
双保险策略,同时开启RDB 和 AOF,重启后,Redis优先使用AOF 来恢复数据,降低丢失数据的量
以上内容有失偏颇处,欢迎指正!