助力软件开发企业降本增效 PHP / java源码系统,只需一次付费,代码终身使用! 广告
**redis数据库基础** 1、redis简介 2、redis数据操作 3、redis持久化 4、redis其他知识 **redis简介** **1.1、简介** Redis是C语言开发的一个开源高性能键值对的内存数据库,可以用来做数据库、缓存、消息中间件等场景,是一种NoSQL(not-only sql,非关系型数据库)的数据库。 **1.2、特点** * 优秀的性能,数据是存储在内存中,读写速度非常快,可支持并发10W QPS * 单线程单进程, 使用非阻塞I/O, 多路复用I/O模型 * 可作为分布式锁 * 支持五种数据类型 * 支持数据持久化到磁盘 * 可以作为消息中间件使用,支持消息发布及订阅 * 默认端口6379,16个数据库,默认0库开始 * **1.3、服务端和客户端** **服务端:** * 服务器端的命令为redis-server 可以使用help查看帮助文档 ~~~ redis-server --help ~~~ * 推荐使用服务的方式管理redis服务 启动 ~~~ sudo service redis start ~~~ 停止 ~~~ sudo service redis stop ~~~ 重启 ~~~ sudo service redis restart ~~~ **客户端:** * 客户端的命令为redis-cli 可以使用help查看帮助文档 ~~~ redis-cli --help ~~~ 运行测试命令 ~~~ ping ~~~ 切换数据库(数据库没有名称,默认有16个,通过0-15来标识) ~~~ select 1 ~~~ ![图片](https://mmbiz.qpic.cn/mmbiz_png/EuxggHvdt57yEhJUaico4pHJDicfGlRxFbnwzbklVI3hL7j3DYnaVYrIROiaibMLzNIazwUTnL0UaTBrkO8yjjU91g/640?wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1) **redis数据操作** PART 02 redis是key-value的数据结构,每条数据都是一个键值对,键的类型是字符串(键不能重复),值的类型分为五种: * 字符串string * 哈希hash * 列表list * 集合set * 有序集合zset **2.1、字符串string** * string是redis最基本的类型 * 最大能存储512MB数据 * string类型是二进制安全的,可以存储任何数据,比如数字、图片等 **增加、修改** ~~~ # 如果设置的键不存在则为添加,如果设置的键已经存在则修改 ~~~ **获取** ~~~ 根据键获取值,如果不存在此键则返回nil ~~~ **键命令(查找、删除)** ~~~ 查找键,参数通配符查找 ~~~ **2.2、哈希hash** * hash用于存储对象,对象的结构为属性、值 * 值的类型为string **增加、修改** ~~~ 设置单个属性 ~~~ **获取** ~~~ 获取指定键所有的属性 ~~~ **删除** ~~~ 删除整个hash键及值,使用del命令 ~~~ **2.3、列表list** * 列表的元素类型为string * 按照插入顺序排序 **增加** ~~~ 在左侧插入数据 ~~~ **修改 ** ~~~ 设置指定索引位置的元素值 ~~~ **获取** ~~~ 返回列表里指定范围内的元素 ~~~ **删除** ~~~ 删除指定元素 ~~~ **2.4、集合set** * 无序集合 * 元素为string类型 * 元素具有唯一性,不重复 * 对于集合没有修改操作 **增加** ~~~ #添加元素 ~~~ **获取** ~~~ 返回所有的元素 ~~~ **删除** ~~~ 删除指定元素 ~~~ **2.5、有序集合zset** * sorted set,有序集合 * 元素为string类型 * 元素具有唯一性,不重复 * 每个元素都会关联一个double类型的score,表示权重,通过权重将元素从小到大排序 * 没有修改操作 **增加** ~~~ 添加 ~~~ **获取** ~~~ 返回指定范围内的元素 ~~~ **删除** ~~~ 删除指定元素 ~~~ ![图片](https://mmbiz.qpic.cn/mmbiz_png/EuxggHvdt57yEhJUaico4pHJDicfGlRxFbnwzbklVI3hL7j3DYnaVYrIROiaibMLzNIazwUTnL0UaTBrkO8yjjU91g/640?wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1) **redis持久化** PART 03 **3.1、redis持久化方式** redis是一个内存数据库,一旦断电或服务器进程退出,内存数据库中的数据将全部丢失,所以需要redis持久化。 redis持久化就是把数据保存在磁盘上,利用永久性存储介质将数据保存,在特定的时间将保存的数据进行恢复的工作机制。 redis提供两种持久化机制: * RDB:存储数据结果,关注点在数据 * AOF:存储操作过程,关注点在数据的操作过 程 **3.2、RDB** * 原理:将Reids在内存中的数据库记录定时dump到磁盘上的RDB持久化,直接把内存中的数据保存到一个 dump 的文件中。 * 开启RDB持久化方式: 客户端可以通过向Redis服务器发送save或bgsave命令让服务器生成rdb文件,或者通过服务器配置文件指定触发RDB条件。 ~~~ # 同步数据到磁盘上命令 ~~~ ![图片](https://mmbiz.qpic.cn/mmbiz_png/EuxggHvdt55tsX8Hvj2TZLxFmUInKUcjzmjV6icc4ria2R2Lfp5Z27Slal3eEQylvUOhWCtI3FYS68hGlTJ0gCmQ/640?wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1) 由于redis的单进程单线程特性,当客户端向服务器发送save命令请求进行持久化时,服务器会阻塞save命令之后的其他客户端的请求,直到数据同步完成。如果数据量太大,同步数据会执行很久,而这期间redis服务器也无法接收其他请求,所以,最好不要在生产环境使用save命令。 ~~~ # 异步保存数据集到磁盘上 ~~~ ![图片](https://mmbiz.qpic.cn/mmbiz_png/EuxggHvdt55tsX8Hvj2TZLxFmUInKUcjQfVwGs5nEtcia6eeb2K2SHcR5ZnwQPQKViahsk1o8fM2NDOcb2JJ6kKg/640?wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1) 当客户端发服务发出bgsave命令时,redis服务器主进程会forks一个子进程来数据同步问题,在将数据保存到rdb文件之后,子进程会退出。所以,与save命令相比,redis服务器在处理bgsave采用子线程进行IO写入,而主进程仍然可以接收其他请求,但forks子进程是同步的,所以forks子进程时,一样不能接收其他请求,这意味着,如果forks子进程花费的时间太久(一般是很快的),bgsave命令仍然有阻塞其他客户的请求的情况发生。 ~~~ # 服务器配置自动触发 ~~~ 这种通过服务器配置文件触发RDB的方式,与bgsave命令类似,达到触发条件时,会forks一个子进程进行数据同步,不过最好不要通过这种方式来触发RDB持久化,因为设置触发的时间太短,则容易频繁写入rdb文件,影响服务器性能,时间设置太长则会造成数据丢失。 * RDB文件: 执行同步数据到磁盘操作后,生成临时rdb文件,并写入数据;完成数据写入,用临时文代替代正式rdb文件,删除原来的rdb文件。RDB默认生成的文件名为dump.rdb(可通过配置文件修改RDB默认文件名)。 **3.3、AOF** * 原理:将Reids的操作日志以追加的方式写入文件,把所有的对 Redis的服务器进行修改的命令都存到一个文件里,保存的是命令的集合。 * 开启AOF持久化方式: ![图片](https://mmbiz.qpic.cn/mmbiz_png/EuxggHvdt55tsX8Hvj2TZLxFmUInKUcjN5d26tgFDVMqTicXbfWeiclibY8M84icSBiaesAkRIPZK8tNVsnZl6doia9Q/640?wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1) 与RDB存储某个时刻的快照不同,AOF持久化方式会记录客户端对服务器的每一次写操作命令,并将这些写操作以Redis命令追加保存到以后缀为aof文件的末尾,在Redis 服务器重启时,会加载并运行aof文件的命令,以达到恢复数据的目的。 Redis默认不开启AOF持久化方式,需要在配置文件中开启并进行更加详细的配置,如下面的redis.conf文件: ~~~ # 开启aof机制 ~~~ * 三种写入策略: 1. always : 客户端的每一个写操作都保存到aof文件,这种策略很安全,但是每个写请注都有IO操作,所以也很慢。 2. everysec : appendfsync的默认写入策略,每秒写入一次aof文件,因此,最多可能会丢失1s的数据。 3. no:Redis服务器不负责写入aof,而是交由操作系统来处理什么时候写入aof文件。更快,但也是最不安全的选择,不推荐使用。 **3.4、两者区别&优劣势** 区别: * RDB持久化是指在指定的时间间隔内将内存中的数据集快照写入磁盘,实际操作过程是fork一个子进程,先将数据集写入临时文件,写入成功后,再替换之前的文件,用二进制压缩存储。 * AOF持久化以日志的形式记录服务器所处理的每一个写、删除操作,查询操作不会记录,以文本的方式记录,可以打开文件看到详细的操作记录。 优劣势: * 如果非常关心你的数据,但仍然可以承受数分钟内的数据丢失,那么可以额只使用 RDB持久化。 * AOF将Redis 执行的每一条命令追加到磁盘中,处理巨大的写入会降低Redis的 性能。 * 数据库备份和灾难恢复:定时生成 RDB 快照非常便于进行数据库备份,并且 R DB恢复数据集的速度也要比 AOF 恢复的速度快。 (Redis 支持同时开启 RDB 和 AOF,系统重启后,Redis 会优先使用 AOF 来恢复数据,这样丢失的数据会最少。) ![图片](https://mmbiz.qpic.cn/mmbiz_png/EuxggHvdt57yEhJUaico4pHJDicfGlRxFbnwzbklVI3hL7j3DYnaVYrIROiaibMLzNIazwUTnL0UaTBrkO8yjjU91g/640?wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1) **redis其他知识** PART 04 **4.1、主从模式** * 原理: 将一台redis服务器的数据,复制到其他的redis服务器,前者称为主节点(master/leader,主节点不用配置,redis默认单机就是主节点),后者称为从节点(slave/follower);数据的复制是单向的,只能由主节点到从节点。master以写为主,slave以读为主。主从复制,读写分离。 * 作用: 数据冗余(实现数据的热备份)、故障恢复、负载均衡(读写分离等操作,可以减缓服务器压力,架构中经常使用)、高可用基石(它是哨兵与集群能够实施的基础)、集群模式的最低配置应该是一主二从,三台服务器。 * 主从复制步骤 当slave启动成功连接到master后会发送一个sync同步命令,master接到命令,启动后台的存盘进程,同时收集所有用于修改数据集命令,在后台进程执行完毕后,master将传送整个数据文件(RDB文件)到slave,并且完成一次完全同步(全量复制);只要是重新连接master,全量复制将会被自动执行,数据一定可以在从机中看到。 全量复制 —— slave服务在接收到数据库文件数据后,将其存盘并加载到内存中; 增量复制 —— master继续将新的所有收集到的修改命令依次传给slave,完成同步; ![图片](https://mmbiz.qpic.cn/mmbiz_png/EuxggHvdt55tsX8Hvj2TZLxFmUInKUcjXWWF5wDuRsp2hNQvialvwYZZicHAibxn9yFf15mia08BDVRH419ZZtOgJQ/640?wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1) ~~~ # 主从复制只需要在从机中配置,命令如下: ~~~ **4.2、哨兵模式** * 原理: 哨兵模式就是自动选取主机的方法,哨兵模式是一种特殊的模式,redis提供了哨兵的命令,它是一个独立的进程,原理是哨兵通过发送命令,等待Redis服务器响应,从而监控运行的多个redis实例。 * 哨兵模式的由来: 主从复制会存在如下问题:一旦主节点宕机,从节点晋升为主节点,同时需要修改应用方的主节点地址,还需要命令所有从节点去复制新的主节点,整个过程需要人工干预。 * 哨兵模式的作用: 1. 通过发送命令,令redis服务器返回运行状态,包括主服务器与从服务器; 2. 当哨兵检测到master宕机,会自动将slave切换到master,然后通过发布订阅模式通知其他从服务器,修改配置文件,切换主机。 * 哨兵的架构模式如下: ![图片](https://mmbiz.qpic.cn/mmbiz_jpg/EuxggHvdt55tsX8Hvj2TZLxFmUInKUcjbyuDY8YLN2ibMz5KiaVw5D7iaG1LLTOqkibQEh8vvGUnwgQQPb3Tp9Mefg/640?wx_fmt=jpeg&wxfrom=5&wx_lazy=1&wx_co=1) 可以执行以下四个任务: * 监控:不断检查主服务器和从服务器是否正常运行。 * 通知:当被监控的某个 Redis 服务器出现问题,Sentinel 通过 API 脚本向管理员或者其他应用程序发出通知。 * 自动故障转移:当主节点不能正常工作时,Sentinel 会开始一次自动的故障转移操作,它会将与失效主节点是主从关系的其中一个从节点升级为新的主节点,并且将其他的从节点指向新的主节点,这样人工干预就可以免了。 * 配置提供者:在 Redis Sentinel 模式下,客户端应用在初始化时连接的是 Sentinel 节点集合,从中获取主节点的信息。 **4.3、集群模式** * 原理: 哨兵模式虽然基本已经可以实现高可用、读写分离;但是在这种模式下每台 Redis 服务器都存储相同的数据,很浪费内存,所以在redis3.0上加入了 Cluster 集群模式,实现了 Redis 的分布式存储,也就是说每台 Redis 节点上存储不同的内容。 * 集群的配置: 集群部署至少需要3台以上的master节点,最好使用3主3从6个节点的模式;这里的 6 台 redis 两两之间并不是独立的,每个节点都会通过集群总线(cluster bus),与其他的节点进行通信。所有的redis节点彼此互联,内部使用二进制协议优化传输速度和带宽;节点的 fail 是通过集群中超过半数的节点检测失效时才生效;客户端与 Redis 节点直连,不需要中间代理层,客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可。 ![图片](https://mmbiz.qpic.cn/mmbiz_png/EuxggHvdt55yicuye5Sk83UZtJQPvzwLrvrMnV60V9FcYncnwibouwNTdsdcE8BUmqm1q28DtAHMVOWicDlic7BQibg/640?wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1) * 工作方式: 在 Redis 的每一个节点上,都有两个东西,一个是插槽(slot),它的取值范围是:0-16384。还有一个就是cluster,可以理解为是一个集群管理的插件。当我们的存取的 Key到达的时候,Redis 会根据crc16的算法得出一个结果,然后把结果对16384求余数,这样每个 key 都会对应一个编号在0-16384 之间的哈希槽,通过这个值,去找到对应的插槽所对应的节点,然后直接自动跳转到这个对应的节点上进行存取操作。 **4.4、reids的雪崩&穿透&击穿** **缓存雪崩** * 定义: redis缓存中大量的key同时失效,此时又刚好有大量的请求打进来,直接打到数据库层,造成数据库阻塞甚至宕机。 ![图片](https://mmbiz.qpic.cn/mmbiz_png/EuxggHvdt55yicuye5Sk83UZtJQPvzwLrVfZTIiaPb2trvfu34JCFiczLNz5VGuCyIdwdXQ6jRP9libqZJczd24jKA/640?wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1) * 解决办法: 1. 让redis数据永不过期,这种方式最可靠的,最安全的但是占空间,内存消耗大,并且不能保持数据最新,所以需要根据具体的业务逻辑来做 2. 将缓存失效时间分散开,比如每个key的过期时间都是随机的,防止同一时间大量数据过期的现象发生,就不会出现同一时间全部请求都落在数据库。 3. 因为redis宕机导致缓存雪崩的问题,可以启动服务熔断机制,暂停业务应用对缓存服务的访问,直接返回错误,但是暂停了业务应用访问缓存系统,全部业务都无法正常的工作 4. 创造redis集群,对数据库进行读写分离 **缓存穿透** * 定义: 指查询一个缓存和数据库都不存在的数据,导致尽管数据不存在但是每次都会到数据库查询。在访问量大时可能DB就会挂掉。如果有人利用不存在的key频繁攻击,则这就形成了漏洞。 ![图片](https://mmbiz.qpic.cn/mmbiz_png/EuxggHvdt55yicuye5Sk83UZtJQPvzwLrhXP2zS4iaOiciaF1vVI8zcHZyuE1W7KsjX643MqutRibFvrTfx1aC9w38g/640?wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1) * 解决办法: 1. 如果一个查询返回的数据为空,我们仍然把这个空结果进行缓存,但它的过期时间会很短,最长不超过五分钟。 2. 接口层增加校验,用户鉴权,id做基础校验。 3. 采用布隆过滤器,将所有可能存在的数据hash到一个足够大的bitmap中。 布隆过滤器:类似于HashSet,可以快速判断一个元素在集合中是否存在,应用场景:快速判断一个元素是否在某容器内,不存在直接返回。(关键点在于hash算法和容器大小。) **缓存击穿** * 定义: 击穿与雪崩的不同在于缓存key失效的量级上。击穿是对于单个key值(热点数据)的缓存失效过期,雪崩则是大面积key同时失效。 ![图片](https://mmbiz.qpic.cn/mmbiz_png/EuxggHvdt55yicuye5Sk83UZtJQPvzwLr8Tu8Tsj3nYN4bfoONVUCIFMu1yXUn6X8VLnzxPlkuAJYD4RiczeZDHA/640?wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1) * 解决办法: 1. 若缓存数据基本不会发生更新,则可尝试将热点数据设置为永不过期。 2. 若缓存的数据更新不频繁,且缓存刷新的整个流程耗时较少的情况下,则可以采用基于Redis、zookeeper 等分布式中间件的分布式互斥锁,或者本地互斥锁以保证仅少量的请求能请求数据库并重新构建缓存,其余线程则在锁释放后能访问到新缓存。 3. 若缓存的数据更新频繁或者在缓存刷新的流程耗时较长的情况下,可以利用定时线程在缓存过期前主动地重新构建缓存或者延后缓存的过期时间,以保证所有的请求能一直访问到对应的缓存。