1、Redis概述
1.1、初识NoSQL
概述:NoSQL指"不仅仅是SQL",泛指非关系型的数据库。随着互联网web2.0网站的兴起,传统的关系数据库在应对超大规模和高并发类型纯动态网站显得力不从心,暴露了很多难以克服的问题。
结构化数据与非结构化数据的区别:
①、结构化数据指的是由二维表结构来逻辑表达和实现的数据,严格遵循数据格式与长度规范,也称作为行数据。
②、非结构化数据指的是数据结构不规则或不完整,没有任何预定义的数据模型,不方便用二维逻辑表来表现的数据,例如办公文档(Word)、文本、图片、HTML、各类报表、视频音频等。
1.2、NoSQL的分类
1.2.1、KV型NoSQL
概述:KV型NoSql顾名思义就是以键值对形式存储的非关系型数据库,是最简单且最容易理解的NoSql,以Redis为代表。
特点:①、数据基于内存,读写效率高;
②、KV型数据,时间复杂度为O(1),查询速度快。
注意:KV型NoSql最大的优点就是高性能,利用Redis自带的BenchMark做基准测试,TPS可达到10万的级别,性能非常强劲。
1.2.2、列式NoSQL
概述:列式NoSql,大数据时代最具代表性的技术之一,常用于存储实时产生的数据(例如打车软件的定位信息),以HBase为代表。
注意:①、查询时只有指定的列会被读取,不会读取所有列;
②、列数据被组织到一起,一次磁盘IO可以将一列数据一次性读取到内存中。
1.2.3、文档型NoSQL
概述:文档型NoSql指的是将半结构化数据存储为文档的一种NoSql,文档型NoSql通常以JSON或者XML格式存储数据,常用于存储文章,博客等内容较多的信息,以MongoDB为代表。
注意:关系型数据库是按部就班地每个字段一列存,在MongDB里面就是一个JSON字符串存储。
1.2.4、搜索型NoSQL
概述:传统关系型数据库主要通过索引来实现快速查询,但是在全文搜索的场景下,索引是无能为力的,like查询一是无法满足所有模糊匹配需求,二是使用限制太大且使用不当容易造成慢查询,搜索型NoSql的诞生正是为了解决关系型数据库全文搜索能力较弱的问题,以ElasticSearch作为代表产品。
1.3、关系型与非关系型数据库的优缺点
1.3.1、关系型数据库
概述:关系型数据库最典型的数据结构是表,由二维表及其之间的联系所组成的一个数据组织。
优点:
①、易于维护,都是使用表结构,格式一致;
②、使用方便,SQL语言通用,可用于复杂查询;
③、支持复杂操作,可用于一个表以及多个表之间非常复杂的查询。
缺点:
①、读写性能比较差,尤其是海量数据的高效率读写;
②、固定的表结构,灵活性不强;
1.3.2、非关系型数据库
优点:
①、格式灵活,存储数据的格式可以是键值对形式、文档形式、图片形式等等,文档形式、图片形式等,使用灵活,应用场景广泛,而关系型数据库则只支持基础类型;
②、读写速度快,nosql可以使用硬盘或者随机存储器作为载体,而关系型数据库只能使用硬盘;
③、扩展性高;
④、成本低,nosql数据库部署简单,基本都是开源软件。
缺点:
①、不提供sql支持,学习和使用成本较高;
②、一般都不支持事务处理;
③、数据结构相对复杂,复杂查询方面稍欠。
1.4、初识Redis
概述:Redis是一个使用ANSI C编写的开源、包含多种数据结构、支持网络、基于内存、可选持久性的键值对存储数据库。
特点:
①、基于内存运行,性能高效;
②、支持分布式,理论上可以无限扩展;
③、Key-Value存储系统;
④、开源的使用ANSI C语言编写、遵守BSD协议、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。
2、Redis安装与入门
2.1、Linux操作系统下安装Redis
2.1.1、搭建虚拟机
首先在VMware新建一个虚拟机,用于后期学习Redis(不会搭建虚拟机可以参考我的hadoop2.7小白级环境搭建教程中第一章有关搭建虚拟机的操作或去b站找视频跟做)。
2.1.2、对虚拟机进行网络设置
①、首先打开vmware,找到左上角的编辑-->点击虚拟网络编辑器:
②、点击新弹出窗口中位于右下角的更改设置:
③、切换到Nat模式,然后记录IP所属网段和虚拟机网关:
④、打开虚拟机,通过如下命令更改虚拟机网络配置信息:
vi /etc/sysconfig/network-scripts/ifcfg-ens33
⑤、进入文件后,参考下图进行修改:
⑥、最后通过如下命令重启虚拟机,等待虚拟机重启后ping任意网站查看是否成功,如果成功就说明我们的网络设置成功了:
# 重启虚拟机
reboot
# 登录虚拟机后通过ping命令验证虚拟机的网络设置
ping baidu.com
# 通过Ctrl+C暂停
2.1.3、获取Redis下载链接
进入Redis的下载目录,找到自己想要下载的redis版本(注意:要找.tar.gz格式的文件),鼠标放在对应版本上,鼠标右键-->左键点击复制链接:
2.1.4、下载并解压文件
打开虚拟机,通过root用户输入如下命令,表示在根目录下创建一个名为download的目录,然后进入download目录后下载redis压缩包并解压到/usr/local目录下:
# 创建download目录
mkdir download
# 进入download目录
cd download
# 安装用于下载的wget
yum install wget -y
# 通过wget下载我们前面复制的链接中存在的Redis压缩包
wget https://download.redis.io/releases/redis-7.0.2.tar.gz
# 将Redis压缩包解压到/usr/local目录下
tar -zxvf redis-7.0.2.tar.gz -C /usr/local
2.1.5、下载C语言编译环境
进入Redis的存放目录/usr/local,通过命令安装C语言编译环境(因为Redis是基于C语言开发,所以需要C语言的编译环境):
# 进入Redis的安装目录
cd /usr/local
# 查看该目录下的文件
ll
# 进入Redis目录
cd redis-7.0.2
# 安装C语言编译环境
yum install -y gcc
2.1.6、编译Redis
在redis-7.0.2目录下执行make命令,将Redis源代码编译成可执行文件。
# 确认自己在redis-7.0.2目录下
pwd
# 执行make命令
make
2.1.7、安装Redis
在redis-7.0.2目录下执行该命令,将编译后的数据安装到预定目录。
make install
从上图可知,预定目录为/usr/local/redis-7.0.2/src,进入该文件通过ls命令可以发现该目录中有很多文件:
重点文件说明:
redis-benchmark:Redis自带的基准性能测试工具
redis-check-aof:对有问题的 AOF 文件进行修复,AOF和RDB文件后面会说明
redis-check-rdb:对有问题的 RDB文件进行修复
redis-sentinel:Redis集群使用
redis-cli:客户端
redis-server:启动服务器
2.1.8、验证Redis
进入/usr/local/redis-7.0.2/src目录,通过如下命令启动一个单机版Redis
# 本质上是打开了一个可执行文件
./redis-server
运行结果如下:
通过上图可知,Redis的默认端口号为6379,但启动后发现通过./redis-server启动的服务在前台运行,可以考虑修改Redis的核心配置文件redis.config的参数使Redis处于后台运行:
# 进入Redis解压后的目录(/usr/local/redis-7.0.2)
cd ..
# 通过ll命令查看是否有redis.conf文件
ll
编辑文件,将daemonize的值修改为yes:
# 编辑文件
vi redis.conf
# 搜索参数
/daemonize
搜索到daemonize后按i键,然后将no修改为yes,再按ESC键,输入 :wq 后回车即可:
实操一下后台运行redis服务:
前面已经了解到Redis的默认端口号为6379,我们查看一下6379端口的状态就可以指定Redis是否在运行了:
# 首先下载用于查看端口状态的lsof
yum install -y lsof
# 通过lsof查看端口状态
lsof -i:6379
运行结果如下:
有对应的内容就说明该端口在运行中,至此我们的Redis已经安装成功了!
注意:在src目录下除了redis-server文件可以启动外,redis-cli也可以启动redis。
2.2、Redis入门
2.2.1、默认16个数据库
概述:Redis是一个字典结构的存储服务器,一个Redis实例提供了多个用来存储数据的字典,客户端可以指定将数据存储在哪个字典中。这与关系数据库实例中可以创建多个数据库类似,所以可以将其中的每个字典都理解成一个独立的数据库。
验证:在Redis目录下的redis.conf文件中查询database就能发现默认是16个数据库(下标从0开始,到15结束,共16个数据库)。
# 先进入/usr/local/redis-7.0.2目录
cd /usr/local/redis-7.0.2
# 编辑redis.conf文件
vi redis.conf
# 搜索database关键字(不要按i键)
/database (输完回车即可)
跳转到如下位置:
2.2.2、Redis的线程
概述:Redis是基于内存的操作,CPU不是Redis的瓶颈,Redis的瓶颈最有可能是机器内存的大小或者网络带宽。由于单线程容易实现,而且CPU不会成为瓶颈,因此Redis顺理成章地采用单线程的方案。
注意:由于Redis采用单线程的方案,所以Redis的高效性不是由于多线程的原因,真正的原因是采用网络IO多路复用技术来保证在多连接的时候,保证系统的高吞吐量。这里 "多路" 指的是多个网络连接,"复用" 指的是复用同一个线程。采用多路 I/O 复用技术可以让单个线程高效的处理多个连接请求(尽量减少网络IO的时间消耗),且Redis在内存中操作数据的速度非常快(内存内的操作不会成为这里的性能瓶颈),因此造就了Redis具有很高的吞吐量。
2.2.3、切换数据库
语法:
select 下标
实操1:链接Redis,为第一个数据库添加任意数据,然后切换到第二个数据库,看是否能拿到第一个数据库的数据。
# 进入redis的src目录
cd /usr/local/redis-7.0.2/src/
# 启动redis使其在后台运行
redis-server ../redis.conf
# 打开redis客户端
redis-cli
# (默认进入第一个数据库,小标为0)为数据库添加一个键为k1,值为10的数据
set k1 10
# 获取当前数据库中键为k1的值
get k1
# 切换到第二个数据库(下标为1)
select 1
# 获取当前数据库中键为k1的值
get k1
# 再次切换到第一个数据库
select 0
# 获取当前数据库中键为k1的值
get k1
运行结果如下:
2.2.4、清空当前库
语法:
FLUSHDB
实操2:在实操1的基础上清空第一个数据库中的数据并验证是否清空。
# 通过如下命令直接清空当前数据库中所有的内容
flushdb
# 尝试获取前面添加的键位k1的值
get k1
运行结果如下:
2.2.5、删除所有库的数据
语法:
FLUSHALL
实操3:为下标为3的库添加任意数据,然后在其他库通过删除所有库的命令删除所有数据库数据,再查看下标为3的数据库数据是否还存在。
# 切换到小标为3的数据库
select 3
# 添加一个键为num,值为123的数据
set num 123
# 获取键位num的值
get num
# 切换到下标为4的数据库
select 4
# 清空所有数据库的数据
flushall
# 切换到小标为3的数据库
select 3
# 再次获取键位num的值
get num
运行结果如下:
3、Redis数据类型
3.1、键相关的语句
3.1.1、keys & scan
概述:keys用于查看当前库中所有的键。
语法:
keys 要查找的值/通配符/要查找的值+通配符
Redis中的通配符:
*:通配任意多个字符;
?:通配单个字符;
[ ] :通配中括号中的某一个字符。
实操4:为第一个数据库添加任意数据,然后通过key键获取当前数据库中所有以num开头的键。
# 进入redis的src目录
cd /usr/local/redis-7.0.2/src/
# 使redis在后台运行
redis-server ../redis.conf
# 启动redis客户端,方便后面操作数据库
redis-cli
#插入多条数据
set num1 1
set num2 2
set abc 3
# 获取所有以num开头的键
keys num*
运行结果如下:
注意:生产环境中已经禁止了采用keys *查询所有键或指定键的操作,当数据量较大时,长时间阻塞redis会导致其他客户端的命令请求一直处于阻塞状态, 更安全的做法是采用scan。
scan语法:
redis-cli [-n 数据库下标] --scan [--pattern "指定字符/通配符/指定字符+通配符"]
-n:用于指定要查询的数据库下标,默认为第一个数据库
--pattern:用于指定匹配的字符,默认值是*,指所有数据库
实操5:采用scan的方式获取第一个数据库中所有以num开头的键。
# 进入redis的src目录
cd /usr/local/redis-7.0.2/src/
# 使redis在后台运行
redis-server ../redis.conf
# 以scan的方式获取redis第一个数据库中所有以num开头的键
redis-cli -n 0 --scan --pattern "num*"
运行结果如下:
3.1.2、exists
概述:exists用于判断某个键是否存在,返回1表示存在,0不存在。
语法:
exists 键名1 [键名2 键名3...]
实操6:查询第一个数据库中是否存在一个名为num1的键。
# 进入redis的src目录
cd /usr/local/redis-7.0.2/src/
# 使redis在后台运行
redis-server ../redis.conf
# 启动redis客户端
redis-cli
# 查看第一个数据库中是否存在名为num1的键
exists num1
# 测试exists能否使用通配符
exists num*
exists *
运行结果如下:
总结:通过实操6可知,existis不支持使用通配符。
3.1.3、type
概述:type用于查看当前键所储存的值的类型,返回当前键所储存的值的类型,如string 、list等。
语法:
type 键名
实操7:查看第一个数据库中num1存储的值是什么类型。
# 进入redis的src目录
cd /usr/local/redis-7.0.2/src/
# 使redis在后台运行
redis-server ../redis.conf
# 启动redis客户端
redis-cli
# 查看num1的类型
type num1
运行结果如下:
3.1.4、del
概述:del用于删除已存在的键,不存在的键会被忽略,删除成功返回1,删除失败返回0。
语法:
del 键名1 [键名2 键名3]
实操8:删除第一个数据库中名为abc的键。
# 进入redis的src目录
cd /usr/local/redis-7.0.2/src/
# 使redis在后台运行
redis-server ../redis.conf
# 启动redis客户端
redis-cli
# 删除名为abc的键(数据库中存在的键)
del abc
# 删除一个不存在的键
del def
运行结果如下:
3.1.5、expire
概述:expire用于给键对应的值设置过期时间,设置成功返回 1 ,键不存在返回 0。
语法:
expire 键名 时间(单位为秒)
实操9:为第一个数据库中键为num2的值设置过期时间为5秒,5秒后再获取该键的值查看是否成功。
# 进入redis的src目录
cd /usr/local/redis-7.0.2/src/
# 使redis在后台运行
redis-server ../redis.conf
# 启动redis客户端
redis-cli
# 查看num2的值
get num2
# 为num2的值设置5秒的过期时间
expire num2 5
# (5秒后执行)再次获取num2的值
get num2
运行结果如下:
3.1.6、ttl
概述:ttl以秒为单位返回键的剩余过期时间。
语法:
ttl 键名
实操10:将数据库中num1的过期时间设置为20秒,然后间断地获取剩余过期时间。
# 进入redis的src目录
cd /usr/local/redis-7.0.2/src/
# 使redis在后台运行
redis-server ../redis.conf
# 启动redis客户端
redis-cli
# 为num1的值设置为1
set num1 1
# 将num1对应的值的失效时间设置为20秒
expire num1 20
# 获取num1的值当前还剩余多少时间失效
ttl num1
运行结果如下:
总结:当指定的键不存在时,返回 -2; 当指定的键存在但没有设置剩余生存时间时,返回 -1 ; 当指定的键存在且设置了过期时间,将以秒为单位返回指定键的剩余生存时间。
3.1.7、persist
概述:persist 用于移除给定键名的过期时间,使得指定的键永不过期。
语法:
persist 键名
注意:当过期时间移除成功时,返回 1 ;如果指定的键名不存在或指定的键名没有设置过期时间,返回 0 。
实操11:为num1设置过期时间为20秒,然后通过persist将num1设置为永不过期,再通过ttl获取num1的过期时间。
# 设置num1的值为1
set num1 1
# 为num1键设置过期时间为20秒
expire num1 20
# 将num1设置为永不过期
persist num1
# 获取num1的过期时间
ttl num1
运行结果如下:
3.2、String
概述:String是Redis最基本的类型,一个键对应一个值。由于String是二进制的,意味着String可以包含任何数据,比如序列化对象或者一张图片,String最多可以放512M的数据。
使用场景:计数器,统计粉丝数,对象缓存存储,分布式锁。
3.2.1、set & get
概述1:Set用于设置指定键的值。如果指定键已经存储了其他值,set 就重写旧值,且无视类型。
概述2:get用于获取指定键的值。如果键不存在或键的值为空就返回 nil 。
语法:
set 键名 值
实操12:为数据库中的num1多次设置值,最后再去获取num1的值,查看其值是多少。
# 多次设置num1的值
set num1 1
set num1 2
set num1 3
# 获取num1的值
get num1
运行结果如下:
3.2.2、append
概述:append用于将给定的值追加到指定键原值末尾。
语法:
append 键名 追加的值
注意:
①、如果键已经存在并且值是一个字符串, append 命令将值追加到键原来的值的末尾。
②、如果键不存在, append 就简单地将 给定键 设为 指定值 ,与语句 set key value 实现的效果一样。
实操13:为数据库中num1追加内容。
# 获取num1的值
get num1
# 为num1追加内容
append num1 45
# 获取num1的值
get num1
运行结果如下:
3.2.3、strlen
概述:strlen用于获取指定键所储存的字符串值的长度,当键储存的不是字符串值时,会返回一个错误。
语法:
strlen 键名
实操14:获取数据库中num1对应的值的长度。
# 获取当前num1的值
get num1
# 获取num1字符串的长度
strlen num1
运行结果如下:
3.2.4、setex
概述:setex用于给指定的键设置值及其对应的过期时间。如果键已经存在, setex命令将会替换旧的值,并设置过期时间。
语法:
setex 键名 过期时间 值
实操15:为默认数据库添加一个名为num123的键,对应的值随意,要求设置其过期时间为20秒。
# 设置num123的值为123456,其过期时间为20秒
setex num123 20 123456
# 获取num123的值
get num123
# 获取当前的过期时间
ttl num123
# (20秒后--num123过期后执行)再次获取num123的值
get num123
运行结果如下:
3.2.5、setnx
概述:setnx只有在指定的键名不存在时才能设置指定键对应的值。
语法:
setnx 键名 值
实操16:通过setnx添加一个已经存在的键和一个不存在的键。
# 通过setnx创建一个已经存在的键
setnx num1 123
# 通过setnx创建一个不存在的键
setnx num5 100
# 分别获取num1和num5的值(验证是否创建成功)
get num1
get num5
运行结果如下:
3.2.6、getrange
概述:getrange用于获取指定下标(下标从0开始)区间范围内的值,类似与MySQL中的between...and...语句。
语法:
getrange 键名 起始下标 结束下标
实操17:创建任意一个键包含较长内容的值,然后通过getrange语句获取该键下标从2到6的内容信息。
# 通过setnx语句创建一个名为var1的键
setnx var1 muxikeqikeepstudy
# 通过getrange语句获取var键中下标从2到6的字符信息
getrange var1 2 6
运行结果如下:
3.2.7、setrange
概述:setrange用于设置指定区间范围内的值。
语法:
setrange 键名 替换的起始下标 值
实操18:将默认数据库中的var1的study替换成relax,并尝试在超出原有字符串长度的下标插入任意数据。
# 获取var1的值
get var1
# 获取var1的长度
strlen var1
# 将study的内容替换成relax
setrange var1 12 relax
# 在超出原有字符串长度的下标位置插入数据
setrange var1 25 hello
# 获取修改后的var的值
get var1
运行结果如下:
3.2.8、incr
概述:incr用于将键中储存的数字值加一。
语法:
incr 键名
注意:
①、如果键不存在,那么键对应的值会先被初始化为 0 ,然后再执行 incr 操作。
②、如果字符串包含除数字以外的字符或是其他类型的数据,将会返回一个错误。
实操19:分别创建一个值为纯数字的字符串和一个值中包含其他字符的键,将两个键分别用incr语句进行值加一的操作。
# 创建一个值为纯数字的键
seten var2 123
# 对新建的键var2做加一操作
incr var2
# 创建一个值中包含其他字符的键
setnx var3 123abc
# 对新建的键var3做加一操作
incr var3
# 获取var4的值(验证var4不存在)
get var4
# 对var4做加一操作
incr var4
运行结果如下:
3.2.9、decr
概述:decr用于将键中储存的数字值减一。
语法:
decr 键名
注意:
①、当键不存在时,键的值会先被初始化为 0 ,然后再执行 decr 操作。
②、当字符串类型的值不是纯数字时或是其他类型的值,那么将返回一个错误。
实操20:分别对一个值为纯数字,值中包含其他字符和空值用decr语句做减一处理。
# 获取var2的值
get var2
# 对var2做减一处理
decr var2
# 获取var3的值
get var3
# 对var3做减一处理
decr var3
# 获取var5的值
get var5
# 对var5做减一处理
decr var5
运行结果如下: