何夜息随笔录-Redis教程
文章目录
NoSQL概述
什么是NoSQL?NoSQL(NoSQL = Not Only SQL ),意即"不仅仅是SQL",它是泛指非关系型的数据库。
首先说关系型数据库:
关系型数据库遵循ACID规则
-
A (Atomicity) 原子性
原子性很容易理解,也就是说事务里的所有操作要么全部做完,要么都不做,事务成功的条件是事务里的所有操作都成功,只要有一个操作失败,整个事务就失败,需要回滚。比如银行转账,从A账户转100元至B账户,分为两个步骤:1)从A账户取100元;2)存入100元至B账户。这两步要么一起完成,要么一起不完成,如果只完成第一步,第二步失败,钱会莫名其妙少了100元。
-
C (Consistency) 一致性
一致性也比较容易理解,也就是说数据库要一直处于一致的状态,事务的运行不会改变数据库原本的一致性约束。
-
I (Isolation) 独立性
所谓的独立性是指并发的事务之间不会互相影响,如果一个事务要访问的数据正在被另外一个事务修改,只要另外一个事务未提交,它所访问的数据就不受未提交事务的影响。
比如现在有个交易是从A账户转100元至B账户,在这个交易还未完成的情况下,如果此时B查询自己的账户,是看不到新增加的100元的。
-
D (Durability) 持久性
持久性是指一旦事务提交后,它所做的修改将会永久的保存在数据库上,即使出现宕机也不会丢失。
NoSQL 特点
- 易于拓展:就是数据之间没有关系。
- 大数据高性能:可以一次写入多条数据,性能更高。
- 数据类型多样
NoSQL 数据库的四大分类
-
KV键值: Redis
-
文档型数据库(bson格式比较多): MongoDB,基于分布式文件存储的数据库;
-
列存储数据库: HBase,分布式文件系统;
-
图关系数据库: 存放社交网络,推荐系统等,专注于构建关系图谱;
Redis是什么
什么是Redis,Redis(Remote Dictionary Server ),即远程字典服务,是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。
Redis特点
- Redis以内存作为数据存储介质,读写数据的效率极高。
- Redis跟memcache不同的是,储存在Redis中的数据是持久化的,断电或重启,数据也不会丢失。
- Redis的存储分为内存存储、磁盘存储和log文件。
- Redis可以从磁盘重新将数据加载到内存中,也可以通过配置文件对其进行配置,因此,redis才能实现持久化。
- Redis支持主从模式,可以配置集群,更利于支撑大型的项目。
Redis的安装
以上都是理论的东西,现在开始实践,安装呢,我选择在docker安装,毕竟这个比较香,不用整花里胡哨的。
走起,直接安装最新版
docker pull redis
可以看到镜像已经安装好了,run直接运行,需要注意的是,Redis的默认端口号是6379,所以我们可以暴露这个端口。然后我的习惯是把配置文件挂载到默认的数据卷中。
docker run -idt --name hyx_redis -p 6379:6379 -v redis_conf:/etc/redis/redis.conf 74d107221092
目录在这里
/var/lib/docker/volumes/redis_conf/_data
然后我们看一下容器的日志,可以看到已经配置好了,等待连接。
然后我们进入容器,连接一下
docker exec -it 4abb124b4e9e /bin/bash
redis-cli 连接Redis
Redis基本命令
Redis默认有16个数据库,可以使用select 数字 来切换不同的数据库
可以使用DBSIZE来查看数据库的使用情况
keys * //查看所有的key
flushall //清除所有数据库的数据
flushdb //清除当前数据库的数据
*************************************************************************
exists key_name //查询变量是否存在,存在返回1,不存在返回0
move key_name 1 //移除当前数据库的这个变量
expire key_name 秒数 //变量存活时间,单位为秒
type key_name //查看变量类型
字符串命令
append key_name 新内容 //类型拼接字符串,追加
strlen key_name //查看字符串长度
incr key_name //自增加1,可以达到计数的效果,就是i++
incrby key_name 数值 //按照数值步长自增
decr key_name //同理,这是减1
decrby key_name //按照数值步长自减
getrange key_name 开始位置 结束位置 //截取字符串
*************************************************************************
可以set时直接指定到期时间
setex key_name 到期时间 值
127.0.0.1:6379> setex price 10 10.2 # 十秒后过期
setnx key value //if not exist 不存在时创建
mset k1 v1 k2 v2 .... //一个设置多个key
对象操作
对象操作
set 对象名:对象编号{键值对} #来声明对象
set user:1 {name:heyexi,weight:50}
get user1 //获得对象
List操作
List操作
给列表添加元素
lpush 列表名 值.....
127.0.0.1:6379> lpush list heyeix nihao
(integer) 2
127.0.0.1:6379> lpush list world
(integer) 3
127.0.0.1:6379> lrange list 0 -1 //拿出所有数据
1) "world"
2) "nihao"
3) "heyeix"
lpush 是加在头部,rpush是加在尾部
*************************************************************************
移除列表元素,有lpop和rpop
127.0.0.1:6379> lrange list 0 -1
1) "world"
2) "nihao"
3) "heyeix"
127.0.0.1:6379> rpop list
"heyeix"
*************************************************************************
获取指定下标的值
lindex 列表名 下标
127.0.0.1:6379> lindex list 0
"world"
*************************************************************************
获取列表长度
llen 列表名
127.0.0.1:6379> llen list
(integer) 2
Set操作
set操作
list操作都是l开头,那么set操作就是s开头
添加集合元素
sadd 集合名称 值...
127.0.0.1:6379> sadd myset 10 23 56 455
(integer) 4
*************************************************************************
查看集合成员
smembers set名称
127.0.0.1:6379> smembers myset
1) "10"
2) "23"
3) "56"
4) "455"
*************************************************************************
判断某个元素是否是该集合成员 类型contain
sismember 集合名称 查找的值
127.0.0.1:6379> sismember myset 12
(integer) 0 //不存在
127.0.0.1:6379> sismember myset 23
(integer) 1 //存在
*************************************************************************
获取长度
scard 集合名称
127.0.0.1:6379> scard myset
(integer) 4
*************************************************************************
移除某个值
srem 集合名 值1 值2...
127.0.0.1:6379> srem myset 23
(integer) 1
127.0.0.1:6379> scard myset
(integer) 3
*************************************************************************
查询第一个集合不同于第二个集合独有的元素
sdiff 集合1 集合2
127.0.0.1:6379> smembers myset
1) "10"
2) "56"
3) "455"
127.0.0.1:6379> sadd set1 10 23 66
(integer) 3
127.0.0.1:6379> sdiff myset set1
1) "56"
2) "455"
可以看到获得了第一个元素自己不同于第二个集合的元素
*************************************************************************
获取交集
sinter 集合1 集合2
127.0.0.1:6379> sinter myset set1
1) "10"
*************************************************************************
获取并集
sunion 集合1 集合2
127.0.0.1:6379> sunion myset set1
1) "10"
2) "23"
3) "56"
4) "66"
5) "455"
hash操作
hash(哈希)操作
想到hash就是想到java的hashmap
就是和Redis的set和get一样,只是这时是变成了hashmap,很明显的套娃
*************************************************************************
使用哈希set和get就变成了hset和hget
127.0.0.1:6379> hset myhash k1 hello k2 world
(integer) 2
127.0.0.1:6379> hget myhash k1
"hello"
*************************************************************************
删除值
127.0.0.1:6379> hdel myhash k1
(integer) 1
*************************************************************************
获取长度
127.0.0.1:6379> hlen myhash
(integer) 1
*************************************************************************
判断指定key是否存在
127.0.0.1:6379> hexists myhash k1
(integer) 0
*************************************************************************
获取所有的key
127.0.0.1:6379> hkeys myhash
1) "k2"
2) "k4"
3) "k5"
4) "k6"
*************************************************************************
获取所有的value
127.0.0.1:6379> hvals myhash
1) "99"
2) "90"
3) "99"
4) "90"
*************************************************************************
哈希保存对象信息,保存实体数据,一般使用哈希
127.0.0.1:6379> hset style:1 name css color red top 20px
(integer) 3
127.0.0.1:6379> hget style:1 name
"css"
Jedis
什么是jedis,jedis是java连接Redis的连接开发工具,相当于中间件,java通过操作jedis来操作Redis数据库。
新建一个Maven项目,然后倒入jedis依赖。
<dependencies>
<!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.3.0</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.47</version>
</dependency>
</dependencies>
然后写代码测试连接,记得开阿里云6379端口
public static void main(String[] args)
{
Jedis jedis = new Jedis("主机ip",6379);
System.out.println(jedis.ping());//测试连接
}
输出:
PONG
说明连接成功
常用的api
上面的命令在jedis就是一个个的方法
public static void main(String[] args)
{
Jedis jedis = new Jedis("ip",6379);
System.out.println("查询所有keys"+jedis.keys("*"));
System.out.println("查询key是否存在"+jedis.exists("heyexi"));
System.out.println("set一个key"+jedis.set("name","何夜息"));
System.out.println("获取key"+jedis.get("name"));
jedis.close();//关闭连接
}
输出
查询所有keys[myhash, user:1, set1, style:1, list, test, myset]
查询key是否存在false
set一个key OK
获取key何夜息
其他都是一样的
Jedis的事务操作
事务就是一组连续的操作,应该遵循acid原则,但是Redis的事务,单条语句是遵循原子性的,但是事务就不是了,只要编译没有错误,也就是命令没错,中间的一条命令发生错误,其他没有发生错误的能够进行执行。
事务的格式很简单:
multi //代表事务开始
中间就是一系列的命令
exec //事务结束
discard //代表放弃事务
@org.junit.Test
public void test1()
{
Jedis jedis = new Jedis("ip",6379);
//开启事务
Transaction multi = jedis.multi();
try{
//开始事务的一系列操作
multi.set("day","星期六");
multi.set("color","blue");
//执行事务
multi.exec();
System.out.println(jedis.get("day"));
System.out.println(jedis.get("color"));
}catch (Exception e)
{
//发错误放弃事务
multi.discard();
}finally
{
jedis.close();
}
}
输出
星期六
blue
SpringBoot整合jedis
现在就到了运用部分了,可以整合到SpringBoot开发项目了。
首先就是添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>2.3.4.RELEASE</version>
</dependency>
然后在配置文件中配置Redis
#redis配置
spring.redis.host=服务器地址
spring.redis.port=6379
最后就是操作Redis了
因为整合到了SpringBoot,所以就不用使用jedis了,可以使用RedisTemplate,和JdbdTemplate是一样的。
redisTemplate.opsForXX()
xx就代表数据类型,就是Redis的数据类型,代表选择我们要操作的数据类型。
@Autowired
private RedisTemplate redisTemplate;
@Test
void redis()
{
redisTemplate.opsForValue().set("bgImg","https://www.heyexi.com/xxx.jpg");
System.out.println(redisTemplate.opsForValue().get("bgImg"));
}
输出
https://www.heyexi.com/xxx.jpg
然后我们打开服务器查看,发现前面加了一些内容,如何解决呢?
然后我们再来看一下存一个实体对象,这个实体务必要序列化,不然会出错。这是我某个项目的一个实体类,直接拿来用了。
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
@Component
public class Balance implements Serializable
{
// 主键ID
private Long id;
// 用户类型
private String ba_user_type;
// 用户编号
private Long ba_user_id;
// 可提现金额
private Float ba_balance;
// 冻结金额
private Float ba_frozen;
// 更新时间
private Timestamp ba_status_time;
// 创建时间
private Timestamp create_date;
// 备注
private String remark;
// 数据状态
private Byte status;
}
开始测试
@Autowired
private RedisTemplate redisTemplate;
@Autowired
private Balance balance;
@Test
void redis()
{
balance.setBa_balance(20.53f);
balance.setBa_status_time(DateUtil.getNowSqlDateTime());
balance.setBa_user_id(Long.valueOf(18));
balance.setStatus(Byte.valueOf("1"));
redisTemplate.opsForValue().set("balance",balance);
System.out.println(redisTemplate.opsForValue().get("balance"));
}
输出如下:
Balance(id=null, ba_user_type=null, ba_user_id=18, ba_balance=20.53, ba_frozen=null, ba_status_time=2020-12-17 16:44:43.903, create_date=null, remark=null, status=1)
还是出现这个问题,这个是因为Redis默认的序列化是jdk的序列化方式,我们需要自己重新定义RedisTemplate的序列化方式。
新建一个Redis的配置类。
@Configuration
public class MyRedis
{
@Bean
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException
{
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
// 设置其他的k-v的默认的序列化
template.setDefaultSerializer(new Jackson2JsonRedisSerializer(Object.class));
//单独设置k的序列化
template.setKeySerializer(new StringRedisSerializer());
return template;
}
}
然后再运行查看就发现OK了
最后就是我们每次操作Redis都需要获取template,而且方法不方便使用,所以项目开发中都会使用通用的工具类,以下就是一个通用的Redis操作工具了,可以直接在项目中使用。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
@Component
public final class RedisUtils
{
private RedisUtils() {}
@Autowired
@Qualifier("redisTemplate")
private RedisTemplate redisTemplate;
// =============================common============================
/**
* 指定缓存失效时间
* @param key 键
* @param time 时间(秒)
*/
public boolean expire(String key, long time) {
try {
if (time > 0) {
redisTemplate.expire(key, time, TimeUnit.SECONDS);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 根据key 获取过期时间
* @param key 键 不能为null
* @return 时间(秒) 返回0代表为永久有效
*/
public long getExpire(String key) {
return redisTemplate.getExpire(key, TimeUnit.SECONDS);
}
/**
* 判断key是否存在
* @param key 键
* @return true 存在 false不存在
*/
public boolean hasKey(String key) {
try {
return redisTemplate.hasKey(key);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 删除缓存
* @param key 可以传一个值 或多个
*/
@SuppressWarnings("unchecked")
public void del(String... key) {
if (key != null && key.length > 0) {
if (key.length == 1) {
redisTemplate.delete(key[0]);
} else {
redisTemplate.delete(CollectionUtils.arrayToList(key));
}
}
}
// ============================String=============================
/**
* 普通缓存获取
* @param key 键
* @return 值
*/
public Object get(String key) {
return key == null ? null : redisTemplate.opsForValue().get(key);
}
/**
* 普通缓存放入
* @param key 键
* @param value 值
* @return true成功 false失败
*/
public boolean set(String key, Object value) {
try {
redisTemplate.opsForValue().set(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 普通缓存放入并设置时间
* @param key 键
* @param value 值
* @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期
* @return true成功 false 失败
*/
public boolean set(String key, Object value, long time) {
try {
if (time > 0) {
redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
} else {
set(key, value);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 递增
* @param key 键
* @param delta 要增加几(大于0)
*/
public long incr(String key, long delta) {
if (delta < 0) {
throw new RuntimeException("递增因子必须大于0");
}
return redisTemplate.opsForValue().increment(key, delta);
}
/**
* 递减
* @param key 键
* @param delta 要减少几(小于0)
*/
public long decr(String key, long delta) {
if (delta < 0) {
throw new RuntimeException("递减因子必须大于0");
}
return redisTemplate.opsForValue().increment(key, -delta);
}
// ================================Map=================================
/**
* HashGet
* @param key 键 不能为null
* @param item 项 不能为null
*/
public Object hget(String key, String item) {
return redisTemplate.opsForHash().get(key, item);
}
/**
* 获取hashKey对应的所有键值
* @param key 键
* @return 对应的多个键值
*/
public Map<Object, Object> hmget(String key) {
return redisTemplate.opsForHash().entries(key);
}
/**
* HashSet
* @param key 键
* @param map 对应多个键值
*/
public boolean hmset(String key, Map<String, Object> map) {
try {
redisTemplate.opsForHash().putAll(key, map);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* HashSet 并设置时间
* @param key 键
* @param map 对应多个键值
* @param time 时间(秒)
* @return true成功 false失败
*/
public boolean hmset(String key, Map<String, Object> map, long time) {
try {
redisTemplate.opsForHash().putAll(key, map);
if (time > 0) {
expire(key, time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 向一张hash表中放入数据,如果不存在将创建
*
* @param key 键
* @param item 项
* @param value 值
* @return true 成功 false失败
*/
public boolean hset(String key, String item, Object value) {
try {
redisTemplate.opsForHash().put(key, item, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 向一张hash表中放入数据,如果不存在将创建
*
* @param key 键
* @param item 项
* @param value 值
* @param time 时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间
* @return true 成功 false失败
*/
public boolean hset(String key, String item, Object value, long time) {
try {
redisTemplate.opsForHash().put(key, item, value);
if (time > 0) {
expire(key, time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 删除hash表中的值
*
* @param key 键 不能为null
* @param item 项 可以使多个 不能为null
*/
public void hdel(String key, Object... item) {
redisTemplate.opsForHash().delete(key, item);
}
/**
* 判断hash表中是否有该项的值
*
* @param key 键 不能为null
* @param item 项 不能为null
* @return true 存在 false不存在
*/
public boolean hHasKey(String key, String item) {
return redisTemplate.opsForHash().hasKey(key, item);
}
/**
* hash递增 如果不存在,就会创建一个 并把新增后的值返回
*
* @param key 键
* @param item 项
* @param by 要增加几(大于0)
*/
public double hincr(String key, String item, double by) {
return redisTemplate.opsForHash().increment(key, item, by);
}
/**
* hash递减
*
* @param key 键
* @param item 项
* @param by 要减少记(小于0)
*/
public double hdecr(String key, String item, double by) {
return redisTemplate.opsForHash().increment(key, item, -by);
}
// ============================set=============================
/**
* 根据key获取Set中的所有值
* @param key 键
*/
public Set<Object> sGet(String key) {
try {
return redisTemplate.opsForSet().members(key);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 根据value从一个set中查询,是否存在
*
* @param key 键
* @param value 值
* @return true 存在 false不存在
*/
public boolean sHasKey(String key, Object value) {
try {
return redisTemplate.opsForSet().isMember(key, value);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将数据放入set缓存
*
* @param key 键
* @param values 值 可以是多个
* @return 成功个数
*/
public long sSet(String key, Object... values) {
try {
return redisTemplate.opsForSet().add(key, values);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 将set数据放入缓存
*
* @param key 键
* @param time 时间(秒)
* @param values 值 可以是多个
* @return 成功个数
*/
public long sSetAndTime(String key, long time, Object... values) {
try {
Long count = redisTemplate.opsForSet().add(key, values);
if (time > 0)
expire(key, time);
return count;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 获取set缓存的长度
*
* @param key 键
*/
public long sGetSetSize(String key) {
try {
return redisTemplate.opsForSet().size(key);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 移除值为value的
*
* @param key 键
* @param values 值 可以是多个
* @return 移除的个数
*/
public long setRemove(String key, Object... values) {
try {
Long count = redisTemplate.opsForSet().remove(key, values);
return count;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
// ===============================list=================================
/**
* 获取list缓存的内容
*
* @param key 键
* @param start 开始
* @param end 结束 0 到 -1代表所有值
*/
public List<Object> lGet(String key, long start, long end) {
try {
return redisTemplate.opsForList().range(key, start, end);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 获取list缓存的长度
*
* @param key 键
*/
public long lGetListSize(String key) {
try {
return redisTemplate.opsForList().size(key);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 通过索引 获取list中的值
*
* @param key 键
* @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推
*/
public Object lGetIndex(String key, long index) {
try {
return redisTemplate.opsForList().index(key, index);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 将list放入缓存
*
* @param key 键
* @param value 值
*/
public boolean lSet(String key, Object value) {
try {
redisTemplate.opsForList().rightPush(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将list放入缓存
* @param key 键
* @param value 值
* @param time 时间(秒)
*/
public boolean lSet(String key, Object value, long time) {
try {
redisTemplate.opsForList().rightPush(key, value);
if (time > 0)
expire(key, time);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将list放入缓存
*
* @param key 键
* @param value 值
* @return
*/
public boolean lSet(String key, List<Object> value) {
try {
redisTemplate.opsForList().rightPushAll(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将list放入缓存
*
* @param key 键
* @param value 值
* @param time 时间(秒)
* @return
*/
public boolean lSet(String key, List<Object> value, long time) {
try {
redisTemplate.opsForList().rightPushAll(key, value);
if (time > 0)
expire(key, time);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 根据索引修改list中的某条数据
*
* @param key 键
* @param index 索引
* @param value 值
* @return
*/
public boolean lUpdateIndex(String key, long index, Object value) {
try {
redisTemplate.opsForList().set(key, index, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 移除N个值为value
*
* @param key 键
* @param count 移除多少个
* @param value 值
* @return 移除的个数
*/
public long lRemove(String key, long count, Object value) {
try {
Long remove = redisTemplate.opsForList().remove(key, count, value);
return remove;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
}
然后来使用这个工具类就变得很方便
@Autowired
private RedisUtils redisUtils;
@Autowired
private Balance balance;
@Test
void redis()
{
balance.setBa_balance(20.53f);
balance.setBa_status_time(DateUtil.getNowSqlDateTime());
balance.setBa_user_id(Long.valueOf(18));
balance.setStatus(Byte.valueOf("1"));
redisUtils.set("new_balance",balance);
System.out.println(redisUtils.get("new_balance"));
}
给Redis设置连接密码
Redis默认连接是没有密码的,我们可以通过命令行来设置连接密码
config set requirepass "密码"
Redis持久化
Redis是内存数据库,只要服务停止,那么数据就会丢失,所以我们有必要进行数据持久化。
Redis提供了两种持久化的机制,分别是RDB(Redis DataBase)和AOF(Append Only File)。
上网查询了这两种机制的介绍如下:
RDB其实就是把数据以快照的形式保存在磁盘上。RDB持久化是指在指定的时间间隔内将内存中的数据集快照写入磁盘。也是默认的持久化方式,这种方式是就是将内存中数据以快照的方式写入到二进制文件中,默认的文件名为dump.rdb。
可以看进入docker容器后,在data目录里就有这个文件,里面就是我设置的内容。
全量备份总是耗时的,有时候我们提供一种更加高效的方式AOF,工作机制很简单,redis会将每一个收到的写命令都通过write函数追加到文件中。通俗的理解就是日志记录。
总结:
rdb就是根据配置文件中sava的时间间隔数值,在固定的时间内去保存数据,做持久化,缺点就是如果意外停止,可能导致最后几条数据来不及持久化。
aof就是把每个命令都保存在aof文件总,追加写入命令,恢复时通过命令来恢复。
但是我们一般使用默认的rdb就行了,因为这样效率高,aof占用空间大,而且Redis只是作为一个缓存的数据库,重要的持久化数据都保存在其他数据库,所以效率和性能是最重要的,这是我的理解。