1、NoSQL引言
**NoSQL**(` Not Only SQL` ),意即**不仅仅是SQL**, 泛指非关系型的数据库。
SQL ----> 关系型数据库(mysql、oracle、sqlserver) 基于表 ,有关联关系,可持久化数据到硬盘,支持事务
NoSQL ----> 非关系型数据库 ,是键值对,基于文档的,图形数据库或宽列存储 ,没有事务
2、NoSQL的四大分类
键值(Key-Value)存储数据库
说明:用到一个哈希表,这个表中有一个特定的键和一个指针指向特定的数据。
特点:简单、易部署,但对部分值进行查询或更新效率低列存储数据库 (海量数据)
说明:通常用来应对分布式存储的海量数据,大数据领域
特点:键指向多个列文档型数据库
说明:同键值存储类似(升级版),该类型的数据模型是版本化的文档,半结构化的文档以特定的格式存储,比如JSON。
特点:以文档形式存储,允许之间嵌套键值。且文档型数据库比键值数据库的查询效率更高图形(Graph)数据库 (阿里云 OSS 对象存储 图片 视频 音频)
说明:使用灵活的图形模型,能够扩展到多个服务器上。
特点:没有标准的查询语言,进行数据库查询需要制定数据模型
3、Redis引言
Redis开源 ,遵循BSD ,基于内存数据存储,被用于作为 数据库、缓存、消息中间件
- 总结: redis是一个内存型的数据库
- 特点:
- Redis是一个高性能key/value内存型数据库
- 读写快
- Redis支持丰富的数据类型 String list hash set zset
- Redis支持持久化 (解决断电丢失内存数据) 内存数据 ----> 磁盘
- Redis单线程,单进程,线程安全
4、Redis安装
# 0.准备环境
- vmware15.x+
- centos7.x+
# 1.下载redis源码包
- https://redis.io/
# 2.下载完整源码包
- redis-4.0.10.tar.gz
# 3.将下载redis资料包上传到Linux中
# 4.解压缩文件
- [root@localhost ~]# tar -zxvf redis-4.0.10.tar.gz
# 5.安装gcc
- yum install -y gcc
# 6.进入解压缩目录执行如下命令
- make MALLOC=libc
# 7.编译完成后执行如下命令
- make install PREFIX=/usr/redis
# 8.进入/usr/redis目录启动redis服务
- ./redis-server
# 9.Redis服务端口默认是 6379
# 10.进入bin目录执行客户端连接操作
- ./redis-cli –p 6379
- ./redis-cli -p 7000 --raw //--raw 代表显示中文
5、Redis数据库相关指令
5.1 数据库操作指令
# 1.Redis中库说明
- 使用redis的默认配置器动redis服务后,默认会存在16个库,编号从0-15
- 可以使用select 库的编号来选择一个redis的库:
# 2.Redis中操作库的指令
- 清空当前的库 FLUSHDB
- 清空全部的库 FLUSHALL
# 3.redis客户端显示中文
- ./redis-cli -p 7000 --raw
5.2 操作key常用指令
# 1.DEL指令
- 语法 : DEL key [key ...]
- 作用 : 删除一个或多个key ,不存在的key 会忽略
- 返回值: 删除key 的数量
# 2.EXISTS指令
- 语法: EXISTS key
- 作用: key 是否存在
- 返回值: 若key 存在,返回1 ,否则返回0
# 3.KEYS
- 语法 : KEYS pattern
- 作用 : 查找所有符合pattern 的key
- 语法:
* :任意数量字符
? :一个字符
[]:括号内的任意值
- 返回值: 符合的key 列表
# 4.MOVE
- 语法 : MOVE key db
- 作用 : 将当前数据库的key 移动到给定的数据库db 当中
- 返回值: 移动成功返回1 ,失败返回0 。
# 5.EXPIRE(PEXPIRE)
- 语法: EXPIRE(PEXPIRE) key seconds
- 作用: 以秒(毫秒)为单位为key 设置生存时间,当key 过期(生存时间为0秒),自动删除
- 返回值:设置成功返回1,否则0
# 6.TTL(PTTL)
- 语法 : TTL(PTTL) key
- 作用 : 以秒(毫秒)为单位,返回给定key 的剩余生存时间(TTL, time to live)
- 返回值:
当key 不存在时,返回-2
当key 存在但没有设置剩余生存时间时,返回-1
否则,以秒为单位,返回key 的剩余生存时间
# 7.RANDOMKEY
- 语法 : RANDOMKEY
- 作用 : 从当前数据库中随机返回(不删除) 一个key 。
- 返回值:当数据库不为空时,返回一个key 。当数据库为空时,返回nil
# 8.RENAME
- 语法 : RENAME key newkey
- 作用 : 将key 改名为newkey 。当key 和newkey 相同或key 不存在时,返回错误。当newkey 已经存在时覆盖旧值。
- 返回值: 改名成功时提示OK ,失败时候返回错误。
# 9.TYPE
- 语法 : TYPE key
- 作用 : 返回key 所储存的值的类型。
- 返回值:
none (key 不存在)、string (字符串)、list (列表)、set (集合)、zset (有序集)、hash (哈希表)
5.3 String类型常用命令
命令 | 说明 |
---|---|
set(mset) | 一次设置一个(多个)key value |
get (mget) | 一次获得一个(多个)key的value |
getset | 获得原始key的值,同时设置新值 |
strlen | 获得对应key存储value的长度 |
append | 为对应key的value追加内容 |
getrange 索引0开始 -1表示末尾 | 截取value的内容 |
setex (psetex) | 设置key存活几秒(毫秒) |
setnx | 存在不做任何操作,不存在添加 |
msetnx原子操作(只要有一个存在不做任何操作) | 可以同时设置多个key,只有有一个存在都不保存 |
decr (decrby) | 进行数值类型的-1(减法)操作 |
Incr (incryby) | 进行数值类型的+1(加法)操作 |
5.4 List类型(有序可重复)常用命令
命令 | 说明 |
---|---|
lpush (lpushx) | 将某个值加入到一个key列表头部 (key保证存在) |
rpush(rpushx) | 将某个值加入到一个key列表末尾(key保证存在) |
lpop (rpop) | 返回和移除列表左(右)边的第一个元素 |
lrange | 获取某一个下标区间内的元素 |
llen | 获取列表元素个数 |
lset | 设置某一个指定索引的值(索引必须存在) |
lindex | 获取某一个指定索引位置的元素 |
lrem | 删除元素 |
ltrim | 保留列表中特定区间内的元素 |
linsert | 在某一个元素之前,之后插入新元素 |
5.5 Set类型(无序不可重复)常用命令
命令 | 说明 |
---|---|
sadd | 为集合添加元素 |
smembers | 无序显示集合中所有元素 |
scard | 返回集合中元素个数 |
spop | 随机返回一个元素 并将元素在集合中删除 |
smove | 从一个集合中向另一个集合移动元素 必须是同一类型 |
srem | 从集合中删除一个元素 |
sismember | 判断集合中是否含有这个元素 |
srandmember | 随机返回元素 |
5.6 ZSet类型(可根据score排序不可重复)常用命令
命令 | 说明 |
---|---|
zadd | 添加一个有序集合元素 |
zcard | 返回集合的元素个数 |
zrange 升序 zrevrange 降序 | 返回一个范围内的元素 |
zrangebyscore | 按照分数查找一个范围内的元素 (实现分页) |
zrank | 返回排名 |
zrevrank | 倒序排名 |
zscore | 显示某一个元素的分数 |
zrem | 移除某一个元素 |
zincrby | 给某个特定元素加分 |
5.7 hash类型(key无序)常用命令
命令 | 说明 |
---|---|
hset | 设置一个key/value对 |
hget | 获得一个key对应的value |
hgetall | 获得所有的key/value对 |
hdel | 删除某一个key/value对 |
hexists | 判断一个key是否存在 |
hkeys | 获得所有的key |
hvals | 获得所有的value |
hmset | 设置多个key/value |
hmget | 获得多个key的value |
hsetnx | 设置一个不存在的key的值 |
hincrby | 为value进行加法运算 |
6、持久化机制
redis数据保存在内存中,需要持久化机制,将数据存储到磁盘
6.1快照RDB方式(默认)
说明:这种方式可以将某一时刻的所有数据都写入硬盘中,保存的文件是以.rdb形式结尾的文件
在redis.conf文件中可根据 dbfilename dump.rdb 和 dir ./修改文件名和保存位置a.客户端方式:
BGSAVE指令触发持久化:
redis接收到BGSAVE命令,会调用fork创建一个子进程(几乎和主进程一样,且共享资源),子进程负责将快照写入磁盘中,而父进程则继续处理命令请求
SAVE指令触发持久化:
redis主进程阻塞,接收到SAVE命令在快照创建完毕之前将不再响应任何其他的命令
shutdown指令触发持久化:
redis通过shutdown指令接收到关闭服务器的请求时,会执行一个save命令,阻塞所有的客户端,不再执行命令,save命令执行完毕后关闭服务器
b.服务器配置自动触发:
在redis.conf中设置多个save配置选项,当任意一个save配置选项条件满足,redis会触发一次BGSAVE命令,例:save 300 100:当300s内有100个key值变化则保存
6.2 AOF持久化
说明:AOF持久化会将执行的写命令写到AOF的文件末尾,记录数据发生的变化,下次只要redis执行一次AOF文件所包含的所有写命令,就可以恢复AOF文件的记录的数据集.
开启AOF持久化(redis.conf配置):
- a.修改 appendonly yes 开启持久化 - b.修改 appendfilename "appendonly.aof" 指定生成文件名称 - c.修改appendfsync everysec(推荐) 修改日志同步频率 解释日志同步频率: eversec:每秒执行一次同步显式的将多个写命令同步到磁盘(即使系统崩溃,最多丢失一秒的数据) always:个redis写命令都要同步写入硬盘,严重降低redis速度,降低硬盘使用寿命 no:由操作系统决定何时同步,当系统崩溃时,会丢失不定数量的数据;当硬盘读写效率低,缓冲区需等待,redis阻塞,导致处理请求速度下降
AOF重写(ReWriter)机制
说明:AOF持久化问题:若对一个key改变100次数据,则文件保存100条命令,文件利用率极低,使用AOF重写,可以只保存一条命令,压缩文件大小
触发重写方式:# 1.客户端方式触发重写 - 执行BGREWRITEAOF命令 不会阻塞redis的服务 # 2.服务器配置方式自动触发(redis.conf配置) - auto-aof-rewrite-percentage 100 - auto-aof-rewrite-min-size 64mb 解释: auto-aof-rewrite-min-size: 表示运行AOF重写时文件最小体积, 默认为64MB; Redis在最近一次重写后记住AOF文件的大小(默认:启动时AOF的大小)。当AOF日志大小增加100%时,则触发重写,隐式调用BGREWRITEAOF。
重写原理:
- 1.redis调用fork创建一个子进程(几乎和主进程一样,且共享资源) - 2.子进程根据内存中的数据库状态快照,往临时文件写入重建数据库状态的命令 - 3. 父进程继续处理client请求,把写命令写入到原来的aof文件中,并把写命令缓存,保证若子进程重写失败不会出问题。 - 4. 子进程把快照内容写到临时文件中后,发信号通知父进程,父进程把缓存的写命令写入到临时文件。 - 5. 父进程可以使用临时文件替换老的aof文件,并重命名,后面收到的写命令开始往新的aof文件中追加。
7、java操作Redis
a.引入依赖
<!--引入jedis连接依赖--> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.9.0</version> </dependency>
b.第一个程序
public class TestClient { public static void main(String[] args){ //1.创建jedis对象(redis服务必须关闭防火墙,开启远程连接) Jedis jedis = new Jedis("192.168.136.138", 6379); //2.执行相关操作(使用jedis对象操作redis) jedis.select(0); jedis.append("message","消息"); //3.释放资源 jedis.close(); } }
8、SpringBoot整合Redis
引言:
由于jedis方法只收字符串,传对象较繁琐,需要引入fastjson使用JSONObject将对象转为json字符串存到redis中。所以需要Spring Boot Data(数据) Redis 中提供的RedisTemplate和StringRedisTemplate
介绍:
StringRedisTemplate是RedisTemplate的子类,方法基本一致,不同之处主要体现在操作的数据类型不同,前者操作String类型,后者操作Object类型。
使用:
a.引入依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
b.配置application.propertie
spring.redis.host=192.168.136.138 spring.redis.port=6379 spring.redis.database=0
c.使用StringRedisTemplate操作string类型示例
@SpringBootTest class SpringbootRedisApplicationTests { @Autowired private StringRedisTemplate StringredisTemplate; @Test public void testKeys(){ //操作key Set<String> keys = StringredisTemplate.keys("*"); keys.forEach(key-> System.out.println(key)); } @Test public void testString(){ //spring data redis API:提供了 OpsForxxx()方法,指定key的类型,字符串类型特殊(opsForValue) ValueOperations<String, String> stringOperations = StringredisTemplate.opsForValue(); //添加一个key-value-存活时间-单位 stringOperations.set("name","zhangsan",1L, TimeUnit.DAYS); //用opsForValue()获取的对象操作string类型...... } }
d.使用RedisTemplate操作操作string类型示例
@SpringBootTest public class TestRedisTemplate { //redisTemplate操作时,会将key和value都进行序列化(转为二进制数据,能够在网络上传输或将对象保存到文件中) @Autowired private RedisTemplate<Object,Object> redisTemplate; @Test public void testKey(){ //阻止key的序列化,否则keys(*) ——> keys(xxx),取不到值 redisTemplate.setKeySerializer(new StringRedisSerializer()); Set<Object> keys = redisTemplate.keys("*"); keys.forEach(System.out::println); } @Test public void testString(){ //注意:某个对象要序列化,需要实现接口 // string类型已经实现了序列化接口,所有student会变成xxxx...,再想要拿此数据比较困难,应取消String的序列化 redisTemplate.setKeySerializer(new StringRedisSerializer()); ValueOperations<Object, Object> valueOperations = redisTemplate.opsForValue(); valueOperations.set("student",new Student(1,"zhangsan",20)); //student会进行反序列化(二进制——>java对象) Student student = (Student) valueOperations.get("student"); System.out.println(student); }
e.RedisTemplate和StringRedisTemplate的bond绑定
@SpringBootTest public class TestBound { //1.opsForxxx //2.bound绑定 : boundXxxOps() 用来对一个key进行多次操作推荐使用该类型方法 @Autowired private StringRedisTemplate stringRedisTemplate; @Test public void testString(){ ValueOperations valueOperations = stringRedisTemplate.opsForValue(); valueOperations.set("name","zhangsan"); valueOperations.append("name","haha"); //绑定age这个key,直接使用 BoundValueOperations<String, String> age = stringRedisTemplate.boundValueOps("age"); age.set("20"); } }