内存式NoSQL数据库Redis
基于内存的分布式NoSQL数据库Redis
知识点01:课程目标
- Redis功能和应用场景
- 目标:掌握Redis的功能、应用场景
- 什么是Redis,能解决什么问题?
- 什么场景下用到Redis?
- Redis的数据结构和数据类型
- 目标:掌握Redis数据结构和常用数据类型
- Redis中以什么形式进行存储?常用的数据类型有哪些?
- Redis的基本使用
- 目标:学习Redis的读写操作:基于命令行命令操作【命令】 / 基于代码提交操作【Jedis】
知识点02:【了解】NoSQL与RDBMS
- 目标:了解NoSQL的应用场景与RDBMS的区别
- 实施
- RDBMS的特点:关系型数据库管理系统
- 工具:MySQL、Oracle、SQL Server……
- 应用:业务性数据存储系统:事务和稳定性
- 用户数据、商品数据、订单数据、人事数据、财务数据
- 数据的安全性和稳定性
- 特点:体现数据之间的关系,支持事务,保证业务完整性和稳定性,小数据量的性能也比较好
- 开发:SQL
- 业务架构中的问题
- 问题:以网站后台存储为例,当并发量很大,所有高并发全部直接请求MySQL,容易导致MySQL奔溃
- 需求:能实现高并发的数据库,接受高并发请求
- NoSQL的特点:Not Only SQL:非关系型数据库
- 工具:Redis、HBASE、MongoDB……
- 应用:一般用于高并发高性能场景下的数据缓存或者数据库存储
- 特点:读写速度特别快,并发量非常高,相对而言不如RDBMS稳定,对事务性的支持不太友好
- 开发:每种NoSQL都有自己的命令语法
- 解决上面RDBMS的问题:使用高并发缓存实现读写分离
- RDBMS的特点:关系型数据库管理系统
- 小结:了解NoSQL的应用场景与RDBMS的区别
知识点03:【掌握】Redis的功能与应用场景
- 目标:理解Redis的功能与应用场景
- 实施
- 介绍
- 相关网站
- 官方介绍
- 介绍
Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。 它支持多种类型的数据结构,如 字符串(strings), 散列(hashes), 列表(lists), 集合(sets), 有序集合(sorted sets) 与范围查询, bitmaps, hyperloglogs 和 地理空间(geospatial) 索引半径查询。 Redis 内置了 复制(replication),LUA脚本(Lua scripting), LRU驱动事件(LRU eviction),事务(transactions) 和不同级别的 磁盘持久化(persistence), 并通过 Redis哨兵(Sentinel)和自动 分区(Cluster)提供高可用性(high availability)。
- **定义**:基于内存的分布式的NoSQL数据库
- 所有数据存储在内存中,并且有**持久化机制**【保证内存数据安全】
- 每次redis重启,会从文件中重新加载数据到内存,**所有读写都只基于内存**
- 功能特点
- 功能:提供高性能高并发的数据存储【读写】
- 特点
- 基于C语言开发的系统,与硬件的交互性能更好
- 基于内存实现数据读写,读写性能更快
- 分布式的:扩展性和稳定性更好
- KV结构数据库:支持事务、拥有各种丰富的数据结构
- 应用场景
- 缓存:用于实现大数据量高并发的大数据量缓存【传统Java架构领域中】
- 临时性存储
- 数据库:用于实现高性能的小数据量读写【大数据中主要应用场景】
- 持久性存储
- 缓存:用于实现大数据量高并发的大数据量缓存【传统Java架构领域中】
- 消息中间件:消息队列【MQ】:用于实现消息传递,一般不用Redis
- 有些工具会利用redis做消息队列
- 小结:理解Redis的功能与应用场景
知识点04:【实现】Redis的Linux版单机部署
- 目标:实现Redis的Linux版单机部署
- 实施
- Windows版本安装及远程工具使用请参考随堂资料《Redis的Windows版安装及远程工具的使用.pdf》
- Redis版本
- 常见版本:redis3、redis5
- redis3:提供集群模式等功能
- redis5:优化了集群模式,修复bug
- 上传redis-5.0.8源码
cd /export/software/
rz
- 解压
tar -zxvf redis-5.0.8.tar.gz -C /export/server/
# **安装依赖**
yum -y install gcc-c++ tcl
# 如果已经安装过,执行命令结果如下:
# **编译安装**
#进入源码目录
cd /export/server/redis-5.0.8/
#编译
make
#安装,并指定安装目录
make PREFIX=/export/server/redis-5.0.8-bin install
- 修改配置
- 复制配置文件
cp /export/server/redis-5.0.8/redis.conf /export/server/redis-5.0.8-bin/
- 创建目录
#redis日志目录
mkdir -p /export/server/redis-5.0.8-bin/logs
#redis数据目录
mkdir -p /export/server/redis-5.0.8-bin/datas
- 修改配置
cd /export/server/redis-5.0.8-bin/
vim redis.conf
- 创建软连接
cd /export/server
ln -s redis-5.0.8-bin redis
- 配置环境变量
vim /etc/profile
# 添加以下内容
# REDIS HOME
export REDIS_HOME=/export/server/redis
export PATH=:$PATH:$REDIS_HOME/bin
# 刷新环境变量:这条不能放入配置文件中
source /etc/profile
- 启动
- 端口:6379
- 启动服务端
- 启动命令
/export/server/redis/bin/redis-server /export/server/redis/redis.conf
# 启动脚本
vim /export/server/redis/bin/redis-start.sh
#!/bin/bash
REDIS_HOME=/export/server/redis
${REDIS_HOME}/bin/redis-server ${REDIS_HOME}/redis.conf
chmod u+x /export/server/redis/bin/redis-start.sh
- 检查服务端
ps -ef | grep redis
或者
netstat -atunlp | grep 6379
- 启动客户端
/export/server/redis/bin/redis-cli -h node1 -p 6379
- 关闭客户端
- exit:退出客户端
- 关闭服务端
- 方式一:客户端中
shutdown
- 方式二:Linux命令行
kill -9 redis的pid
- 方式三:通过客户端命令进行关闭
bin/redis-cli -h node1 -p 6379 shutdown
- **测试**
node1:6379> keys *
(empty list or set)
node1:6379> set s1 hadoop
OK
node1:6379> keys *
1) "s1"
node1:6379> get s1
"hadoop"
node1:6379>
- 远程工具的使用:参考Windows安装文档
- 小结:实现Redis的Linux版单机部署
知识点05:【掌握】Redis的数据结构及数据类型
- 目标:掌握Redis的数据结构及数据类型
- 实施
- 数据结构:Redis中所有数据存储都以KV结构形式存在
- K:作为唯一标识符,唯一标识一条数据,固定为String类型,写入时指定KV,读取时,根据K读取V
- V:真正存储的数据,可以有多种类型:String、Hash、List、Set、Zset、BitMap、HypeLogLog
- 理解Redis:类似于Java中的一个Map集合,可以存储多个KV,根据K获取V
- 数据类型
每一种类型的应用场景和命令都是不一样的 | Key:String | Value类型 | Value值 | 应用场景 | | --- | --- | --- | --- | | pv_20200101 | String | 10000 | 一般用于存储单个数据指标的结果 | | flow_20200101 | Hash | UV:1000 PV : 2000 IP: 100 | 用于存储整个对象所有属性值 | | uv | List | {100,200,300,100,600} | 有序允许重复的集合,每天获取最后一个值 | | uv_20200101 | Set | {userid1,userid2,userid3,userid4……} | 无序且不重复的集合,直接通过长度得到UV | | top10_product | ZSet【score,element】 | {10000-牙膏,9999-玩具,9998-电视……} | 有序不可重复的集合,统计TopN |
String:类似于Java中的字符串,用于存储单个指标的结果
- 数据结构:Redis中所有数据存储都以KV结构形式存在
# 存储单个指标
K:20220101_uv
V:123456
- Hash:类似于Java中Map集合,用于存储整个对象的所有属性
# 一个KV存储多个指标的结果
K:20220101_rs
V:{uv:12345, pv:678910, order_amt:100000, order_cnt:100}
- List:类似于Java中List集合,有序且可重复
# 存储一个有序可重复的结果,昨天的UV,近7天的UV,近15天UV,1个月UV
K:uv_days
V: [100, 400, 300, 200, 100]
- Set:类似于Java中Set集合,不可重复
- 需求:统计昨日所有用户个数:userid
# Flink或者Struct Steaming
select count(distinct userid) uv from table
- 用Redis解决
K:20220101_uv
V:(userid1,userid2,userid3……)
- Zset:类似于Java中TreeMap,结合了List和Set共同特性,有序【自己选择升序或者降序】且不可重复
# 需求:销量最高前10个商品:排序,不可重复
K:top10_goods
V: {10000:牙膏,9999:袜子……}
- 小结:掌握Redis的数据结构及数据类型
知识点06:【掌握】Redis的通用命令
- 目标:掌握Redis常用的通用命令
- 实施
- keys:列举当前数据库中所有Key
- del key:删除某个KV
- exists key :判断某个Key是否存在
- type key:判断这个K对应的V的类型的
- expire K 过期时间:设置某个K的过期时间,一旦到达过期时间,这个K会被自动删除
- ttl K:查看某个K剩余的存活时间
- select N:切换数据库的
- move key N:将某个Key移动到某个数据库中
- flushdb:清空当前数据库的所有Key
- flushall:清空所有数据库的所有Key
- 小结:掌握Redis常用的通用命令
知识点07:【掌握】String类型的常用命令
- 目标:掌握String类型的常用命令
- 实施
- set:给String类型的Value的进行赋值或者更新
- get:读取String类型的Value的值
- mset:用于批量写多个String类型的KV
- mget:用于批量读取String类型的Value
- setnx:只能用于新增数据,当K不存在时可以进行新增
- incr:用于对数值类型的字符串进行递增,递增1,一般用于做计数器
- incrby:指定对数值类型的字符串增长固定的步长
- decr:对数值类型的数据进行递减,递减1
- decrby:按照指定步长进行递减
- strlen:统计字符串的长度
- getrange:用于截取字符串
- 小结:掌握String类型的常用命令
知识点08:【掌握】Hash类型的常用命令
- 目标:掌握Hash类型的常用命令
- 实施
- hset:用于为某个K添加一个属性
- hget:用于获取某个K的某个属性的值
- hmset:批量的为某个K赋予新的属性
- hmget:批量的获取某个K的多个属性的值
- hgetall:获取所有属性的值
- hdel:删除某个属性
- hlen:统计K对应的Value总的属性的个数
- hexists:判断这个K的V中是否包含这个属性
- hvals:获取所有属性的value的
- hkeys:后去所有属性
- 小结
- 掌握Hash类型的常用命令
node1:6379> hset p1 name laoda
(integer) 1
node1:6379> keys *
1) "p1"
2) "s5"
3) "s1"
4) "s4"
5) "s3"
6) "s6"
7) "s2"
node1:6379> hset p1 age 18
(integer) 1
node1:6379> hset p2 name laoer
(integer) 1
node1:6379> hset p2 age 20
(integer) 1
node1:6379> keys *
1) "p1"
2) "s5"
3) "s1"
4) "s4"
5) "p2"
6) "s3"
7) "s6"
8) "s2"
node1:6379> hget p1 name
"laoda"
node1:6379> hget p2 name
"laoer"
node1:6379> hget p2 age
"20"
node1:6379> hget p1 age
"18"
node1:6379> hmset p1 gender male addr shanghai
OK
node1:6379> hmget p1 name gender addr
1) "laoda"
2) "male"
3) "shanghai"
node1:6379> hgetall p1
1) "name"
2) "laoda"
3) "age"
4) "18"
5) "gender"
6) "male"
7) "addr"
8) "shanghai"
node1:6379> hdel p1 gender
(integer) 1
node1:6379> hgetall p1
1) "name"
2) "laoda"
3) "age"
4) "18"
5) "addr"
6) "shanghai"
node1:6379> hlen p1
(integer) 3
node1:6379> hexists p1 name
(integer) 1
node1:6379> hexists p1 gender
(integer) 0
node1:6379> hvals p1
1) "laoda"
2) "18"
3) "shanghai"
node1:6379> hkeys p1
1) "name"
2) "age"
3) "addr"
node1:6379>
知识点09:【掌握】List类型的常用命令
- 目标:掌握List类型的常用命令
- 实施
- lpush:将每个元素放到集合的左边,左序放入
- rpush:将每个元素放到集合的右边,右序放入
- lrange:通过下标的范围来获取元素的数据
- llen:统计集合的长度
- lpop:删除左边的一个元素
- rpop:删除右边的一个元素
- 小结:掌握List类型的常用命令
node1:6379> lpush list1 1 2 3 3
(integer) 4
node1:6379> rpush list1 4 4 5 6 7
(integer) 9
node1:6379> lrange list1 0 -1
1) "3"
2) "3"
3) "2"
4) "1"
5) "4"
6) "4"
7) "5"
8) "6"
9) "7"
node1:6379> lrange list1 0 3
1) "3"
2) "3"
3) "2"
4) "1"
node1:6379> lrange list1 5 8
1) "4"
2) "5"
3) "6"
4) "7"
node1:6379> lrange list1 -4 -1
1) "4"
2) "5"
3) "6"
4) "7"
node1:6379> llen list1
(integer) 9
node1:6379> lpop list1
"3"
node1:6379> lrange list1 0 -1
1) "3"
2) "2"
3) "1"
4) "4"
5) "4"
6) "5"
7) "6"
8) "7"
node1:6379> rpop list1
"7"
node1:6379> lrange list1 0 -1
1) "3"
2) "2"
3) "1"
4) "4"
5) "4"
6) "5"
7) "6"
node1:6379>
知识点10:【掌握】Set类型的常用命令
- 目标:掌握Set类型的常用命令
- 实施
- sadd:用于添加元素到Set集合中
- smembers:用于查看Set集合的所有成员
- sismember:判断是否包含这个成员
- srem:删除其中某个元素
- scard:统计集合长度
- sunion:取两个集合的并集
- sinter:取两个集合的交集
- 小结:掌握Set类型的常用命令
node1:6379> sadd set1 5 2 1 1 3 1
(integer) 4
node1:6379> smembers set1
1) "1"
2) "2"
3) "3"
4) "5"
node1:6379> srem set1 3
(integer) 1
node1:6379> smembers set1
1) "1"
2) "2"
3) "5"
node1:6379> sismember set1 5
(integer) 1
node1:6379> sismember set1 3
(integer) 0
node1:6379> scard set1
(integer) 3
node1:6379> sadd set2 1 3 4 4 5 7
(integer) 5
node1:6379> smembers set2
1) "1"
2) "3"
3) "4"
4) "5"
5) "7"
node1:6379> sunion set1 set2
1) "1"
2) "2"
3) "3"
4) "4"
5) "5"
6) "7"
node1:6379> sinter set1 set2
1) "1"
2) "5"
node1:6379>
知识点11:【掌握】Zset类型的常用命令
- 目标:掌握Zset类型的常用命令
- 实施
- zadd:用于添加元素到Zset集合中
- zrange:范围查询
- zrevrange:倒序查询
- zrem:移除一个元素
- zcard:统计集合长度
- zscore:获取评分
- 小结
- 掌握Zset类型的常用命令
node1:6379> zadd zset1 20.5 yinyu 99 shengwu 100 yuwen 35.7 shuxue
(integer) 4
node1:6379> zrange zset1 0 -1
1) "yinyu"
2) "shuxue"
3) "shengwu"
4) "yuwen"
node1:6379> zrange zset1 0 -1 withscores
1) "yinyu"
2) "20.5"
3) "shuxue"
4) "35.700000000000003"
5) "shengwu"
6) "99"
7) "yuwen"
8) "100"
node1:6379> zrevrange zset1 0 -1
1) "yuwen"
2) "shengwu"
3) "shuxue"
4) "yinyu"
node1:6379> zrevrange zset1 0 -1 withscores
1) "yuwen"
2) "100"
3) "shengwu"
4) "99"
5) "shuxue"
6) "35.700000000000003"
7) "yinyu"
8) "20.5"
node1:6379> zrem zset1 shuxue
(integer) 1
node1:6379> zrange zset1 0 -1
1) "yinyu"
2) "shengwu"
3) "yuwen"
node1:6379> zcard zset1
(integer) 3
node1:6379> zscore zset1 yinyu
"20.5"
node1:6379> zscore zset1 yuwen
"100"
node1:6379>
知识点12:【掌握】BitMap类型的常用命令
- 目标:了解BitMap类型的常用命令
- 实施
- 功能:通过一个String对象的存储空间,来构建位图,用每一位0和1来表示状态
- Redis中一个String最大支持512M = 2^32次方,1字节 = 8位
- 使用时,可以指定每一位对应的值,要么为0,要么为1,默认全部为0
- 用下标来标记每一位,第一个位的下标为0
- 功能:通过一个String对象的存储空间,来构建位图,用每一位0和1来表示状态
- 举例:统计UV
- 一个位图中包含很多位,可以用每一个位表示一个用户id
- 读取数据,发现一个用户id,就将这个用户id对应的那一位改为1
- 统计整个位图中所有1的个数,就得到了UV
- setbit:修改某一位的值
- 语法:setbit bit1 位置 0/1
setbit bit1 0 1
- getbit:查看某一位的值
- 语法:getbit K 位置
getbit bit1 9
- bitcount:用于统计位图中所有1的个数
- 语法:bitcount K [start end]
bitcount bit1
#start和end表示的是字节:1 字节 = 8 位
bitcount bit1 0 10
- bitop:用于位图的运算:and/or/not/xor
- 语法:bitop and/or/xor/not bitrs bit1 bit2
bitop and bit3 bit1 bit2
bitop or bit4 bit1 bit2
node1:6379> setbit bit1 0 1
(integer) 0
node1:6379> getbit bit1 0
(integer) 1
node1:6379> getbit bit1 1
(integer) 0
node1:6379> getbit bit1 2
(integer) 0
node1:6379> getbit bit1 3
(integer) 0
node1:6379> setbit bit1 9 1
(integer) 0
node1:6379> setbit bit1 17 1
(integer) 0
node1:6379> bitcount bit1
(integer) 3
node1:6379> bitcount bit1 0 7
(integer) 3
node1:6379> bitcount bit1 0 0
(integer) 1
node1:6379> bitcount bit1 0 1
(integer) 2
node1:6379> bitcount bit1 0 2
(integer) 3
node1:6379> setbit bit2 0 1
(integer) 0
node1:6379> setbit bit2 10 1
(integer) 0
node1:6379> bitop and bit3 bit1 bit2
(integer) 3
node1:6379> bitcount bit3
(integer) 1
node1:6379>
- 小结:了解BitMap类型的常用命令
知识点13:【掌握】HyperLogLog类型的常用命令
- 目标:了解HyperLogLog类型的常用命令
- 实施
- 功能:类似于Set集合,用于实现数据的去重,底层实现原理不一样
- 应用:适合于数据量比较庞大的情况下的使用,存在一定的误差率
- pfadd:用于添加元素
- 语法:pfadd K e1 e2 e3……
pfadd pf1 userid1 userid1 userid2 userid3 userid4 userid3 userid4
pfadd pf2 userid1 userid2 userid2 userid5 userid6
- pfcount:用于统计个数
- 语法:pfcount K
pfcount pf1
- pfmerge:用于实现集合合并
- 语法:pfmerge pfrs pf1 pf2……
pfmerge pf3 pf1 pf2
- 小结:了解HyperLogLog类型的常用命令
知识点14:【掌握】Jedis:使用方式与Jedis依赖
- 目标:掌握Redis的使用方式及构建Jedis工程依赖
- 实施
- Redis的使用方式
- 命令操作Redis,一般用于测试开发阶段
- 分布式计算或者Java程序读写Redis,一般用于实际生产开发
- Spark/Flink读写Redis
- 所有数据库使用Java操作方式整体是类似的
- Redis的使用方式
//todo:1-构建客户端连接对象
Connection conn = DriverManager.getConnect(url,username,password)
//todo:2-执行操作:所有操作都在客户端连接对象中:方法
prep.execute(SQL)
//todo:3-释放连接
conn.close
- Jedis依赖
- 参考附录一添加依赖
- 小结
- 掌握Redis的使用方式及构建Jedis工程依赖
知识点15:【掌握】Jedis:构建连接
- 目标:实现Jedis的客户端连接
- 实施
package bigdata.itcast.cn.jedis;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
/**
* @ClassName JedisClientTest
* @Description TODO 测试Jedis客户端的开发,实现Redis数据库的操作
* @Create By Frank
*/
public class JedisClientTest {
// todo:1-构建一个连接对象
Jedis jedis = null;
@Before
public void getConnection(){
//方式一:直接构建一个Jedis连接实例
// jedis = new Jedis("node1",6379);
//方式二:使用连接池构建
//构建连接池配置
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(10); //设置最大连接数
//构建连接池
JedisPool jedisPool = new JedisPool(config,"node1",6379);
//获取连接
jedis = jedisPool.getResource();
}
// todo:2-实现具体的操作
// todo:3-释放连接
@After
public void closeConnection(){
jedis.close();
}
}
- 小结:实现Jedis的客户端连接
知识点16:【掌握】Jedis:String操作
- 目标:Jedis中实现String的操作
- 实施
set/get/incr/exists/expire/setexp/ttl
@Test
public void testString(){
//set/get/incr/exists/expire/setex/ttl
// jedis.set("s1","hadoop");
// System.out.println(jedis.get("s1"));
// jedis.set("s2","1");
// jedis.incr("s2");
// System.out.println(jedis.get("s2"));
// System.out.println(jedis.exists("s1"));
// System.out.println(jedis.exists("s3"));
// jedis.expire("s1",20);
// while(true){
// System.out.println(jedis.ttl("s1"));
// }
jedis.setex("s3",10,"hive");
}
- 小结:Jedis中实现String的操作
知识点17:【掌握】Jedis:其他类型操作
- 目标:Jedis中实现其他类型操作
- 实施
- Hash类型
hset/hmset/hget/hgetall/hdel/hlen/hexists
public void testHash(){
//hset/hmset/hget/hgetall/hdel/hlen/hexists
jedis.hset("m1","name","zhangsan");
System.out.println(jedis.hget("m1","name"));
Map<String,String> maps = new HashMap<>();
maps.put("age","18");
maps.put("phone","110");
jedis.hmset("m1",maps);
List<String> hmget = jedis.hmget("m1", "name", "age");
System.out.println(hmget);
System.out.println("=");
Map<String, String> m1 = jedis.hgetAll("m1");
for(Map.Entry map : m1.entrySet()){
System.out.println(map.getKey()+"\t"+map.getValue());
}
System.out.println("=");
System.out.println(jedis.hlen("m1"));
jedis.hdel("m1","name");
System.out.println(jedis.hlen("m1"));
System.out.println(jedis.hexists("m1","name"));
System.out.println(jedis.hexists("m1","age"));
}
- List类型
lpush/rpush/lrange/llen/lpop/rpop
@Test
public void testList(){
//lpush/rpush/lrange/llen/lpop/rpop
jedis.lpush("list1","1","2","3");
System.out.println(jedis.lrange("list1",0,-1));
jedis.rpush("list1","4","5","6");
System.out.println(jedis.lrange("list1",0,-1));
System.out.println(jedis.llen("list1"));
jedis.lpop("list1");
jedis.rpop("list1");
System.out.println(jedis.lrange("list1",0,-1));
}
- Set类型
sadd/smembers/sismember/scard/srem
@Test
public void testSet(){
//sadd/smembers/sismember/scard/srem
jedis.sadd("set1","1","2","3","1","2","3","4","5","6");
System.out.println("长度:"+jedis.scard("set1"));
System.out.println("内容:"+jedis.smembers("set1"));
System.out.println(jedis.sismember("set1","1"));
System.out.println(jedis.sismember("set1","7"));
jedis.srem("set1","2");
System.out.println("内容:"+jedis.smembers("set1"));
}
- Zset类型
zadd/zrange/zrevrange/zcard/zrem
@Test
public void testZset(){
//zadd/zrange/zrevrange/zcard/zrem
jedis.zadd("zset1",20.9,"yuwen");
jedis.zadd("zset1",10.5,"yinyu");
jedis.zadd("zset1",70.9,"shuxue");
jedis.zadd("zset1",99.9,"shengwu");
Set<String> zset1 = jedis.zrange("zset1", 0, -1);
System.out.println(zset1);
System.out.println(jedis.zrevrange("zset1",0,-1));
System.out.println(jedis.zcard("zset1"));
jedis.zrem("zset1","yuwen");
System.out.println(jedis.zrangeWithScores("zset1",0,-1));
}
- 小结:Jedis中实现其他类型操作
附录一:Jedis Maven依赖
<dependencies>
<!-- Jedis 依赖 -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.2.0</version>
</dependency>
<!-- JUnit 4 依赖 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
知识点09:【掌握】Redis持久化:RDB设计
- 目标:掌握Redis的RDB持久化机制
- 实施
- 问题
Redis中的数据都存储在内存中,由内存对外提供读写,Redis一旦重启,内存中的数据就会丢失,Redis如何实现持久化?
- RDB方案
- Redis默认的持久化方案
- 思想
- 定期检查,在一定的时间内,如果Redis内存中的数据产生了一定次数的更新,就将整个Redis内存中的所有数据拍摄一个全量快照文件存储在硬盘上
- 新的快照会覆盖老的快照文件,快照是全量快照,包含了内存中所有的内容,基本与内存一致
- 如果Redis故障重启,从硬盘的快照文件进行恢复
- 举例
- 配置:save 30 2
- 解释:如果30s内,redis内存中的数据发生了2条更新【插入、删除、修改】,就将整个Redis内存数据保存到磁盘文件中,作为快照
- 过程
- 触发
- 手动触发:当执行某些命令时,会自动拍摄快照【一般不用】
- save:手动触发拍摄RDB快照的,将内存的所有数据拍摄最新的快照
- 前端运行
- 阻塞所有的客户端请求,等待快照拍摄完成后,再继续处理客户端请求
- 特点:快照与内存是一致的,数据不会丢失,用户的请求会被阻塞
- bgsave:手动触发拍摄RDB快照的,将内存的所有数据拍摄最新的快照
- 后台运行
- 主进程会fork一个子进程负责拍摄快照,客户端可以正常请求,不会被阻塞
- 特点:用户请求继续执行,用户的新增的更新数据不在快照中
- shutdown:执行关闭服务端命令
- flushall:清空,没有意义
- save:手动触发拍摄RDB快照的,将内存的所有数据拍摄最新的快照
- 自动触发:按照一定的时间内发生的更新的次数,拍摄快照
- 配置文件中有对应的配置,决定什么时候做快照
- 手动触发:当执行某些命令时,会自动拍摄快照【一般不用】
#Redis可以设置多组rdb条件,默认设置了三组,这三组共同交叉作用,满足任何一个都会拍摄快照
save 900 1
save 300 10
save 60 10000
- 为什么默认设置3组?根据不同读写速度场景,保证实现交叉快照
- 优缺点
- 优点
- rdb方式实现的是全量快照,快照文件中的数据与内存中的数据是一致的
- 快照是二进制文件,生成快照加载快照都比较快,体积更小
- Fork进程实现,性能更好
- 总结:更快、更小、性能更好
- 缺点:存在一定概率导致部分数据丢失
- 优点
- 应用:希望有一个高性能的读写,不影响业务,允许一部分的数据存在一定概率的丢失**【做缓存】**,大规模的数据备份和恢复
- 小结:掌握Redis的RDB持久化机制
知识点10:【实现】Redis持久化:RDB测试
- 目标:实现RDB持久化的测试
- 实施
- 查看当前快照
ll /export/server/redis/datas/
- 配置修改
cd /export/server/redis
vim redis.conf
#221行
save 20 2
- 重启redis服务,配置才会生效
shutdown
redis-start.sh
- 插入数据
set s1 "laoda"
set s2 "laoliu"
set s3 "laoliu"
- 查看dump的rdb快照
ll /export/server/redis/datas/
- 小结
- 实现RDB持久化的测试
知识点11:【掌握】Redis持久化:AOF设计
- 目标:掌握Redis的AOF持久化机制
- 实施
- 问题
RDB存在一定概率的数据丢失,如何解决?
- AOF方案
- 思想
- 按照一定的规则,将内存数据的操作日志追加写入一个文件中
- 当Redis发生故障,重启,从文件中进行读取所有的操作日志,恢复内存中的数据
- 重新对Redis进行执行,用于恢复内存中的数据
- 过程
- 实现:追加的规则
- appendfsync always
- 每更新一条数据就同步将这个更新操作追加到文件中
- 优点:数据会相对安全,几乎不会出现数据丢失的情况
- 缺点:频繁的进行数据的追加,增大磁盘的IO,导致性能较差
- appendfsync everysec
- 每秒将一秒内Redis内存中数据的操作异步追加写入文件
- 优点:在安全性和性能之间做了权衡,性能要比always高
- 缺点:有数据丢失风险 ,但最多丢失1秒
- appendfsync no
- 交给操作系统来做,不由Redis控制
- 肯定不用的
- appendfsync always
- 思想
- 优缺点
- 优点:安全性和性能做了折中方案,提供了灵活的机制,如果性能要求不高,安全性可以达到最高
- 缺点
- 这个文件是普通文本文件,相比于二进制文件来说,每次追加和加载比较慢
- 数据的变化以追加的方式写入AOF文件
- 问题:文件会不断变大,文件中会包含不必要的操作【过期的数据】
- 解决:模拟类似于RDB做全量的方式,按照一定条件生成一次全量的AOF文件
- 应用:数据持久化安全方案,理论上绝对性保证数据的安全
- 持久化方案:两种方案怎么选?
- 工作中:两个一般一起用,互不冲突
- 问题:如果RDB和AOF同时使用,Redis启动时加载谁的文件?
- 优先级:AOF优先级高于RDB
- 利用RDB做迁移的时候
- step1:现在老集群拍摄快照生成最新的RDB文件
- step2:放入新集群的目录中,新集群不开启AOF,加载RDB文件到内存中
- step3:新集群中通过命令:appendonly yes,临时开启AOF,自动生成AOF文件,自动写入数据到AOF文件
- step4:修改配置文件,更改为开启AOF
- 小结:掌握Redis的AOF持久化机制
知识点12:【实现】Redis持久化:AOF实现
- 目标:实现AOF持久化
- 实施
- 开启并配置
vim redis.conf
#699:开启aof
appendonly yes
#729:默认每s刷写一次
appendfsync everysec
#770,771
#增幅100%就重新覆盖一次
auto-aof-rewrite-percentage 100
#文件至少要大于64MB,一般建议更改为GB大小
auto-aof-rewrite-min-size 64mb
- 重启Redis
shutdown
redis-start.sh
- 查看数据
keys *
- 从AOF文件恢复数据
- 查看aof文件
ll /export/server/redis/datas
- 小结
- 实现AOF持久化