天天看点

Redis 学习笔记 数据类型 事物 日志

Redis学记笔记

Redis是一个开源的内存中的数据结构存储系统,它可以用作:数据库、缓存和消息中间件。

Redis 内置了复制(Replication),LUA脚本(Lua scripting), LRU驱动事件(LRU eviction),事务(Transactions) 和不同级别的磁盘持久化(Persistence),并通过 Redis哨兵(Sentinel)和自动分区(Cluster)提供高可用性(High Availability)。

Redis也提供了持久化的选项,Redis的所有数据都是保存在内存中,然后不定期的通过异步方式保存到磁盘上(这称为“半持久化模式” RDB持久化);也可以把每一次数据变化都写入到一个append only file( AOF持久化)里面(这称为“全持久化模式”)。

Redis不使用表,他的数据库不会预定义或者强制去要求用户对Redis存储的不同数据进行关联。

数据库的工作模式按存储方式可分为:硬盘数据库和内存数据库。Redis 将数据储存在内存里面,读写数据的时候都不会受到硬盘 I/O 速度的限制,所以速度极快。

  1. Redis类型
    1. String(字符串)

string 是 redis 最基本的类型,你可以理解成与 Memcached 一模一样的类型,一个 key 对应一个 value。

string 类型是二进制安全的。意思是 redis 的 string 可以包含任何数据。比如jpg图片或者序列化的对象。

string 类型是 Redis 最基本的数据类型,string 类型的值最大能存储 512MB。

Redis 学习笔记 数据类型 事物 日志
    1. Hash(哈希)

Redis hash 是一个键值(key=>value)对集合。

Redis hash 是一个 string 类型的 field 和 value 的映射表,hash 特别适合用于存储对象。

Redis 学习笔记 数据类型 事物 日志
    1. List(列表)

Redis 列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)。

Redis 学习笔记 数据类型 事物 日志
    1. Set(集合)

Redis 的 Set 是 string 类型的无序集合。

集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是 O(1)。

Redis 学习笔记 数据类型 事物 日志
    1. zset(sorted set:有序集合)

Redis zset 和 set 一样也是string类型元素的集合,且不允许重复的成员。

不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。

zset的成员是唯一的,但分数(score)却可以重复。

Redis 学习笔记 数据类型 事物 日志
  1. Redis事务(Transactions)

概念,定义:所谓的事务,就是指对数据进行读写的一系列操作。事务在执行时,会提供专门的属性保证,包括原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability),也就是 ACID 属性。这些属性既包括了对事务执行结果的要求,也有对数据库在事务执行前后的数据状态变化的要求。

redis官方事务解释:Transactions – Redis

特性 解释 Redis是否满足
原子性(Atomicity) 一个事务中的多个操作必须都完成,或者都不完成
一致性(Consistency) 指数据库中的数据在事务执行前后是一致的
隔离性(Isolation) 它要求数据库在执行一个事务时,其它操作无法存取到正在执行事务访问的数据。
持久性(Durability) 数据库执行事务后,数据的修改要被持久化保存下来。当数据库重启后,数据的值需要是被修改后的值。
    1. Redis 如何实现事务?
  • mysql   ----->start trantation ---->普通sql ------->回滚rollback------>commit
  • redis -------> multi-(放入queue队列)-->普通命令----->discard (取消,不是真正的回滚感觉) ---->exec

事务的执行过程包含三个步骤。

  • 第一步,事务的开启 MULTI。
  • 第二步,具体操作(例如增删改数据)发送给服务器端。例如 GET、SET 等。

不过,这些命令虽然被客户端发送到了服务器端,但 Redis 实例只是把这些命令暂存到一个命令队列中,并不会立即执行。(Queued)

  • 第三步,提交事务EXEC 命令,当服务器端收到 EXEC 命令后,才会实际执行命令队列中的所有命令。

做一个简单的事务

multi

get sarula_string_test_name

set sarula_string_test_name sarula_new_name

exec

Redis 学习笔记 数据类型 事物 日志
    1. Redis 的事务机制能保证哪些属性?
  • 原子性(Atomicity)

我们需要分三种情况来看。

  1. 第一种情况是,在执行 EXEC 命令前,客户端发送的操作命令本身就有错误(比如语法错误,使用了不存在的命令),在命令入队时就被 Redis 实例判断出来了。

multi

get sarula_string_test_name

happy sarula_string_test_name

set sarula_string_test_name sarula_11111

exec

Redis 学习笔记 数据类型 事物 日志

解释:对于这种情况,在命令入队时,Redis 就会报错并且记录下这个错误。此时,我们还能继续提交命令操作。等到执行了 EXEC 命令之后,Redis 就会拒绝执行所有提交的命令操作,返回事务失败的结果。这样一来,事务中的所有命令都不会再被执行了,保证了原子性。

  1. 第一种情况不同的是,事务操作入队时,命令和操作的数据类型不匹配,但 Redis 实例没有检查出错误。但是,在执行完 EXEC 命令以后,Redis 实际执行这些事务操作时,就会报错。不过,需要注意的是,虽然 Redis 会对错误命令报错,但还是会把正确的命令执行完。在这种情况下,事务的原子性就无法得到保证了。

