课程计划html
1. REDIS 入 门 | (了解) | (操做) | |
---|---|---|---|
2. 数据类型 | (重点) | (操做) | (理解) |
3. 经常使用指令 | (操做) | ||
4. Jedis | (重点) | (操做) | |
5. 持 久 化 | (重点) | (理解) | |
6. 数据删除与淘汰策略 | (理解) | ||
7. 主从复制 | (重点) | (操做) | (理解) |
8. 哨 兵 | (重点) | (操做) | (理解) |
9. Cluster集群方案 | (重点) | (操做) | (理解) |
10. 企业级缓存解决方案 | (重点) | (理解) | |
11. 性能指标监控 | (了解) |
在这个部分,咱们将学习如下3个部分的内容,分别是:java
◆ Redis 简介(NoSQL概念、Redis概念)linux
◆ Redis 的下载与安装git
◆ Redis 的基本操做github
在讲解NoSQL的概念以前呢,咱们先来看一个现象:redis
(1)问题现象sql
每一年到了过年期间,你们都会自觉自发的组织一场活动,叫作春运!之前咱们买票都是到火车站排队,后来呢有了12306,有了他之后就更方便了,咱们能够在网上买票,可是带来的问题,你们也很清楚,春节期间买票进不去,进去了刷不着票。什么缘由呢,人太多了!数据库
除了这种作铁路的,它系统作的不专业之外,还有马爸爸作的淘宝,它面临同样的问题。淘宝也崩,也是用户量太大!做为咱们整个电商界的东哥来讲,他第一次作图书促销的时候,也遇到了服务器崩掉的这样一个现象,缘由一样是由于用户量太大!编程
(2)现象特征json
再来看这几个现象,有两个很是类似的特征:
第一,用户比较多,海量用户
第二,高并发
这两个现象出现之后,对应的就会形成咱们的服务器瘫痪。核心本质是什么呢?其实并非咱们的应用服务器,而是咱们的关系型数据库。关系型数据库才是最终的罪魁祸首!
(3)形成缘由
什么样的缘由致使的整个系统崩掉的呢:
1.性能瓶颈:磁盘IO性能低下
关系型数据库菜存取数据的时候和读取数据的时候他要走磁盘IO。磁盘这个性能自己是比较低的。
2.扩展瓶颈:数据关系复杂,扩展性差,不便于大规模集群
咱们说关系型数据库,它里面表与表之间的关系很是复杂,不知道你们能不能想象一点,就是一张表,经过它的外键关联了七八张表,这七八张表又经过她的外键,每张又关联了四五张表。你想一想,查询一下,你要想拿到数据,你就要从A到B、B到C、C到D的一直这么关联下去,最终很是影响查询的效率。同时,你想扩展下,也很难!
(4)解决思路
面对这样的现象,咱们要想解决怎么版呢。两方面:
一,下降磁盘IO次数,越低越好。
二,去除数据间关系,越简单越好。
下降磁盘IO次数,越低越好,怎么搞?我不用你磁盘不就好了吗?因而,内存存储的思想就提出来了,我数据不放到你磁盘里边,放内存里,这样是否是效率就高了。
第二,你的数据关系很复杂,那怎么办呢?干脆简单点,我断开你的关系,我不存关系了,我只存数据,这样不就没这事了吗?
把这两个特征一合并一块儿,就出来了一个新的概念:NoSQL
(1)概念
NoSQL:即 Not-Only SQL( 泛指非关系型的数据库),做为关系型数据库的补充。 做用:应对基于海量用户和海量数据前提下的数据处理问题。
他说这句话说的很是客气,什么意思呢?就是咱们数据存储要用SQL,可是呢能够不只仅用SQL,还能够用别的东西,那别的东西叫什么呢?因而他定义了一句话叫作NoSQL。这个意思就是说咱们存储数据,能够不光使用SQL,咱们还能够使用非SQL的这种存储方案,这就是所谓的NoSQL。
(2)特征
可扩容,可伸缩。SQL数据关系过于复杂,你扩容一下难度很高,那咱们Nosql 这种的,不存关系,因此它的扩容就简单一些。
大数据量下高性能。包数据很是多的时候,它的性能高,由于你不走磁盘IO,你走的是内存,性能确定要比磁盘IO的性能快一些。
灵活的数据模型、高可用。他设计了本身的一些数据存储格式,这样能保证效率上来讲是比较高的,最后一个高可用,咱们等到集群内部分再去它!
(3)常见 Nosql 数据库
目前市面上常见的Nosql产品:Redis、memcache、HBase、MongoDB
(4)应用场景-电商为例
咱们以电商为例,来看一看他在这里边起到的做用。
第一类,在电商中咱们的基础数据必定要存储起来,好比说商品名称,价格,生产厂商,这些都属于基础数据,这些数据放在MySQL数据库。
第二类,咱们商品的附加信息,好比说,你买了一个商品评价了一下,这个评价它不属于商品自己。就像你买一个苹果,“这个苹果很好吃”就是评论,可是你能说很好吃是这个商品的属性嘛?不能这么说,那只是一我的对他的评论而已。这一类数据呢,咱们放在另一个地方,咱们放到MongoDB。它也能够用来加快咱们的访问,他属于NoSQL的一种。
第三,图片内的信息。注意这种信息相对来讲比较固定,他有专用的存储区,咱们通常用文件系统来存储。至因而不是分布式,要看你的系统的一个整个 瓶颈 了?若是说你发现你须要作分布式,那就作,不须要的话,一台主机就搞定了。
第四,搜索关键字。为了加快搜索,咱们会用到一些技术,有些人可能了解过,像分ES、Lucene、solr都属于搜索技术。那说的这么热闹,咱们的电商解决方案中还没出现咱们的redis啊!注意第五类信息。
第五,热点信息。访问频度比较高的信息,这种东西的第二特征就是它具备波段性。换句话说他不是稳定的,它具备一个时效性的。那么这类信息放哪儿了,放到咱们的redis这个解决方案中来进行存储。
具体的咱们从咱们的整个数据存储结构的设计上来看一下。
咱们的基础数据都存MySQL,在它的基础之上,咱们把它连在一起,同时对外提供服务。向上走,有一些信息加载完之后,要放到咱们的MongoDB中。还有一类信息,咱们放到咱们专用的文件系统中(好比图片),就放到咱们的这个搜索专用的,如Lucene、solr及集群里边,或者用ES的这种技术里边。那么剩下来的热点信息,放到咱们的redis里面。
概念:Redis (Remote Dictionary Server:远程词典服务器) 是用 C 语言开发的一个开源的高性能(内存型)键值对(key-value)数据库。
特征:
(1)数据间没有必然的关联关系;
(2)内部采用单线程机制进行工做;
(3)高性能。官方提供测试数据,50个并发执行100000 个请求,读的速度是110000 次/s,写的速度是81000次/s。
(4)多数据类型支持
字符串类型,string
列表类型,list
hash set
散列类型,zset/sorted_set
集合类型
有序集合类型
(5)支持持久化,能够进行数据灾难恢复
(1)为热点数据加速查询(主要场景)。如热点商品、热点新闻、热点资讯、推广类等高访问量信息等。
(2)即时信息查询。如各位排行榜、各种网站访问统计、公交到站信息、在线人数信息(聊天室、网站)、设备信号等。
(3)时效性信息控制。如验证码控制、投票控制等。
(4)分布式数据共享。如分布式集群架构中的 session 分离 (5)消息队列
后期全部资料分4中不一样色块显示,详情以下:
本课程所示,均基于Center OS7安装Redis。
安装的时候可能还会须要安装gcc环境:因此能够参照:
http://blog.sina.com.cn/s/blog_ae829c5c0102z27v.html
(1)下载并安装Redis
下载安装包:
wget http://download.redis.io/releases/redis-5.0.0.tar.gz
解压安装包:
tar –xvf redis-5.0.0.tar.gz
编译(在解压的目录中执行):
make
安装(在解压的目录中执行):
make install
(2)redis相关命令
redis-server,服务器启动命令 客户端启动命令
redis-cli,redis核心配置文件
redis.conf,RDB文件检查工具(快照持久化文件)
redis-check-dump,AOF文件修复工具
redis-check-aof
资料中找到“Redis-x64-3.2.100.msi”
双击进行安装:选择目录,勾选添加环境变量
打开cmd:执行redis-server "D:\Program Files\Redis\redis.windows.conf"
出现Server started便可
注意:这个窗口不可关闭,一旦关闭redis服务将中止
没有出现上述图片,而是报错:“ listening socket 127.0.0.1:6379: bind: No error ”
解决
客户端链接redis服务:如能出现以下效果即说明redis服务真正启动成功
找到资料:“redis-desktop-manager-0.8.8.384.exe”
双击安装,默认安装便可
双击桌面“RedisDesktopManager”图标,打开redis桌面管理工具
创建链接
输入链接内容:
左侧出现链接
双击链接:出现redis默认的15个库
到此为止,说明图形化客户端链接redis服务成功
启动服务器——参数启动
redis-server [--port port]
范例
redis-server --port 6379
启动服务器——配置文件启动
redis-server config_file_name
范例
redis-server redis.conf
启动客户端
redis-cli [-h host] [-p port]
范例
redis-cli –h 61.129.65.248 –p 6384
注意:服务器启动指定端口使用的是--port,客户端启动指定端口使用的是-p。-的数量也不一样。
建立配置文件存储目录
mkdir conf
建立服务器文件存储目录(包含日志、数据、临时配置文件等)
mkdir data
建立快速访问连接
ln -s redis-5.0.0 redis
修改启动配置文件redis.conf/redis.windows.conf
配置项介绍:
设置服务器以守护进程的方式运行,开启后服务器控制台中将打印服务器运行信息(同日志内容相同)
daemonize yes|no # 改成yes就是后台启动,看不到以前的欢迎信息,cmd窗口关闭也没影响
#windows版本的redis不支持后台启动,配置也没有用
绑定主机地址:bind ip
bind 127.0.0.1 #指定当前redis服务运行在哪一个ip上(通常都指定为本机)
设置服务器端口
port 6379 #6379是redis的默认端口
日志文件
logfile "日志文件名"
设置服务器文件保存地址:dir path
dir "/redis/data"
windows配置为:dir "./data"(须要在redis安装的目录新建data文件夹)
启动
配置为后台启动以后,能够经过ps查看是否启动
开发的时候,咱们仍是经过前台启动,由于查看日志方便
杀掉进程:kill 9 6457
从新配置daemonize no
屏蔽logfile (若是不屏蔽,日志仍是会写入日志文件,不会直接在cmd窗口显示)
服务器容许客户端链接最大数量,默认0,表示无限制。当客户端链接到达上限后,Redis会拒绝新的链接
maxclients count
客户端闲置等待最大时长,达到最大值后关闭对应链接。如需关闭该功能,设置为 0
timeout seconds
设置服务器以指定日志记录级别
loglevel debug|verbose|notice|warning
日志记录文件名
logfile filename
注意:日志级别开发期设置为verbose便可,生产环境中配置为notice,简化日志输出量,下降写日志IO的频度。
咱们须要会如下操做:
功能性命令
帮助信息查阅
退出指令
清除屏幕信息
设置 key,value 数据
set key value
范例
set name itheima
根据 key 查询对应的 value,若是不存在,返回空(nil)
get key
范例
get name
获取命令帮助文档
help [command]
范例
help set
获取组中全部命令信息名称
help [@group-name]
范例
help @string
1.6.4 退出命令行客户端模式
退出客户端
quit 或 exit
快捷键
Ctrl+C
到这里,Redis 入门的相关知识,咱们就所有学习完了,再来回顾一下,这个部分咱们主要讲解了哪些内容呢?
首先,咱们对Redis进行了一个简单介绍,包括NoSQL的概念、Redis的概念等
而后,咱们介绍了Redis 的下载与安装。包括下载与安装、服务器与客户端启动、以及相关配置文件(3类)
最后,咱们介绍了Redis 的基本操做。包括数据读写、退出与帮助信息获取
在这个部分,咱们将学习一共要学习三大块内容:
首先须要了解一下数据类型
接下来将针对着咱们要学习的数据类型进行逐一的讲解,如string、hash、list、set等
最后咱们经过一个案例来总结前面的数据类型的使用场景
在讲解数据类型以前,咱们得先思考一个问题,数据类型既然是用来描述数据的存储格式的,若是你不知道哪些数据将来会进入到咱们来的redis中,那么对应的数据类型的选择,你就会出现问题,咱们一块来看一下:
(1)原始业务功能设计
秒杀。他这个里边数据变化速度特别的快,访问量也特别的高,用户大量涌入之后都会针对着一部分数据进行操做,这一类要记住。
618活动。对于咱们京东的618活动、以及天猫的双11活动,相信你们不用说都知道这些数据必定要进去,由于他们的访问频度实在过高了。
排队购票。咱们12306的票务信息。这些信息在原始设计的时候,他们就注定了要进redis。
(2)运营平台监控到的突发高频访问数据
此类平台临时监控到的这些数据,好比说如今出来的一个八卦的信息,这个新闻一旦出现之后呢,顺速的被围观了,那么这个时候,这个数据就会变得访量特别高,那么这类信息也要进入进去。
(3)高频、复杂的统计数据
在线人数。好比说直播如今很火,直播里边有不少数据,例如在线人数。进一我的出一我的,这个数据就要跳动,那么这个访问速度很是的快,并且访量很高,而且它里边有一个复杂的数据统计,在这里这种信息也要进入到咱们的redis中。
投票排行榜。投票投票类的信息他的变化速度也比较快,为了追求一个更快的一个即时投票的名次变化,这种数据最好也放到redis中。
基于以上数据特征咱们进行分析,最终得出来咱们的Redis中要设计5种 数据类型:
string、hash、list、set、sorted_set/zset(应用性较低)
在学习第一个数据类型以前,先给你们介绍一下,在随后这部份内容的学习过程当中,咱们每一种数据类型都分红三块来说:
首先是讲下它的基本操做
接下来说一些它的扩展操做
最后咱们会去作一个小的案例分析
在学习string这个数据形式以前,咱们先要明白string究竟是修饰什么的
咱们知道redis 自身是一个 Map,其中全部的数据都是采用 key : value 的形式存储
对于这种结构来讲,咱们用来存储数据必定是一个值前面对应一个名称,咱们经过名称来访问后面的值。
前面这一部分咱们称为key,后面的一部分称为value,而咱们的数据类型,他必定是修饰value的。
数据类型指的是存储的数据的类型,也就是 value 部分的类型,key 部分永远都是字符串。
(1)存储的数据:单个数据,最简单的数据存储类型,也是最经常使用的数据存储类型。
string,他就是存一个字符串儿,注意是value那一部分是一个字符串,它是redis中最基本、最简单的存储数据的格式。
(2)存储数据的格式:一个存储空间保存一个数据
每个空间中只能保存一个字符串信息,这个信息里边若是是存的纯数字,他也能当数字使用,咱们来看一下,这是咱们的数据的存储空间。
(3)存储内容:一般使用字符串,若是字符串以整数的形式展现,能够做为数字操做使用.
一个key对一个value,而这个itheima就是咱们所说的string类型,固然它也能够是一个纯数字的格式。
(1)基础指令
添加/修改数据添加/修改数据
set key value
获取数据
get key
删除数据
del key
断定性添加数据 (了解)
setnx key value #SET if Not eXists (大写组成命令)
添加/修改多个数据
mset key1 value1 key2 value2 … #m:Multiple 多个 many
获取多个数据
mget key1 key2 …
获取数据字符个数(字符串长度)
strlen key #STRing LENgth
追加信息到原始信息后部(若是原始信息存在就追加,不然新建)
append key value
演示:
(2)单数据操做与多数据操做的选择之惑
即set 与mset的关系。这对于这两个操做来讲,没有什么你应该选哪一个,而是他们本身的特征是什么,你要根据这个特征去比对你的业务,看看究竟适用于哪一个。
假如说这是咱们如今的服务器,他要向redis要数据的话,它会发出一条指令。那么当这条指令发过来的时候,好比说是这个set指令过来,那么它会把这个结果返回给你,这个时候咱们要思考这里边一共通过了多长时间。
首先,发送set指令要时间,这是网络的一个时间,接下来redis要去运行这个指令要消耗时间,最终把这个结果返回给你又有一个时间,这个时间又是一个网络的时间,那咱们能够理解为:一个指令发送的过程当中须要消耗这样的时间.
可是若是说如今不是一条指令了,你要发3个set的话,还要多长时间呢?对应的发送时间要乘3了,由于这是三个单条指令,而运行的操做时间呢,它也要乘3了,但最终返回的也要发3次,因此这边也要乘3。
因而咱们能够获得一个结论:单指令发3条它须要的时间,假定他们两个同样,是6个网络时间加3个处理时间,若是咱们把它合成一个mset呢,咱们想想。
假如说用多指令发3个指令的话,其实只须要发一次就好了。这样咱们能够获得一个结论,多指令发3个指令的话,其实它是两个网络时间加上3个redis的操做时间,为何这写一个小加号呢,就是由于毕竟发的信息量变大了,因此网络时间有可能会变长。
那么经过这张图,你就能够获得一个结论,咱们单指令和多指令他们的差异就在于你发送的次数是多仍是少。当你影响的数据比较少的时候,你能够用单指令,也能够用多指令。可是一旦这个量大了,你就要选择多指令了,他的效率会高一些。
下面咱们来看一string的扩展操做,分红两大块:一块是对数字进行操做的,第二块是对咱们的key的时间进行操做的。
设置数值数据增长指定范围的值
incr key #相似于i++ incrby key increment #至关于i+n incrbyfloat key increment #能够操做小数 #increment 增长
设置数值数据减小指定范围的值
decr key #相似于i-- decrby key increment #至关于i-n #decrement 减小
设置数据具备指定的生命周期 (***)
setex key seconds value *** psetex key milliseconds value #expire : 有效期,期限
演示:
数据操做不成功的反馈与数据正常操做之间的差别
表示运行结果是否成功 : 例如setnx
(integer) 0 → false 失败
(integer) 1 → true 成功
表示运行结果值:例如获取数据长度 strlen
(integer) 3 → 3 3个
(integer) 1 → 1 1个
数据未获取到时,对应的数据为(nil),等同于null
数据最大存储量:512MB
string在redis内部存储默认就是一个字符串,当遇到增减类操做incr,decr时会转成数值型进行计算
注意:redis虽然能够存储数字,可是数字在内部存储时依然是字符串类型
按数值进行操做的数据,若是原始数据不能转成数值,或超越了redis 数值上限范围,将报错
上限是:9223372036854775807(也就是java中Long型数据最大值,Long.MAX_VALUE)
redis全部的操做都是原子性的,采用单线程处理全部业务,命令是一个一个执行的,所以无需考虑并发带来的数据影响.
它的应用场景在于:主页高频访问信息显示控制,例如新浪微博大V主页显示粉丝数与微博数量。
咱们来思考一下:这些信息是否是你进入大V的页面儿之后就要读取这写信息的啊,那这种信息必定要存储到咱们的redis中,由于他的访问量过高了!那这种数据应该怎么存呢?咱们来一起看一下方案!
(1)在redis中为大V用户设定用户信息,以用户主键和属性值做为key,后台设定定时刷新策略便可。
eg: user:id:3506728370:fans → 12210947 eg: user:id:3506728370:blogs → 6164 eg: user:id:3506728370:focuses → 83 #eg就是例子的意思
演示:
这个用户的id为123456,这个大v存储了blogs和fans俩信息,分开存储
(2)也能够使用json格式保存数据
eg: user:id:3506728370 → {“fans”:12210947,“blogs”:6164,“ focuses ”:83 }
(3) key 的设置约定
数据库中的热点数据key命名惯例:由表名:主键名:主键值:字段名,做为key名
表名 | 主键名 | 主键值 | 字段名 | |
---|---|---|---|---|
eg1: | order | id | 29437595 | name |
eg2: | equip | id | 390472345 | type |
eg3: | news | id | 202004150 | title |
下面咱们来学习第二个数据类型hash
对象类数据的存储若是具备较频繁的更新需求操做会显得笨重!
在正式学习以前,咱们先来看一个关于数据存储的困惑:
好比说前面咱们用以上形式存了数据,若是咱们用单条去存的话,它存的条数会不少。
但若是咱们用json格式,它存一条数据就够了。
问题来了,假如说如今粉丝数量发生变化了,你要把整个值都改了。
可是用单条存的话就不存在这个问题,你只须要改其中一个就好了。
这个时候咱们就想,有没有一种新的存储结构,能帮咱们解决这个问题呢。
咱们一起来分析一下:
如上图所示:单条的话是对应的数据在后面放着。
仔细观察:咱们看左边是否是长得都如出一辙啊,都是对应的表名、ID等的一系列的东西。
咱们能够将右边红框中的这个区域给他封起来。
那若是要是这样的形式的话,以下图,咱们把它一合并,并把右边的东西给他变成这个格式,这不就好了吗?
这个图其实你们并不陌生:
第一,你前面学过一个东西叫hashmap不就这格式吗?
第二,redis自身不也是这格式吗?那是什么意思呢?
注意,这就是咱们要讲的第二种格式,hash。
在右边对应的值,咱们就存具体的值,那左边儿这就是咱们的key。
问题来了,那中间的这一块叫什么呢?这个东西咱们给他起个名儿,叫作field字段。
那么右边儿总体这块儿空间咱们就称为hash,也就是说hash是存了一个key value的存储空间。
总结:
hash的结构是:key:{field:value,field2:value2}
在redis中的hash的值,能够理解为是一个对象
新的存储需求:对一系列存储的数据进行编组,方便管理,典型应用存储对象信息
须要的存储结构:一个存储空间保存多个键值对数据
hash类型:底层使用哈希表结构实现数据存储
如上图所示,这种结构叫作hash,左边一个key,对右边一个存储空间。这里要明确一点,右边这块儿存储空间叫hash,也就是说hash是指的一个数据类型,他指的不是一个数据,是这里边的一堆数据,那么它底层呢,是用hash表的结构来实现的。
值得注意的是:
若是field数量较少,存储结构优化为类数组结构
若是field数量较多,存储结构使用HashMap结构
添加/修改数据
hset key field value #h:hash
获取数据
hget key field hgetall key
删除数据
hdel key field1 [field2] #能够删除多个field,空格分隔便可
设置field的值,若是该field存在则不作任何操做
hsetnx key field value
添加/修改多个数据
hmset key field1 value1 field2 value2 …
获取多个数据
hmget key field1 field2 …
获取哈希表中字段的数量
hlen key
获取哈希表中是否存在指定的字段
hexists key field
演示:hash的key是user:123
这里删除了一个还剩下一个,又增长3个,按理说长度应该是4的。这里缺显示的是5,这个多是老师对视频截图以后出的问题·
在看完hash的基本操做后,咱们再来看他的拓展操做,他的拓展操做相对比较简单:
获取哈希表中全部的字段名或字段值
hkeys key hvals key
设置指定字段的数值数据增长指定范围的值
hincrby key field increment hincrbyfloat key field increment
演示:
(1)hash类型中value只能存储字符串,不容许存储其余数据类型,不存在嵌套现象。若是数据未获取到,对应的值为(nil)
(2)每一个 hash 能够存储 (2的32次方 - 1) 个键值对
(3)hash类型十分贴近对象的数据存储形式,而且能够灵活添加删除对象属性。但hash设计初衷不是为了存储大量对象而设计 的,切记不可滥用,更不能够将hash做为对象列表使用
(4)hgetall 操做能够获取所有属性,若是内部field过多,遍历总体数据效率就很会低,有可能成为数据访问瓶颈
双11活动日,销售手机充值卡的商家对移动、联通、电信的30元、50元、100元商品推出抢购活动,每种商品抢购上限1000 张。
也就是商家有了,商品有了,数量有了。最终咱们的用户买东西就是在改变这个数量。那你说这个结构应该怎么存呢?对应的商家的ID做为key,而后这些充值卡的ID做为field,最后这些数量做为value。而咱们所谓的操做是其实就是increa这个操做,只不过你传负值就好了。看一看对应的解决方案。
以商家id做为key
将参与抢购的商品id做为field
将参与抢购的商品数量做为对应的value
抢购时使用降值的方式控制产品数量
注意:实际业务中还有超卖等实际问题,这里不作讨论
演示:
前面咱们存数据的时候呢,单个数据也能存,多个数据也能存
可是这里面有一个问题,咱们存多个数据用hash的时候它是没有顺序的
咱们平时操做,实际上数据不少状况下都是有顺序的,那有没有一种可以用来存储带有顺序的这种数据模型呢
list就专门来干这事儿
数据存储需求:存储多个数据,并对数据进入存储空间的顺序进行区分
须要的存储结构:一个存储空间保存多个数据,且经过数据能够体现进入顺序
list类型:保存多个数据,底层使用双向链表存储结构实现
先来经过一张图,回忆一下顺序表、链表、双向链表。
下图为将cnpc插入前的图示
下图为将cnpn插入后的图示
顺序表:想要插入cnpc,须要将后边的元素都向后移动,比较麻烦
链表:想要插入cnpc,须要将头指针和huawei的链条断开,而后从新链接
链表的形式,插入挺高效,就是查询比较慢,若是要查询alibaba,就须要从头查到尾
双向链表:它的链条是双向的
想要插入cnpc,也是须要断开头指针和华为的链条,从新链接,因此插入也高效
可是查询也是高效的,查询能够双向查询
虽然查询alibaba也是从头开始查询,可是若是这时再查询tencent,就能够反向直接查询到
list对应的存储结构是什么呢?里边存的这个东西是个列表,他有一个对应的名称。
就是key存一个list的这样结构。对应的基本操做,你实际上是能够想到的。
来看一下,由于它是双向的,因此他左边右边都能操做,它对应的操做结构两边都能进数据。这就是链表的一个存储结构。
往外拿数据的时候怎么拿呢?一般是从一端拿,固然另外一端也能拿。
若是两端都能拿的话,这就是个双端队列,两边儿都能操做。若是只能从一端进一端出,这个模型我们前面了解过,叫作栈。
lpush,rpush,lrange
队列(排队),先进先出
栈堆:摞(摞东西),先进后出
最后看一下他的基本操做
添加/修改数据
lpush key value1 [value2] …… rpush key value1 [value2] …… #l:left #r:right
获取数据:由于列表存储的数据有顺序,因此能够经过索引来获取
lrange key start stop # range:范围,查询某个范围的数据。stop若是写-1,意味着直接查询全部 lindex key index #index:索引,查询某个索引的数据 llen key
获取并移除数据:删除以后会返回被删除的数据
lpop key #左侧删除末尾数据(删除最左侧的数据) rpop key #右侧删除末尾数据(删除最右侧的数据)
演示:
分析:
移除指定数据:不返回删除的数据
lrem key count value #rem remove移除 #count 能够删除多个,若是list中存储了俩a,就能够是 lrem list1 2 a
规定时间内获取并移除数据:规定时间内取数据,能取到就返回,取不到就返回nil
blpop key1 [key2] timeout brpop key1 [key2] timeout brpoplpush source destination timeout #b:block:阻塞,块
演示:
(1)list中保存的数据都是string类型的,数据总容量是有限的,最多 (2的32次方- 1) 个元素(4294967295)。
(2)list具备索引的概念,可是操做数据时一般以队列的形式进行入队出队操做,或以栈的形式进行入栈出栈操做
(3)获取所有数据操做结束索引设置为-1
(4)list能够对数据进行分页操做,一般第一页的信息来自于list,第2页及更多的信息经过数据库的形式加载
企业运营过程当中,系统将产生出大量的运营数据,如何保障多台服务器操做日志的统一顺序输出?
假如如今你有多台服务器,每一台服务器都会产生它的日志,假设你是一个运维人员,你想看它的操做日志,你怎么看呢?打开A机器的日志看一看,打开B机器的日志再看一看吗?这样的话你会可能会疯掉的!由于左边看的有可能它的时间是11:01,右边11:02,而后再看左边11:03,它们自己是连续的,可是你在看的时候就分红四个文件了,这个时候你看起来就会很麻烦。能不能把他们合并呢?答案是能够的!怎么作呢?创建起redis服务器。当他们须要记日志的时候,记在哪儿,所有发给redis。等到你想看的时候,经过服务器访问redis获取日志。而后获得之后,就会获得一个完整的日志信息。那么这里面就能够获取到完整的日志了,依靠什么来实现呢?就依靠咱们的list的模型的顺序来实现。进来一组数据就往里加,谁先进来谁先加进去,它是有必定的顺序的。
依赖list的数据具备顺序的特征对信息进行管理
使用队列模型解决多路信息汇总合并的问题
使用栈模型解决最新消息的问题
演示:
先进先出,就用rpush放入,lrange取出便可
新的存储需求:存储大量的数据,在查询方面提供更高的效率
须要的存储结构:可以保存大量的数据,高效的内部存储机制,便于查询
set类型:与hash存储结构彻底相同,仅存储field,不存储值(存储的值都是nil),而且field是不容许重复的
经过这个名称,你们也基本上可以认识到和咱们Java中的set彻底同样。咱们如今要存储大量的数据,而且要求提升它的查询效率。用list这种链表形式,它的查询效率是不高的,那怎么办呢?这时候咱们就想,有没有高效的存储机制。其实前面咱讲Java的时候说过hash表的结构就很是的好,可是这里边咱们已经有hash了,他作了这么一个设定,干吗呢,他把hash的存储空间给改一下,右边你原来存数据改掉,所有存空,那你说数据放哪儿了?放到原来的filed的位置,也就在这里边存真正的值,那么这个模型就是咱们的set 模型。
set类型:与hash存储结构彻底相同,仅存储field,不存储值(存储的值都是nil),而且field是不容许重复的
看一下它的整个结构:
添加数据
sadd key member1 [member2]
获取所有数据
smembers key #s:set #members:成员,set集合中每个元素叫作成员
删除数据
srem key member1 [member2]
获取集合数据总量
scard key
判断集合中是否包含指定数据
sismember key member #ismember:是成员么,是集合中的成员么
随机获取集合中指定数量的数据 (了解)
srandmember key [count] #rand:random随即
随机获取集中的某个数据并将该数据移除集合
spop key [count]
演示
求两个集合的交、并、差集
sinter key1 [key2 …] #intersection : 交叉,交集 sunion key1 [key2 …] # union :联合 sdiff key1 [key2 …] # difference:不一样,差异
求两个集合的交、并、差集并存储到指定集合中
sinterstore destination key1 [key2 …] sunionstore destination key1 [key2 …] sdiffstore destination key1 [key2 …] # store:存储 # destination:目的
将指定数据从原始集合中移动到目标集合中
smove source destination member #move:移动 #source:源
经过下面一张图回忆一下交、并、差
演示:
set 类型不容许数据重复,若是添加的数据在 set 中已经存在,将只保留一份。
set 虽然与hash的存储结构相同,可是没法启用hash中存储值的空间。
(1)黑名单
资讯类信息类网站追求高访问量,可是因为其信息的价值,每每容易被不法分子利用,经过爬虫技术, 快速获取信息,个别特种行业网站信息经过爬虫获取分析后,能够转换成商业机密进行出售。例如第三方火 车票、机票、酒店刷票代购软件,电商刷评论、刷好评。
同时爬虫带来的伪流量也会给经营者带来错觉,产生错误的决策,有效避免网站被爬虫反复爬取成为每一个网站都要考虑的基本问题。在基于技术层面区分出爬虫用户后,须要将此类用户进行有效的屏蔽,这就是黑名单的典型应用。
ps:不是说爬虫必定作摧毁性的工做,有些小型网站须要爬虫为其带来一些流量。
(2)白名单
对于安全性更高的应用访问,仅仅靠黑名单是不能解决安全问题的,此时须要设定可访问的用户群体, 依赖白名单作更为苛刻的访问验证。
基于经营战略设定问题用户发现、鉴别规则
周期性更新知足规则的用户黑名单,加入set集合
用户行为信息达到后与黑名单进行比对,确认行为去向
黑名单过滤IP地址:应用于开放游客访问权限的信息源
黑名单过滤设备信息:应用于限定访问设备的信息源
黑名单过滤用户:应用于基于访问权限的信息源
使用微信的过程当中,当微信接收消息后,会默认将最近接收的消息置顶,当多个好友及关注的订阅号同时发送消息时,该排序会不停的进行交替。
同时还能够将重要的会话设置为置顶。
一旦用户离线后,再次打开微信时,消息该按照什么样的顺序显示。
咱们分析一下:
100这台手机表明你。而200、300、400这三台表明你好友的手机。
在这里有一些东西须要交代一下,由于咱们每一个人的都会对本身的微信中的一些比较重要的人设置会话置顶,将他的那条对话放在最上面。咱们假定这我的有两个会话置顶的好友,分别是400和500,而这里边就包含400.
下面呢,咱们就来发这个消息,第一个发消息的是300,他发了个消息给100。发完之后,这个东西应该怎么存储呢?在这里面必定要分开,记录置顶的这些人的会话,对应的会话显示顺序和非置顶的必定要分两。
这里面咱们建立两个模型,一个是普通的,一个是置顶的,而上面的这个置顶的用户呢,咱们用set来存储,由于不重复。而下面这些由于有顺序,很容易想到用list去存储,否则你怎么表达顺序呢?
上图分析:
set指定存储的是置顶好友:400和500
置顶好友和普通好友的聊天消息,经过list保存
那当300发给消息给100之后,这个时候咱们先断定你在置顶人群中吗?不在,那好,300的消息对应的顺序就应该放在普通的列表里边。而在这里边,咱们把300加进去。第一个数据也就是如今300。
上图分析:
接下来400,发了个消息。判断一下,他是须要置顶的,因此400将进入list的置顶里边放着。目前尚未特殊的地方。
上图分析:
再来200发消息了,和刚才的断定方法同样,先看在不在置顶里,不在的话进普通,而后在普通里边把200加入就好了,OK,到这里目前尚未顺序变化。(右进右出)
接下来200又发消息过来,同一我的给你连发了两条,那这个时候200的消息到达之后,先判断是否在置顶范围,不在,接下来他要放在list普通中,这里你要注意一点,由于这里边已经有200,因此进来之后先干一件事儿,把200杀掉,没有200,而后再把200加进来,那你想一下,如今这个位置顺序是什么呢?就是新的都在右边,对不对?
还记得咱们说list模型,若是是一个双端队列,它是能够两头进两头出。固然咱们双端从一头进一头出,这就是栈模型,如今我们运用的就是list模型中的栈模型(假设:右进右出)。
上图分析:
如今300发消息,先断定他在不在,不在,用普通的队列,接下来按照刚才的操做,无论你里边原来有没有300,我先把300杀掉,没了,200天然就填到300的位置了,他如今是list里面惟一一个,而后让300进来,注意是从右侧进来的,那么如今300就是最新的。
上图分析:
那么到这里呢,咱们让100来读取消息。你以为这个消息顺序应该是什么样的?首先置顶的400有一个,他跑在最上面,而后list普通若是出来的话,300是最新的消息,而200在他后面的。用这种形式,咱们就能够作出来他的消息顺序来。
看一下最终的解决方案:
依赖list的数据具备顺序的特征对消息进行管理,将list结构做为栈使用(一端进一段出)
置顶与普通会话分别建立独立的list分别管理
当某个list中接收到用户消息后,将消息发送方的id从list的一侧加入list(上边的案例是右侧)
多个相同id发出的消息反复入栈会出现问题,在入栈以前不管是否具备当前id对应的消息,先删除对应id
推送消息时先推送置顶会话list,再推送普通会话list,推送完成的list清除全部数据 消息的数量,也就是微信用户对话数量采用计数器的思想另行记录,伴随list操做同步更新
演示
最终结果是200,300,400,跟我们分析的正好相反,由于这里是左侧添加的
想要实现跟分析同样的结果,使用rpush便可
总结一下,在整个数据类型的部分,咱们主要介绍了哪些内容:
首先咱们了解了一下数据类型,接下来针对着咱们要学习的数据类型,进行逐一讲解了string、hash、list、set等,最后经过一个案例总结了一下前面的数据类型的使用场景。
在这部分中呢,咱们家学习两个知识,第一个是key的经常使用指令,第二个是数据库的经常使用指令
和前面咱们学数据类型作一下区分,前面你学的那些指令呢,都是针对某一个数据类型操做的,如今学的都是对全部的操做的
来看一下,咱们在学习Key的操做的时候,咱们先想一下的操做咱们应该学哪些东西
key是一个字符串,经过key获取redis中保存的数据
对于key自身状态的相关操做,例如:删除,断定存在,获取类型等
对于key有效性控制相关操做,例如:有效期设定,断定是否有效,有效状态的切换等
对于key快速查询操做,例如:按指定策略查询key
删除指定key
del key
获取key是否存在
exists key
获取key的类型
type key
演示
排序
sort
更名
rename key newkey renamenx key newkey
演示
为指定key设置有效期
expire key seconds *** pexpire key milliseconds expireat key timestamp pexpireat key milliseconds-timestamp
获取key的有效时间
ttl key # ttl:time to live:存活时间(过时时间) pttl key
切换key从时效性转换为永久性
persist key #persist 继续存在,坚持
演示
查询key
keys pattern
查询模式规则
*匹配任意数量的任意符号 ? 配合一个任意符号 [] 匹配一个指定符号
keys * keys 查询全部 it* keys 查询全部以it开头 *heima 查询全部以heima结尾 keys ??heima 查询全部前面两个字符任意,后面以heima结尾 查询全部以 keys user:? user:开头,最后一个字符任意 keys u[st]er:1 查询全部以u开头,以er:1结尾,中间包含一个字母,s或t
演示
在这个地方咱们来说一下数据库的经常使用指令,在讲这个东西以前,咱们先思考一个问题:
假如说大家十我的同时操做redis,会不会出现key名字命名冲突的问题。
必定会,为何?由于你的key是由程序而定义的。你想写什么写什么,那在使用的过程当中你们都在不停的加,迟早有一天他会冲突的。
redis在使用过程当中,伴随着操做数据量的增长,会出现大量的数据以及对应的key。
那这个问题咱们要不要解决?要!怎么解决呢?咱们最好把数据进行一个分类,除了命名规范咱们作统一之外,若是还能把它分开,这样是否是冲突的机率就会小一些了,这就是我们下面要说的解决方案!
redis为每一个服务提供有16个数据库,编号从0到15
每一个数据库之间的数据相互独立
在对应的数据库中划出一块区域,说他就是几,你就用几那块,同时,其余的这些均可以进行定义,一共是16个,这里边须要注意一点,他们这16个共用redis的内存。没有说谁大谁小,也就是说数字只是表明了一起区域,区域具体多大未知。这是数据库的一个分区的一个策略!
切换数据库 ***
select index
其余操做
ping
演示
数据移动
move key db
数据总量
dbsize
数据清除
flushdb # 清除当前库的数据 flushall # 清除全部库的数据 #flush:冲洗,冲掉
演示
在学习完redis后,咱们如今就要用Java来链接redis了,也就是咱们的这一章要学的Jedis了
在这个部分,咱们主要讲解如下3个内容:
HelloWorld(Jedis版)
Jedis简易工具类开发z
可视化客户端
对于咱们如今的数据来讲,它是在咱们的redis中,而最终咱们是要作程序。那么程序就要和咱们的redis进行链接。
干什么事情呢?两件事:程序中有数据的时候,咱们要把这些数据所有交给redis管理。
同时,redis中的数据还能取出来,回到咱们的应用程序中。
那在这个过程当中,在Java与redis之间打交道的这个东西就叫作Jedis.
简单说,Jedis就是提供了Java与redis的链接服务的,里边有各类各样的API接口,你能够去调用它。
除了Jedis外,还有没有其余的这种链接服务呢?其实还有不少,了解一下:
Java语言链接redis服务 Jedis(SpringData、Redis 、 Lettuce)
其它语言:C 、C++ 、C# 、Erlang、Lua 、Objective-C 、Perl 、PHP 、Python 、Ruby 、Scala
(1)jar包导入:资料中“jar/jedis-3.1.0.jar”
下载地址:https://mvnrepository.com/artifact/redis.clients/jedis
(2)客户端链接redis
链接redis
Jedis jedis = new Jedis("localhost", 6379);
操做redis
jedis.set("name", "itheima"); jedis.get("name");
关闭redis链接
jedis.close();
API文档
http://xetorthio.github.io/jedis/
建立:com.itheima.JedisTest
public class JedisTest { public static void main(String[] args) { //1.获取链接对象 Jedis jedis = new Jedis("127.0.0.1",6379); //2.执行操做 jedis.set("age","39"); // jedis提供的方法与redis命令基本一致 String hello = jedis.get("hello"); System.out.println(hello); jedis.lpush("list1","a","b","c","d"); List<String> list1 = jedis.lrange("list1", 0, -1); for (String s:list1 ) { System.out.println(s); } jedis.sadd("set1","abc","abc","def","poi","cba"); Long len = jedis.scard("set1"); System.out.println(len); //3.关闭链接 jedis.close(); } }
报错:
修改配置文件bind为虚拟机真实ip
修改程序
Jedis jedis = new Jedis("192.168.40.130",6379); //ip须要为redis服务所在的真实ip
注意:若是配置文件的ip修改过,而且将redis重启,而后代码中也改了ip以后,仍是链接不上那多是linux的防火墙问题:
#关闭防火墙 systemctl stop firewalld #重启redis服务
前面咱们作的程序仍是有点儿小问题,就是咱们的Jedis对象的管理是咱们本身建立的,真实企业开发中是不可能让你去new一个的,那接下来我们就要作一个工具类,简单来讲,就是作一个建立Jedis的这样的一个工具。
JedisPool:Jedis提供的链接池技术
poolConfig:链接池配置对象
host:redis服务地址
port:redis服务端口号
JedisPool的构造器以下: (源码)
public JedisPool(GenericObjectPoolConfig poolConfig, String host, int port) { this(poolConfig, host, port, 2000, (String)null, 0, (String)null); }
建立jedis的配置文件:jedis.properties
jedis.host=192.168.40.130 jedis.port=6379 jedis.maxTotal=50 #控制一个pool可分配多少个jedis实例 jedis.maxIdle=10 #控制一个pool最多有多少个状态为idle(空闲的)的jedis实例 # 就是说若是没人用jedis,那么这个池子里就只有10个jedis实例 # 若是用的人很是多,可是不会超过50个
建立JedisUtils:com.itheima.util.JedisUtils,使用静态代码块初始化资源
public class JedisUtils { private static int maxTotal; private static int maxIdel; private static String host; private static int port; private static JedisPoolConfig jpc; private static JedisPool jp; static { ResourceBundle bundle = ResourceBundle.getBundle("jedis"); maxTotal = Integer.parseInt(bundle.getString("jedis.maxTotal")); maxIdel = Integer.parseInt(bundle.getString("jedis.maxIdel")); host = bundle.getString("jedis.host"); port = Integer.parseInt(bundle.getString("jedis.port")); //Jedis链接池配置 jpc = new JedisPoolConfig(); jpc.setMaxTotal(maxTotal); jpc.setMaxIdle(maxIdel); jp = new JedisPool(jpc,host,port); } }
对外访问接口,提供jedis链接对象,链接从链接池获取,在JedisUtils中添加一个获取jedis的方法:getJedis
public static Jedis getJedis(){ Jedis jedis = jp.getResource(); return jedis; }
测试类:使用链接池
package com.itheima; import com.itheima.util.JedisUtils; import redis.clients.jedis.Jedis; public class JedisTest { public static void main(String[] args) { //1.获取链接对象 // Jedis jedis = new Jedis("192.168.40.130",6379); Jedis jedis = JedisUtils.getJedis(); //2.执行操做 // jedis.set("age","39"); // String hello = jedis.get("hello"); // System.out.println(hello); // jedis.lpush("list1","a","b","c","d"); // List<String> list1 = jedis.lrange("list1", 0, -1); // for (String s:list1 ) { // System.out.println(s); // } jedis.sadd("set1","abc","abc","def","poi","cba"); Long len = jedis.scard("set1"); System.out.println(len); //3.关闭链接 jedis.close(); } }
4.3.1 Redis Desktop Manager
下面呢,进入到持久化的学习.这部份内容理解的东西多,操做的东西少。在这个部分,咱们将讲解四个东西:
持久化简介
RDB
AOF
RDB与AOF区别
不知道你们有没有碰见过,就是正工做的时候停电了,若是你用的是笔记本电脑还好,你有电池
但若是你用的是台式机呢,那恐怕就比较灾难了
假如你如今正在写一个比较重要的文档,若是你要使用的是word,这种办公自动化软件的话,他一旦遇到停电,其实你不用担忧,由于它会给你生成一些其余的文件
其实他们都在作一件事儿,帮你自动恢复,有了这个文件,你前面的东西就再也不丢了。那什么是自动恢复呢?你要先了解他的整个过程。
咱们说自动恢复,其实基于的一个前提就是他提早把你的数据给存起来了。你日常操做的全部信息都是在内存中的,而咱们真正的信息是保存在硬盘中的,内存中的信息断电之后就消失了,硬盘中的信息断电之后还能够保留下来!
咱们将文件由内存中保存到硬盘中的这个过程,咱们叫作数据保存,也就叫作持久化。
可是把它保存下来不是你的目的,最终你还要把它再读取出来,它加载到内存中这个过程,咱们叫作数据恢复,
这就是咱们所说的word为何断电之后还可以给你保留文件,由于它执行了一个自动备份的过程,也就是经过自动的形式,把你的数据存储起来,那么有了这种形式之后,咱们的数据就能够由内存到硬盘上实现保存。
(1)什么是持久化
利用永久性存储介质将数据进行保存,在特定的时间将保存的数据进行恢复的工做机制称为持久化 。
持久化用于防止数据的意外丢失,确保数据安全性。
(2)持久化过程保存什么?
咱们知道一点,计算机中的数据所有都是二进制,若是如今我要你给我保存一组数据的话,你有什么样的方式呢,其实最简单的就是如今长什么样,我就记下来就好了,那么这种是记录纯粹的数据,也叫作快照存储,也就是它保存的是某一时刻的数据状态。
还有一种形式,它不记录你的数据,它记录你全部的操做过程,好比说你们用idea的时候,有没有遇到过写错了ctrl+z撤销,而后ctrl+y还能恢复,这个地方它也是在记录,可是记录的是你全部的操做过程,那我想问一下,操做过程,我都给你留下来了,你说数据还会丢吗?确定不会丢,由于你全部的操做过程我都保存了。这种保存操做过程的存储,用专业术语来讲能够说是日志,这是两种不一样的保存数据的形式啊。
总结一下:
第一种:将当前数据状态进行保存,快照形式,存储数据结果,存储格式简单,关注点在数据。(Ctrl+c加Ctrl+v)
第二种:将数据的操做过程进行保存,日志形式,存储操做过程,存储格式复杂,关注点在数据的操做过程。
RDB:Redis DataBase
手动执行一次保存操做
save
save指令相关配置
设置本地数据库文件名,默认值为 dump.rdb,一般设置为dump-端口号.rdb
dbfilename filename
设置存储.rdb文件的路径,一般设置成存储空间较大的目录中,目录名称data
dir path
设置存储至本地数据库时是否压缩数据,默认yes,设置为no,节省 CPU 运行时间,但存储文件变大
rdbcompression yes|no
设置读写文件过程是否进行RDB格式校验,默认yes,设置为no,节约读写10%时间消耗,单存在数据损坏的风险
rdbchecksum yes|no
演示
增长dbfilename配置(屏蔽logfile)
从新启动redis
查看data目录的文件
而后在启动redis服务的窗口,能够看到db saved on disk
关闭服务
启动服务:发现从硬盘读取了数据
查询
以前咱们退出redis,从新进入,以前的数据就丢失了,如今没有丢失,就是由于在退出以前save保存了
save指令工做原理
须要注意一个问题,来看一下,如今有四个客户端各自要执行一个指令,把这些指令发送到redis服务器后,他们执行有一个前后顺序问题,假定就是按照1234的顺序放过去的话,那会是什么样的?
记得redis是个单线程的工做模式,它会建立一个任务队列,全部的命令都会进到这个队列里边,在这儿排队执行,执行完一个消失一个,当全部的命令都执行完了,OK,结果达到了。
可是若是如今咱们执行的时候save指令保存的数据量很大会是什么现象呢?
他会很是耗时,以致于影响到它在执行的时候,后面的指令都要等,因此说这种模式是不友好的,这是save指令对应的一个问题,当cpu执行的时候会阻塞redis服务器,直到他执行完毕,因此说咱们不建议你们在线上环境用save指令。
以前咱们讲到了当save指令的数据量过大时,单线程执行方式形成效率太低,那应该如何处理?
此时咱们能够使用:bgsave指令,bg实际上是background的意思,后台执行的意思
手动启动后台保存操做,但不是当即执行
bgsave
bgsave指令相关配置
后台存储过程当中若是出现错误现象,是否中止保存操做,默认yes
stop-writes-on-bgsave-error yes|no
演示
bgsave指令工做原理
当执行bgsave的时候,客户端发出bgsave指令给到redis服务器。
注意,这个时候服务器立刻回一个结果告诉客户端后台已经开始了,与此同时它会建立一个子进程,使用Linux的fork(分叉)函数建立一个子进程,让这个子进程去执行save相关的操做
此时咱们能够想一下,咱们主进程一直在处理指令,而子进程在执行后台的保存,它会不会干扰到主进程的执行吗?
答案是不会,因此说他才是主流方案。
子进程开始执行以后,它就会建立RDB文件把它存起来,操做完之后他会把这个结果返回,也就是说bgsave的过程分红两个过程,
第一个是服务端拿到指令直接告诉客户端开始执行了;
另一个过程是一个子进程在完成后台的保存操做,操做完之后回一个消息。
设置自动持久化的条件,知足限定时间范围内key的变化数量达到指定数量即进行持久化
save second changes
参数
second:监控时间范围
changes:监控key的变化量
范例:
save 900 1 #900s以内只要有1个key发生改变,就会保存 save 300 10 save 60 10000
演示:
删除数据库文件
配置
从新启动redis
链接redis,添加数据
观察rdb文件
若是再设置两个,那么rdb文件就会被修改(文件的修改时间会变)
save配置工做原理
方式 | SAVE指令 | BGSAVE指令 |
---|---|---|
读写 | 同步 | 异步 |
阻塞客户端指令 | 是 | 否 |
额外内存消耗 | 否 | 是 |
启动新进程 | 否 | 是 |
RDB特殊启动形式
以前已经介绍了三种,还有三种了解一下便可:
服务器运行过程当中重启
debug reload
关闭服务器时指定保存数据
shutdown save
全量复制(在主从复制中详细讲解)
RDB优势:
RDB是一个紧凑压缩的二进制文件,存储效率较高
RDB内部存储的是redis在某个时间点的数据快照,很是适合用于数据备份,全量复制等场景
RDB恢复数据的速度要比AOF快不少
应用:服务器中每X小时执行bgsave备份,并将RDB文件拷贝到远程机器中,用于灾难恢复。
RDB缺点
RDB方式不管是执行指令仍是利用配置,没法作到实时持久化,具备较大的可能性丢失数据
bgsave指令每次运行要执行fork操做建立子进程,要牺牲掉一些性能
Redis的众多版本中未进行RDB文件格式的版本统一,有可能出现各版本服务之间数据格式没法兼容现象
为何要有AOF,这得从RDB的存储的弊端提及:
存储数据量较大,效率较低,基于快照思想,每次读写都是所有数据,当数据量巨大时,效率很是低
大数据量下的IO性能较低
基于fork建立子进程,内存产生额外消耗
宕机带来的数据丢失风险
那解决的思路是什么呢?
不写全数据,仅记录部分数据
下降区分数据是否改变的难度,改记录数据为记录操做过程
对全部操做均进行记录,排除丢失数据的风险
AOF(append only file)持久化:以独立日志的方式记录每次写命令,重启时再从新执行AOF文件中命令 达到恢复数据的目的。与RDB相比能够简单理解为由记录数据改成记录数据产生的变化
AOF的主要做用是解决了数据持久化的实时性,目前已是Redis持久化的主流方式
AOF写数据过程
启动AOF相关配置
开启AOF持久化功能,默认no,即不开启状态
appendonly yes|no
AOF持久化文件名,默认文件名为appendonly.aof,建议配置为appendonly-端口号.aof
appendfilename filename
AOF持久化文件保存路径,与RDB持久化文件保持一致便可
dir
AOF写数据策略,默认为everysec
appendfsync always|everysec|no
AOF写数据三种策略(appendfsync)
always(每次):每次写入操做均同步到AOF文件中数据零偏差,性能较低,不建议使用。
everysec(每秒):每秒将缓冲区中的指令同步到AOF文件中,在系统忽然宕机的状况下丢失1秒内的数据 数据准确性较高,性能较高,建议使用,也是默认配置
no(系统控制):由操做系统控制每次同步到AOF文件的周期,总体过程不可控
演示:
修改配置(而且清除data目录中的全部文件)
链接
查看日志文件,发现有了(虽然只是登录了,没执行任何命令,可是1s已通过去了,这个文件就会生成,只不过里边没有记录东西)
执行指令
查看日志文件
select 0 , 是默认执行的一个指令,默认操做的是0号库
*3表明这个指令分为3部分
再次执行指令
再次查看日志
执行其余命令,可是若是执行的命令,没有影响数据,是不会记录的
查看日志,发现只有一次del name4
中止重启
从新查询数据,发现是有的,加载日志文件成功
场景:AOF写数据遇到的问题,若是连续执行以下指令该如何处理
什么叫AOF重写?
随着命令不断写入AOF,文件会愈来愈大,为了解决这个问题,Redis引入了AOF重写机制压缩文件体积。
AOF文件重写是将Redis进程内的数据转化为写命令同步到新AOF文件的过程。
简单说就是将对同一个数据的若干个条命令执行结果 转化成 最终结果数据对应的指令进行记录。
AOF重写做用
下降磁盘占用量,提升磁盘利用率
提升持久化效率,下降持久化写时间,提升IO性能
下降数据恢复用时,提升数据恢复效率
AOF重写规则
进程内具备时效性的数据,而且数据已超时将再也不写入文件
非写入类的无效指令将被忽略,只保留最终数据的写入命令
如del key一、 hdel key二、srem key三、set key4 1十一、set key4 222等
如select指令虽然不更改数据,可是更改了数据的存储位置,此类命令一样须要记录
对同一数据的多条写命令合并为一条命令
如lpushlist1 a、lpush list1 b、lpush list1 c能够转化为:lpush list1 a b c。
为防止数据量过大形成客户端缓冲区溢出,对list、set、hash、zset等类型,每条指令最多写入64个元素
AOF重写方式
手动重写
bgrewriteaof # 这是一个redis指令,不是配置
演示:
中止服务和客户端,而后清除data内的全部文件
执行指令:设置几个数据,而后手动重写
接下来再设置数据
查看日志文件:发现重写以前的都看不懂了,重写以后的仍是以前的
再次设置多个相同的指令
查看日志:发现仍是以前的,可是这里会有屡次记录(咱们须要经过重写简化)
收手动重写
手动重写:发现已经重写完毕
手动重写原理分析:
自动重写
自动重写触发条件设置
auto-aof-rewrite-min-size size # 达到指定大小就自动重写,size能够指定多少兆:2MB 、 4MB auto-aof-rewrite-percentage percent #按照存储占用总体的百分比自动重写,percent指定10%,达到10%就能够自动重写
自动重写触发比对参数( 运行指令info Persistence获取具体信息 )
aof_current_size aof_base_size
自动重写触发条件公式:
aof_current_size:aof当前尺寸,若是大于auto-aof-rewrite-min-siz:aof重写的最小尺寸,就重写
aof_base_size:aof的基础尺寸,就是目前已经备份的总大小
aof_current_size:就是当前即将要备份的大小
上边的公式写反了:应该是aof_current_size - aof_base_size
持久化方式 | RDB | AOF |
---|---|---|
占用存储空间 | 小(数据级:压缩) | 大(指令级:重写) |
存储速度 | 慢 | 快 |
恢复速度 | 快 | 慢 |
数据安全性 | 会丢失数据 | 依据策略决定 |
资源消耗 | 高/重量级 | 低/轻量级 |
启动优先级 | 低 | 高 |
RDB与AOF的选择之惑
对数据很是敏感,建议使用默认的AOF持久化方案
AOF持久化策略使用everysecond,每秒钟fsync一次。该策略redis仍能够保持很好的处理性能,当出 现问题时,最多丢失0-1秒内的数据。
注意:因为AOF文件存储体积较大,且恢复速度较慢
数据呈现阶段有效性,建议使用RDB持久化方案
数据能够良好的作到阶段内无丢失(该阶段是开发者或运维人员手工维护的),且恢复速度较快,阶段 点数据恢复一般采用RDB方案
注意:利用RDB实现紧凑的数据持久化会使Redis降的很低,慎重总结:
综合比对
RDB与AOF的选择其实是在作一种权衡,每种都有利有弊
如不能承受数分钟之内的数据丢失,对业务数据很是敏感,选用AOF
如能承受数分钟之内的数据丢失,且追求大数据集的恢复速度,选用RDB
灾难恢复选用RDB