Redis学习笔记 [初识Redis,学习常用的5种数据类型]
Posted 小智RE0
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Redis学习笔记 [初识Redis,学习常用的5种数据类型]相关的知识,希望对你有一定的参考价值。
近期计划对redis再进行一段详细的学习,
在B站找到了尚硅谷的redis教学视频->【尚硅谷】Redis 6 入门到精通 超详细 教程,进行学习记录
文章目录
🛴1.关于NoSql的概念引入
首先从技术的发展来看,大致可以分为三点:
(1) 功能性的方面:Java语言,JSP页面,Tomcat服务器,H5前端语言,Linux操作系统,JDBC数据库连接驱动,SVN版本控制;
(2) 扩展性的方面:比如学过的这些框架,都具有很好的扩展性->,Spring,SpringMVC,Hibernate,Mybatis;
(3) 性能方面: 比如这个NOSQL数据库,Java线程,大数据相关的Hadoop,nginx反向代理服务器,MQ消息队列,ElasticSearch搜索引擎
Web1.0时代的数据服务
这样的单体架构是没有太大压力的,随着Web2.0时代来临,用户的访问量大幅度提升,同时产生大量用户数据,互联网数据产生的大量数据,造成访问量压力.
首先是这个CPU的内存压力问题;
比如说,现在使用了Nginx反向代理服务器,后端开了两个服务,此时用户在服务器A登录了,产生了session会话,那么要是切换到服务器B,怎么无缝切换呢?
- 1.首先可以考虑使用浏览器cookie存储,但是安全性不能保证;
- 2.可以考虑使用session复制,但是一直复制的话,会产生数据冗余问题;
- 3.那么采用NOSQL数据库,比如redis进行缓存,将第一次登录产生的session会话存储起来,其他服务需要时,在redis缓存中获取即可.
IO的压力问题
🌈1.1 NoSql概念
那么初步了解一下什么是NoSql吧!
NoSql也就是非关系型数据库
,不依赖于业务逻辑进行存储,采用了key-value
键值对的形式进行存储;
它的几个特点:(1)不支持ACID原则,(2)在性能方面优于SQL,(2)不遵循传统SQL的标准;
常用的场景:高并发的海量数据使用,
不适用的场景:
- 基于SQL的结构化查询存储场景,
- 处理复杂的关系,[就需要考虑使用即使查询]; -
- 需要事务支持的;
可以看看常见的NoSql数据库:
Mecache :
- 数据在内存中,一般不进行持久化;
- 支持简单的
key-value
键值格式,支持类型单一化; - 作为
缓存数据库
来辅助持久化的数据库(比如mysql).
Redis
- 数据在内存中,可支持持久化,主要用于数据的备份恢复;
- 不但支持基础的键值对格式,还支持其他类型 (list,set,hash,zset);
- 也是作为辅助性质的缓存数据库使用;
MongoDb
- 特点:高性能,开源且模式自由的
文档型数据库
- 数据都位于内存中,若内存不足,就会把不常用的数据存入硬盘,
- 虽采用键值对格式,但对于
value
提供查询功能(比如JSON这样的); - 可根据数据的特点替代
RDBMS
(关系数据库管理系统),作为独立的数据库; - 可以配合
RDBMS
,存储特定的数据.
🌈1.2行式数据库 | 列式数据库
行式数据库可以这么理解,比如这个数据表存储的这三条信息,在行式数据库的规则中,就会采用一行一行的进行存储;
- 比如需要查询id为3的数据,那么效率就很快;
- 若查询年龄的平均数时,查询时就会慢一点;
列式存储数据库
🛴2.Redis概述与安装使用
🌈2.1基础知识
- 使用 C 语言开发的数据库,基于内存存储,具有过期失效策略;
- 支持value类型存储,可使用的数据类型有
String
[字符串],List
[链表],set
[集合],zset
[有序集合],hash
[哈希];这些数据类型还支持push/pop,add/remove的原子性操作; - 支持数据的持久化,可以将内存中数据存入硬盘,重启时加载使用;具有完好的容灾恢复机制;
- redis采用了单线程的多路IO复用;
- redis还可支持主从读写分离的同步机制.
🌈2.2安装使用
安装部分; 之前已经安装过redis—>
(1)前台启动
前台启动的方式; [不推荐使用]
输入./redis-server
这个弊端就是一旦这样启动redis后,我们不能在这个页面执行操作其他的命令;
使用ctrl
+c
即可停止服务;
(2)后台启动
后台服务启动的方式;进行配置;
这里可以将redis的配置文件拷贝一份放置到etc
目录下;
(这里之前由于上次进行主从练习,所以将配置文件做了三份);
此时cd /etc
键入该目录;ls
查看文件;
将
deamonize no
改为deanonize yes
;
可直接在ftp中使用记事本修改;
或使用vi编辑器命令进行修改;
vi redis79.conf
然后回到之前安装redis的目录;
注意启动的是etc下完成配置的文件;
此时即成功启动;
可使用命令查看
ps -ef | grep redis
使用这种方式的话,可以采用客户端来连接redis
先输入命令
./redis-cli
可输入命令
ping
查看;
若需要退出,可使用shutdown
命令退出
或者输入exit
退出后,在查看进程时,采用kill -9
停止对应的进程号即可;
🌈2.3相关的知识说明
-
默认是6379端口号;
-
这个redis它默认采用16个数据库,类似于数组从零开始的索引下标;初始启动时默认使用0号数据库;
-
使用命令
select <dbid>
可以切换数据库;
比如切换到6号库
-
可以统一进行密码管理,所有数据库此案有同样的密码;
-
可使用命令
dbsize
查看当前数据库的key
键的数量; -
命令
flushdb
可清空当前数据库, -
命令
flushall
可通杀全部的数据库;
Redis采用了单线程 + 多路IO复用技术;
- 这里的多路复用是指使用一个线程检查多个文件描述符(socket)就绪状态,例如调用select 或 poll函数方法;传入多个文件描述符,若有一个文件描述符就绪,则进行返回,否则可以阻塞直到超时,得到就绪状态后会进行真正的操作可支持在同一个线程中执行,或者启用线程池执行;
多线程+锁(缓存的memcached)
单线程+多路IO复用(redis)
;- 和memcache不同之处: redis支持多种数据类型,支持持久化, 采用了单线程+多路IO复用;
这里的单线程+多路IO复用,可以这样理解;
比如这个买票的案例,黄牛在火车站买票即可看作是单线程的处理;
而其他的乘客在向黄牛发出买票的请求后,可看做多路的IO操作,用户在等待黄牛的票据时,可以去做自己的事情,不需要在这里进行一直阻塞等待;
🛴3.key键操作 以及常见的几种数据类型
🌈3.1key键的操作
📢3.1.1基本概念及常用命令
keys *
命令可查看当前数据库中的所有key;当然可以匹配数据库使用keys * 指定的数据库
exists key
可以判断某个key是否存在;type key
可查看key的类型del key
可删除指定的key数据unlink key
可根据指定的value
选择非阻塞式的删除; (注意只是将keys
从keyspace
元数据中删除,真正的删除会在后序的异步操作指向);expire key 秒数
可为指定的key设置过期时间;ttl key
可查看当前key还有多少秒过期, (-1
表示永远都不失效,-2
表示已经失效);select
命令可用于切换数据库;dbsize
可查看当前数据库的key数量
📢3.1.2练习操作
练习;
由于这个初始化,里面没有数据;先设置几个key进去
在redis启动后,使用 set 键名 value值
命令即可
- 试试命令
keys *
;可看到设置的几个键;
- 使用命令
exists key
可查看是否存在;
返回(0)代表不存在该key;
-
可使用
type 键名
查看键的类型;
-
使用命令
del key名
删除k2,查看效果;
-
尝试对k3使用
expire 键名 过期时间
设置一个过期时间50秒; 然后使用ttl key名
查看是否过期;
-
对于这个仅保留下来的键
k1
;有没有设置过期时间,那么它就是一直有效的;ttl key名
若返回-1 则说明永不失效;
🌈3.2 数据类型(1)String—字符串
📢3.2.1基本概念及常用命令
- String字符串作为最基本的类型,一个key对应一个value;
- String类型是二进制安全的,也就是说Redis的String可以包含存入任何数据,比如说 jpg/png类型的图片(或者是序列化的对象)都可以;
- String类型作为redis最基本的数据类型,一个redis中的字符串
value
可以最多存512M
;
String的数据结构是可变的简单动态字符串,内部实现类似于ArrayList结构,采用了预分配冗余空间的方式减少内存的频繁分配操作.
内部采用当前字符串实际分配的空间, 容量
capacity
一般高于(len
)实际字符串的长度;
- 当字符串的长度小于1M时,扩容都会加倍现有的空间;
- 若长度超过1M,扩容时,仅扩容1M空间;
- 注意字符串最大长度512M.
常用的命令学习
set key value
添加键值对的命令;*NX
当数据库中的key不存在时,可以将key-value
添加到数据库;*XX
当数据库汇总的key存在时,可以将key-value
添加到数据库,与NX
参数互斥;*EX
key的超时秒数;*PX
key的超时毫秒数,与EX
参数互斥;
get key
查询对应的键值;append key value
将给定的value 值追加到原来值的末尾位置;然后返回总的长度值;strlen key
获取到指定key值的长度;setnx key value
只要在key不存在时,可设置key的值;(即这个key要是存在
,将无法对这个key的value值进行覆盖)
-
incr key
- 可将key中存储的数字值增加1;
- 只能对数字值进行操作,如果该数字为空,那么新增值为1;
-
decr key
- 可将key中存储的数字值减少1;
- 只能对数字值进行操作,如果该数字为空,那么新增值为-1;
incrby /decrby key 步长
可将key中处处的数字值进行增或减操作,自定义步长;- 注意原子性问题
incr key
时间复杂度为O(1); - 对于存储在指定key的值进行原子性的+1操作;
- 这里原子性操作是指不会被线程调度机制打断的操作; 该操作开始后,就会一直运行到结束,其中不会有任何的context switch(切换线程出现).
- 注意原子性问题
关于原子性的问题分析
学习JMM时,知道了在Java中的i++
就不是原子性操作;
比如说现在内存中的一个共享变量值为 i=0
; 那么使用线程A和线程B分别对这个变量i
进行++100
的操作,最终值的范围 可能为 2 ~ 200
因为线程A和线程B在执行过程中,可能会被对方打断操作;
mset key1 value1 key2 value2 .....
可以同时设置一个/多个键值对;mget key key2 key3
可以同时获取一个或者多个键值对;msetnx key1 value1 key2 value2.....
同时设置一个或者多个 键值对时,当且仅当所有给定的key都不存在;(注意若有一个失败,则全部失败);
getrange key 起始位置 结束位置
可获取值指定范围的值,类似于Java语言中的substring函数;这个范围是左右封闭的区间
;setrange key 起始位置 新的value
从起始位置开始 可用新的value覆盖
指定的key存储的字符串值;
setex key 过期时间 value
可在设置键值的同时,设置单位秒数的过期时间;getset key value
以新换旧,采用设置的新value替换指定key的值, 且返回原来的value值
📢3.2.2练习操作
练习:
(1)首先将刚才操作创建的结果key键删除,在进行接下来的学习操作
使用命令
flushdb
清空数据库,可看到当前库中已无数据;
(2)设置存入两个新的键值;
k1 - xiaozhi001
k2 - xiaozhi002
(3)使用get key
命令,获取到指定的键的值;
然后使用set k1 新值
命令,操作k1,会发现新值将原来的值直接覆盖;
(4)使用append key value
将给定的value 值追加到原来k1值
的末尾位置;返回一个长度;
(5)命令strlen key
可返回指定键的长度;
(6)命令setnx key value
只要在key不存在时,才可以设置key的值;
来试试对k1操作;(可发现这里由于k1存在,设置的值无法生效);
为了接下来的练习操作,再添加几个新的键值对 ;
(7) 试试 incr key
命令,对数字值进行加一操作;
(8) 试试 decr key
命令,对于数字值进行减1操作;
(9) 尝试使用命令incrby key 步长值
进行新增指定步长操作 ;
使用命令decrby key 步长值
进行减少指定步长操作 ;
(10)使用 mset key1 value1 key2 value2 .....
创建多个键值对学习;
可用mget命令
获取多个键值对;
(11)使用msetnx key1 value1 key2 value3 ....
设置多个键值对时,若其中一个键已存在,则该命令设置的键值对都不会生效;
当设置的键值对都不存在时,才会添加成功;
(12) 命令getrange key 起始位置 结束位置
;截取指定位置的值; 这个范围是左右封闭的区间
;
(13)使用命令 setrange key 起始位置 新的value
;设置新值;
从user
的索引9开始,使用iscodersss
替换接下来的10位字符,
原先的user
值被彻底替换;
(14)练习使用setex key 过期时间 value
命令,设置键值对的同时,设置单位为秒数的过期时间;
(15) 练习使用getset key value
设置新的值替换旧值,
🌈3.3 数据类型(2)List—列表
📢3.3.1基本概念及常用命令
- 单键多值的存储方式;
- redis列表采用了简单的字符串列表,按照插入顺序进行排序,可添加一个元素到列表的头部/尾部;
- 底层采用双向链表,处理两端的节点操作性能好,但是处理中间节点的数据时,性能差一点
- 实际上这个List类型的数据结构可以理解为快速链表
quickList
, - 首先在列表元素较少的情况下会使用一块连续的内存进行存储,该结构是
ziplist
,即压缩列表的形式; - 可以将所有的元素紧挨着一起存放,分配时采用连续的内存;
- 当数据量较大时变为
quicklist
由于普通的链表需要附加的指针空间过大;
比如说这个列表存储的是int 类型的数据,结果上还需要使用额外的指针prev以及next指针
;
在Redis中将链表和ziplist结合形成quicklist,即将多个ziplist使用双向的指针,连接串起来使用;
常用命令
lphsh/rpush key value1 value2 value3 ....
从左边/右边插入一个或者多个值,lpop/rpop key
从左侧/右侧弹出一个值;- 值在则键在, 值光则键亡
rpoplpush key1 key2
从key1 列表的右边弹出一个值,插入到key2列表的左边;lrange key start stop
按照索引下标获得元素,(由左至右).
lrange 指定列表名 0 -1
0:左边第一个, -1 右边第一个; (0-1表示获取所有)lindex key index
按照索引获取元素,(注意是从左至右的);llen key
获取列表长度;
linsert key before value newvalue
在value的后面添加一个newvalue
值;lrem key n value
从左边删除指定的 n 个值(由左至右);lset key index value
将列表key下标为index的值替换为指定的value;
📢3.3.2 练习操作
首先使用命令 flushdb
清空数据库之前的数据;
(1) 使用lphsh k1 v1 v2 ....
创建列表,依次从左边为链表插入值;
命令lrange 起始位置 结束位置
可从左至右按索引获取元素
(2) 使用rphsh k1 v1 v2 ....
创建列表,依次从右边为链表插入值;
命令lrange 起始位置 结束位置
可从左至右按索引获取元素
(3) 使用命令 lpop key
,从列表左侧弹出一个值;
(4)使用命令 rpop key
,从列表右侧弹出一个值;
(5)使用命令rpoplpush key1 key2
从K1列表的右侧弹出一个值,添加到K2列表的左侧;
(6)使用llen 指定列表
命令获取到指定列表的长度;
使用lindex 指定列表 指定索引
获取到指定类别的指定索引位置的元素
;
(7)使用linsert 指定列表 before 指定元素 新元素
命令;
在指定的列表的指定元素之前,添加新的元素值
(8)可使用命令lrem 指定列表 指定个数 指定元素
;
从左侧开始,在指定列表
中的删除
指定个数的指定元素
;
(9)lset 指定列表 指定索引 新的元素值
命令;
可将指定列表
的指定索引位置
元素替换为指定的新元素值
;
🌈3.4 数据类型(3)Set—集合
📢3.4.1基本概念及常用命令
- 这个Set类型的也就是类似于List集合类型的; 特点是 Set可以自动去重处理;
- set提供了判断某数据成员是否位于set集合内的重要接口;
- set类型作为string类型的无序集合,底层采用了
value为null的hash表
,那么添加,删除,查询操作的复杂度都平均是O(1)的;
- Set数据结构采用了dict字典,而这个字典的结构采用了哈希表实现;
- 在Java中的HashSet内部实现采用了HashMap,但所有的value都指示到了同一个对象.
- Redis的set集合,内部同样地采用了hash结构,所有value指向到同一个内部值.
常用命令
sadd key value1 value2 .....
可以将一个或者多个元素存入到集合key
中,已存在的元素就会被忽略;smembers key
可取出该集合的所有值;sismember key value
可判断集合key
是否包含当前的value
值,若包含则返回1
,若无则返回0
;scard key
返回该集合的元素个数;srem key value1 value2....
删除集合中的某个元素,spop key
随机从该集合中弹出一个值;srandmember key n
随机从指定集合中取出n个值,注意不是删除
;
smove 源集合 目标集合 value
将某个集合的值移动到另一个集合;sinter key1 key2
返回两个集合的交集sunion key1 key2
返回两个集合的并集;sdiff key1 key2
返回两个集合的差集元素( 例如:即除了交集元素之外的元素);
📢3.4.2 练习操作
首先采用flushdb
命令清空数据库,在进行接下来的操作练习;
(1)可使用命令sadd key 值 值 ....
创建指定的set类型集合,会忽略已经存在的数据,即去重;
使用命令smembers 指定键
可获取到指定集合的所有数据
(2)使用命令sismember 指定集合 指定值
判断该指定集合是否存在指定值;
若存在则返回1; 若不存在则返回0;
(3)使用命令scard 指定集合
即可返回指定集合的元素个数;
使用命令 srem 指定集合 指定元素
集合删除指定集合中的指定元素
(4)可使用命令spop 指定集合
随机从指定集合中删除一个元素;
可使用命令srandmember 指定集合 指定个数
可随机访问指定集合的指定个元素;
先清空数据库,接下来的几个练习操作需要使用到2个以上的集合;
创建3个新的集合
(5)可使用命令 smove source destination value
将某个集合的元素移动到另一个集合;
(6) 使用命令sinter key1 key2
可返回两个集合的交集;
(7)可使用命令sunion key1 key2
返回两个集合的并集;
(8)可使用命令 sdiff key1 key2
返回两个集合的差集元素( 例如:即除了交集元素之外的元素);
🌈3.5 数据类型(4)Hash—哈希
📢3.5.1基本概念及常用命令
- Hash类型是一个键值对的集合;
- 采用了String类型的field和value的映射哈希关系表;所有采用hash适合存储对象;
- 对用的数据结构有两种:ziplist压缩列表和 hashtable哈希表;当 属性值 (field-value)的长度较短时,可使用ziplist结构,反之则在于hashtable结构.
案例分析
比如说使用用户Id作为查找的key,存储的value用户对象包含着姓名,年龄,生日等信息,那么可用普通的键值结果存储.
可试试这几种存储方式进行存储:
方法(1) 可将用户id作为键, 序列化之后的对象(或者是转为JSON格式的对象) 作为value值存储;
若存储序列化之后的对象,那么就需要先进行反序列化再修改数据,然后序列化后进行存储;
若存储JSON格式的对象,那么就需要解析之后再修改数据,在转为JSON格式进行存储;
方法(2)
可将用户id+姓名
作为键,存储当前用户的姓名数据,
将用户的id+年龄
作为键,存储当前用户的年龄数据;
将用户的id+生日
作为键,存储当前用户的生日数据
但是这样存储的话会出现
用户Id数据冗余
问题;
方法(3)
可以采用用户Id作为键; 而将用户的数据和对应的数据信息采用hash
数据类型存入到value中;
常用命令
hset key field value
为key集合中的field键
赋值value
;hget key1 field
由key1集合中的field获取到value
值;hmset key1 field1 value1 field2 value2.....
可以一次性地为集合key1中的多个field属性设置value值;hexists key1 field
判断这个key集合是否存在指定的field
;hkeys key
获取到当前集合下的所有field属性;hvals key
获取到当前集合下的所有value值;hincrby key field increment
为哈希表的key中的 field属性值 进行指定步长的增量操作;hsetnx key field value
当这个field不存在时,将field的值设为value;
📢3.5.2 练习操作
(1) 使用hset 键 属性 值
命令创建,键名为user:180
和user:181
的两个用户数据;分别存储id,name,age的信息;
实际就是这样一个结构:
(2)使用命令hget 指定集合key 指定属性
获取到集合中的属性值;
(3)使用命令hmset key1 field1 value1 field2 value2.....
可以快速地创建一个集合;
(4)可用hexists key1 field
判断这个key集合中是否存在指定的field;
(5)hkeys key
获取到当前集合下的所有field属性;
hvals key
获取到当前集合下的所有value值;
(6) hincrby key field increment
为哈希表的key中的 指定属性值 进行 指定的增量操作;
(7)hsetnx key field value
当这个field不存在时,将field的值设为value;
🌈3.6 数据类型(5)Zset—有序集合
📢3.6.1基本概念及常用命令
- 有序集合zset也是没有重复元素的字符串集合;但它还为每个成员关联了一个
score
分数属性,默认按照由小到大的方式进行排序; - 集合中的成员虽不能重复,但分数score可重复;
- 由于zset存储元素时有序,在进行范围查找时效率很高;
常用命令
zadd key score1 value1 score2 value2 .....
将一个/多个member元素和它的score值存入有序集合key;zrange key start stop withscores
获取到指定集合的下标在start->stop
之间的元素;zrangebyscore key minmax withscores limit offset count
在有序集合key中获取到score
分数位于min和max
之间的元素;[由小到大排序]
zrevrangebyscore key maxmin withscores limit offset count
在有序集合key中获取到score
分数位于min和max
之间的元素;[由大到小排序]
zincrby key increment value
对集合的指定元素score
增加一个指定的数值;zrem key value
删除该集合下,指定value值的元素;zcount key min max
统计当前集合的指定范围内元素个数;zrank key value
获取到指定value值 在指定key集合中的排名, [排名由0开始].
📢3.6.2练习操作
(1)使用zadd key score1 value1 score2 value2 .....
命令模拟存储多个数据;
使用zrange 指定集合 起始 结束
返回指定范围内的数据;
(2)zrangebyscore key minmax withscores limit offset count
指定范围的从小到大进行排序
(3)zrevrangebyscore key maxmin withscores limit offset count
指定范围的从大到小进行排序
(4)可用命令 zincrby key 指定的增量 指定的元素
; 将指定集合中的指定元素的score
增加指定的增量;
(5)使用 zrem 指定集合 指定元素
删除指定集合中的指定value元素
(6)使用命令zcount 指定集合 起始值 结束值
统计score在指定范围内的数据量个数
(7)使用命令zrank 指定集合 指定value
即可获取到指定的value在zrank中的排名;
📢3.6.3 数据结构
ZSet有序集合,类似于Java中的map结构,可以为每个元素赋予一个分数属性(权重)score
,另一方面来看,这个结构又类似于TreeSet,在其中的元素按照score
进行排序,可以得到每个元素的名词,也可以通过score的范围获取到元素的列表.
底层采用的数据结构为hash和跳跃表;
(1) 首先哈希结构 将 value和 score的关系映射关联起来
,保证了元素value的唯一性, 可采用value找到对应的score权重值;
(2)跳表结构的目的是为了对 元素value进行排序,可根据score的范围获取到元素的列表;
在效率方面,跳表结构堪比于红黑树.
模拟查找的案例;
(1)在链表结构中找到 节点元素51 ,需要一个一个地进行遍历查找到数据;
(2)在跳表结构中找到 节点元素51;步骤相对来说少一点;
首先在第2层找到节点1,要找的51大于1,那么就像向下一个节点21查询,
再向后找,发现22->指向空元素,那么,此时可以转移到第1层;
周到元素41,再向后找,此时注意到这个51元素位于41和61之间,那么再下移到第0层;
查找到元素51;
以上是关于Redis学习笔记 [初识Redis,学习常用的5种数据类型]的主要内容,如果未能解决你的问题,请参考以下文章