multi

get sarula_string_test_name

lpop sarula_string_test_name

set sarula_string_test_name sarula_11111

exec

Redis 学习笔记 数据类型 事物 日志

* 传统数据库(例如 MySQL)在执行事务时,会提供回滚机制,当事务执行发生错误时,事务中的所有操作都会撤销,Redis 中并没有提供回滚机制。虽然 Redis 提供了 DISCARD 命令,但是,这个命令只能用来主动放弃事务执行,把暂存的命令队列清空,起不到回滚的效果。

Redis 学习笔记 数据类型 事物 日志

编程无bug,那是不太可能的。

那么我们怎么去避免,脚本方式,脚本里面写判断:Redis 脚本使用 Lua 解释器来执行脚本。

简单理解就是,一组动作,要么全部执行,要么就全部不执行.从而避免出现数据不一致的情况(原子性).

  1. 第三种情况:在执行事务的 EXEC 命令时,Redis 实例发生了故障,导致事务执行失败。

在这种情况下,如果 Redis 开启了 AOF 日志,那么,只会有部分的事务操作被记录到 AOF 日志中。我们需要使用 redis-check-aof 工具检查 AOF 日志文件,这个工具可以把未完成的事务操作从 AOF 文件中去除。这样一来,我们使用 AOF 恢复实例后,事务操作不会再被执行,从而保证了原子性。当然,如果 AOF 日志并没有开启,那么实例重启后,数据也都没法恢复了,此时,也就谈不上原子性了。

redis-check-aof --fix AOF文件

Redis 学习笔记 数据类型 事物 日志
Redis 学习笔记 数据类型 事物 日志

总结

命令入队时就报错,会放弃事务执行,保证原子性;命令入队时没报错,实际执行时报错,不保证原子性;EXEC 命令执行时实例故障,如果开启了 AOF 日志,可以保证原子性。

注意理解误区:单个 Redis 命令的执行是原子性的,但 Redis 没有在事务上增加任何维持原子性的机制,所以 Redis 事务的执行并不是原子性的。事务可以理解为一个打包的批量执行脚本,但批量指令并非原子化的操作,中间某条指令的失败不会导致前面已做指令的回滚,也不会造成后续的指令不做。

  • 一致性(Consistency)
  1. 情况一:命令入队时就报错在这种情况下,事务本身就会被放弃执行,所以可以保证数据库的一致性。
  2. 情况二:命令入队时没报错,实际执行时报错在这种情况下,有错误的命令不会被执行,正确的命令可以正常执行,也不会改变数据库的一致性。
  3. 情况三:EXEC 命令执行时实例发生故障在这种情况下,实例故障后会进行重启,这就和数据恢复的方式有关了,

总结来说,在命令执行错误或 Redis 发生故障的情况下,Redis 事务机制对一致性属性是有保证的。

*如果只有部分操作被记录到了 AOF 日志,我们可以使用 redis-check-aof 清除事务中已经完成的操作,数据库恢复后也是一致的。

  • 隔离性(Isolation)

两种情况来分析:

  1. 并发操作在 EXEC 命令前执行,此时,隔离性的保证要使用 WATCH 机制来实现,否则隔离性无法保证;

Watch机制:

Redis 学习笔记 数据类型 事物 日志
Redis 学习笔记 数据类型 事物 日志

解释:WATCH 机制的作用是,在事务执行前,监控一个或多个键的值变化情况,当事务调用 EXEC 命令执行时,WATCH 机制会先检查监控的键是否被其它客户端修改了。如果修改了,就放弃事务执行,避免事务的隔离性被破坏。然后,客户端可以再次执行事务,此时,如果没有并发修改事务数据的操作了,事务就能正常执行,隔离性也得到了保证。

  1. 并发操作在 EXEC 命令后执行,此时,隔离性可以保证。(好理解,不用讲)

因为 Redis 是用单线程执行命令,而且,EXEC 命令执行后,Redis 会保证先把命令队列中的所有命令执行完。所以,在这种情况下,并发操作不会破坏事务的隔离性。

  1. Redis持久性

redis会fork一个子进程作为主进程的副本;

主进程负责接收并处理客户端请求,子进程负责将内存中的数据写入硬盘中的临时文件;

RDB(Redis DataBase)------数据  name - sarula

AOF(AppendOnlyFiel)------命令 set name sarula

两种方式:aof  rdb

    1. RDB(Redis DataBase)

RDB持久化概念:是指在指定的时间间隔内将内存中的数据集快照写入磁盘,实际操作过程是fork一个子进程,先将数据集写入临时文件,写入成功后,再替换之前的文件,用二进制压缩存储。

Redis 学习笔记 数据类型 事物 日志
Redis 学习笔记 数据类型 事物 日志
Redis 学习笔记 数据类型 事物 日志

Redis会将数据集的快照dump到dump.rdb文件中。此外,我们也可以通过配置文件来修改Redis服务器dump快照的频率,在打开redis6380.conf文件之后,我们搜索save,可以看到下面的配置信息:

cd  /usr/local/redis

save 900 1              #在900秒(15分钟)之后,如果至少有1个key发生变化,则dump内存快照。

save 300 10            #在300秒(5分钟)之后,如果至少有10个key发生变化,则dump内存快照。

save 60 10000        #在60秒(1分钟)之后,如果至少有10000个key发生变化,则dump内存快照。

    1. AOF(AppendOnlyFiel)

AOF持久化以日志的形式记录服务器所处理的每一个写、删除操作,查询操作不会记录,以文本的方式记录,可以打开文件看到详细的操作记录。

Redis 学习笔记 数据类型 事物 日志

cd /usr/local/redis

vim  redis6380.conf

在Redis的配置文件中存在三种同步方式,它们分别是:

appendfsync always     #每次有数据修改发生时都会写入AOF文件。

appendfsync everysec  #每秒钟同步一次,该策略为AOF的缺省策略。

appendfsync no          #从不同步。高效但是数据不会被持久化。

#如果AOF和RDB同时存在的时候,Redis会优先使用从AOF文件来还原数据库状态,如果AOF关闭状态时,则从RDB中恢复

  1. Redis为什么这么快
  • 采用单线程,完全基于内存,内存运算级别 (单进程多线程模型:MySQL)
  • 数据结构简单,对数据操作也简单,Redis中的数据结构是专门进行设计的;
  • 使用多路I/O复用模型,非阻塞IO;

(相关概念:并发性I/O流,意味着能够让一个计算单元来处理来自多个客户端的流请求。并行性,意味着服务器能够同时执行几个事情,具有多个计算单元)

    1. 数据库工作模式

数据库的工作模式按存储方式可分为:硬盘数据库和内存数据库。Redis 将数据储存在内存里面,读写数据的时候都不会受到硬盘 I/O 速度的限制,所以速度极快。

    1. I/O复用之 Epoll

说白一点,就是“代理”

  1. epoll 是Linux内核中的一种可扩展IO事件处理机制,核心方法只有三个。

Linux系统内核提供了三个系统调用:  include/linux/syscalls.h

源码:eventpoll.c - fs/eventpoll.c - Linux source code (v4.19.76) - Bootlin

  • epoll_create;用于创建 保存epoll文件描述符的空间。redis服务器在启动时,创建事件循环,调用epoll_create方法创建epoll实例
  • epoll_ctl:用于添加和删除监视对象的文件描述符。当有新的客户端连接时,把新的连接描述符注册到epoll实例。
  • epoll_wait:等待文件描述符发生变化。调用epoll_wait获取客户端产生的io事件
  1. epoll效率高,是因为基于红黑树(自平衡二叉树)、双向链表、事件回调机制
  2. redis的IO多路复用,Linux上用epoll进行了实现
  3. 多进程redis设置
    1. 设置步骤

cd /usr/local/redie  #切换到redis安装路径下

cp redis.conf redis6380.conf   #复制到新的配置文件供第二个redis使用

    1. 启动多进程netstat -lnpt

重启redis

redis-cli shutdown

redis-server /usr/local/redis/redis.conf

redis-server /usr/local/redis/redis6380.conf

查看 ps -aux | grep redis

  1. Redis 常见问题及解决方案
  • 缓存穿透

就是客户持续向服务器发起对不存在服务器中数据的请求。客户先在Redis中查询,查询不到后去数据库中查询。

解决方案:

1.接口层增加校验,对传参进行个校验,比如说我们的id是从1开始的,那么id<=0的直接拦截;

2.缓存中取不到的数据,在数据库中也没有取到,这时可以将key-value对写为key-null,这样可以防止攻击用户反复用同一个id暴力攻击

  • 缓存击穿

就是一个很热门的数据,突然失效,大量请求到服务器数据库中。

大家都喜欢看腾讯视频上的《水果传》,但是你的会员突然到期了,大家在你的网站上看不到腾讯视频的账号,纷纷打电话向你询问,这就是缓存击穿

解决方案:

最好的办法就是设置热点数据永不过期,拿到刚才的比方里,那就是你买腾讯一个永久会员

  • 缓存雪崩

就是大量数据同一时间失效

解决方案:

1.缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生。

2.如果缓存数据库是分布式部署,将热点数据均匀分布在不同搞得缓存数据库中。

  1. 防火墙端口的打开

centos系统防火墙需要开启

查看已开放端口

1 firewall-cmd --zone=public --list-ports

添加开放端口

1 firewall-cmd --zone=public --add-port=80/tcp --permanent

重载防火墙配置,不然查看开放端口都查不到,也不能用,重载配置后即可

1 firewall-cmd --reload

继续阅读