Redis学习
Redis是什么?有什么特点?
是什么:
Redis是一个开源的,基于内存亦可持久化的日志型、高性能Key-Value数据库,并提供多种语言的API
干什么:
性能极高 –redis读写性能测试redis官网测试读写能到10万左右,redis读写能力为2W/s,mysql读能力5K/s、写能力为3K/s,数据上看redis性能碾压mysql
丰富的数据类型 – Redis支持二进制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 数据类型操作。
原子 – Redis的所有操作都是原子性的,意思就是要么成功执行要么失败完全不执行。单个操作是原子性的。多个操作也支持事务,即原子性,通过8和EXEC指令包起来。 但是Redis 事务的执行并不是原子性的。
redis事务可以理解为一个打包的批量执行脚本,但批量指令并非原子化的操作,中间某条指令的失败不会导致前面已做指令的回滚,也不会造成后续的指令不做。
什么是Nosql
NoSQL = Not Only SQL(不仅仅是SQL)
Not Only Structured Query Language
关系型数据库:列+行,同一个表下数据的结构是一样的。
非关系型数据库:数据存储没有固定的格式,并且可以进行横向扩展。
NoSQL泛指非关系型数据库,随着web2.0互联网的诞生,传统的关系型数据库很难对付web2.0时代!尤其是超大规模的高并发的社区,暴露出来很多难以克服的问题,NoSQL在当今大数据环境下发展的十分迅速,Redis是发展最快的。
Nosql特点
-
方便扩展(数据之间没有关系,很好扩展!)
-
大数据量高性能(Redis一秒可以写8万次,读11万次,NoSQL的缓存记录级,是一种细粒度的缓存,性能会比较高!)
-
数据类型是多样型的!(不需要事先设计数据库,随取随用)
-
传统的 RDBMS 和 NoSQL
传统的 RDBMS(关系型数据库) - 结构化组织 - SQL - 数据和关系都存在单独的表中 row col - 操作,数据定义语言 - 严格的一致性 - 基础的事务 - ...
Nosql - 不仅仅是数据 - 没有固定的查询语言 - 键值对存储,列存储,文档存储,图形数据库(社交关系) - 最终一致性 - CAP定理和BASE - 高性能,高可用,高扩展 - ...
一.Redis安装
1.linux安装Redis
第一步:下载redis安装包
wget http://download.redis.io/releases/redis-4.0.6.tar.gz
第二步:解压压缩包
tar -zxvf redis-4.0.6.tar.gz
第三步:yum安装gcc依赖
yum install gcc
第四步:跳转到redis解压目录下
cd redis-4.0.6
第五步:编译安装
make MALLOC=libc
将/usr/local/redis-4.0.6/src目录下的文件加到/usr/local/bin目录
cd src && make install
2.window安装
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VUEgwWCn-1611141871860)(C:\Users\dong\AppData\Roaming\Typora\typora-user-images\image-20210110134354488.png)]
第一步:启动服务端
第二步:启动客户端进行连接测试
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CNFN4R0G-1611141871863)(C:\Users\dong\AppData\Roaming\Typora\typora-user-images\image-20210110134554492.png)]
连接成功
3.性能测试
redis-benchmark:**Redis官方提供的性能测试工具,参数选项如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bdPFkb7a-1611141871864)(C:\Users\dong\AppData\Roaming\Typora\typora-user-images\image-20210110135124569.png)]
依次是
10万个请求进行写入测试
50个并发客户端
每次写入3个字节
只有一台服务器处理,单机性能
所有请求1毫秒内完成
每秒处理93129次请求
二.配置文件
port 6379 --端口号
bind 127.0.0.1 --这个配置是只允许本地客户端访问
protected-mode yes --是否开启保护模式。默认开启,如果没有设置bind项的ip和redis密码的话,服务将只允许本地访 问
loglevel notice – 配置日志级别。选项有debug, verbose, notice, warning
logfile “” --日志名称。空字符串表示标准输出。注意如果redis配置为后台进程,标准输出中信息会发送到/dev/null
databases 16 --设置数据库个数。默认数据库是 DB 0,可以通过SELECT where dbid is a number between 0 and ‘databases’-1为每个连接使用不同的数据库。
**save 900 1 # 持久化设置:
save 300 10 # 下面的例子将会进行把数据写入磁盘的操作:
save 60 10000 # 900秒(15分钟)之后,且至少1次变更
\ # 300秒(5分钟)之后,且至少10次变更
\ # 60秒之后,且至少10000次变更
\ # 不写磁盘的话就把所有 “save” 设置注释掉就行了。
\ # 通过添加一条带空字符串参数的save指令也能移除之前所有配置的save指令,如: save “”
配置文件详解:https://www.jianshu.com/p/41f393f594e8
三.基本数据类型
在redis中无论什么数据类型,在数据库中都是以key-value形式保存,通过进行对Redis-key的操作,来完成对数据库中数据的操作。
常用命令
exists key
:判断键是否存在del key
:删除键值对move key db
:将键值对移动到指定数据库expire key second
:设置键值对的过期时间type key
:查看value的数据类型
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ngR0Wpd8-1611141871866)(C:\Users\dong\AppData\Roaming\Typora\typora-user-images\image-20210110142137970.png)]
关于TTL
命令
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8gJn9zVT-1611141871868)(C:\Users\dong\AppData\Roaming\Typora\typora-user-images\image-20210110142718592.png)]
Redis的key,通过TTL命令返回key的过期时间,一般来说有3种:
- 当前key没有设置过期时间,所以会返回-1.
- 当前key有设置过期时间,而且key已经过期,所以会返回-2.
- 当前key有设置过期时间,且key还没有过期,故会返回key的正常剩余时间.
关于重命名RENAME
和RENAMENX
RENAME key newkey
修改 key 的名称RENAMENX key newkey
仅当 newkey 不存在时,将 key 改名为 newkey 。
String(字符串)
命令 | 描述 | 示例 |
---|---|---|
APPEND key value | 向指定的key的value后追加字符串 | 127.0.0.1:6379> set msg hello OK 127.0.0.1:6379> append msg " world" (integer) 11 127.0.0.1:6379> get msg “hello world” |
DECR/INCR key | 将指定key的value数值进行+1/-1(仅对于数字) | 127.0.0.1:6379> set age 20 OK 127.0.0.1:6379> incr age (integer) 21 127.0.0.1:6379> decr age (integer) 20 |
INCRBY/DECRBY key n | 按指定的步长对数值进行加减 | 127.0.0.1:6379> INCRBY age 5 (integer) 25 127.0.0.1:6379> DECRBY age 10 (integer) 15 |
INCRBYFLOAT key n | 为数值加上浮点型数值 | 127.0.0.1:6379> INCRBYFLOAT age 5.2 “20.2” |
STRLEN key | 获取key保存值的字符串长度 | 127.0.0.1:6379> get msg “hello world” 127.0.0.1:6379> STRLEN msg (integer) 11 |
GETRANGE key start end | 按起止位置获取字符串(闭区间,起止位置都取) | 127.0.0.1:6379> get msg “hello world” 127.0.0.1:6379> GETRANGE msg 3 9 “lo worl” |
SETRANGE key offset value | 用指定的value 替换key中 offset开始的值 | 127.0.0.1:6379> SETRANGE msg 2 hello (integer) 7 127.0.0.1:6379> get msg “tehello” |
GETSET key value | 将给定 key 的值设为 value ,并返回 key 的旧值(old value)。 | 127.0.0.1:6379> GETSET msg test “hello world” |
SETNX key value | 仅当key不存在时进行set | 127.0.0.1:6379> SETNX msg test (integer) 0 127.0.0.1:6379> SETNX name sakura (integer) 1 |
SETEX key seconds value | set 键值对并设置过期时间 | 127.0.0.1:6379> setex name 10 root OK 127.0.0.1:6379> get name (nil) |
MSET key1 value1 [key2 value2…] | 批量set键值对 | 127.0.0.1:6379> MSET k1 v1 k2 v2 k3 v3 OK |
MSETNX key1 value1 [key2 value2…] | 批量设置键值对,仅当参数中所有的key都不存在时执行 | 127.0.0.1:6379> MSETNX k1 v1 k4 v4 (integer) 0 |
MGET key1 [key2…] | 批量获取多个key保存的值 | 127.0.0.1:6379> MGET k1 k2 k3 1) “v1” 2) “v2” 3) “v3” |
PSETEX key milliseconds value | 和 SETEX 命令相似,但它以毫秒为单位设置 key 的生存时间, | |
getset key value | 如果不存在值,则返回nil,如果存在值,获取原来的值,并设置新的值 |
String类似的使用场景:value除了是字符串还可以是数字,用途举例:
- 计数器
- 统计多单位的数量:uid:123666:follow 0
- 对象存储缓存
List(列表)
Redis列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)
一个列表最多可以包含 232 - 1 个元素 (4294967295, 每个列表超过40亿个元素)。
首先我们列表,可以经过规则定义将其变为队列、栈、双端队列等
正如图Redis中List是可以进行双端操作的,所以命令也就分为了LXXX和RLLL两类,有时候L也表示List例如LLEN
命令 | 描述 |
---|---|
LPUSH/RPUSH key value1[value2..] |
从左边/右边向列表中PUSH值(一个或者多个)。 |
LRANGE key start end |
获取list 起止元素==(索引从左往右 递增)== |
LPUSHX/RPUSHX key value |
向已存在的列名中push值(一个或者多个) |
LINSERT key BEFORE|AFTER pivot value |
在指定列表元素的前/后 插入value |
LLEN key |
查看列表长度 |
LINDEX key index |
通过索引获取列表元素 |
LSET key index value |
通过索引为元素设值 |
LPOP/RPOP key |
从最左边/最右边移除值 并返回 |
RPOPLPUSH source destination |
将列表的尾部(右)最后一个值弹出,并返回,然后加到另一个列表的头部 |
LTRIM key start end |
通过下标截取指定范围内的列表 |
LREM key count value |
List中是允许value重复的 count > 0 :从头部开始搜索 然后删除指定的value 至多删除count个 count < 0 :从尾部开始搜索… count = 0 :删除列表中所有的指定value。 |
BLPOP/BRPOP key1[key2] timout |
移出并获取列表的第一个/最后一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。 |
BRPOPLPUSH source destination timeout |
和RPOPLPUSH 功能相同,如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。 |
---------------------------LPUSH---RPUSH---LRANGE--------------------------------
127.0.0.1:6379> LPUSH mylist k1 # LPUSH mylist=>{1}
(integer) 1
127.0.0.1:6379> LPUSH mylist k2 # LPUSH mylist=>{2,1}
(integer) 2
127.0.0.1:6379> RPUSH mylist k3 # RPUSH mylist=>{2,1,3}
(integer) 3
127.0.0.1:6379> get mylist # 普通的get是无法获取list值的
(error) WRONGTYPE Operation against a key holding the wrong kind of value
127.0.0.1:6379> LRANGE mylist 0 4 # LRANGE 获取起止位置范围内的元素
1) "k2"
2) "k1"
3) "k3"
127.0.0.1:6379> LRANGE mylist 0 2
1) "k2"
2) "k1"
3) "k3"
127.0.0.1:6379> LRANGE mylist 0 1
1) "k2"
2) "k1"
127.0.0.1:6379> LRANGE mylist 0 -1 # 获取全部元素
1) "k2"
2) "k1"
3) "k3"
---------------------------LPUSHX---RPUSHX-----------------------------------
127.0.0.1:6379> LPUSHX list v1 # list不存在 LPUSHX失败
(integer) 0
127.0.0.1:6379> LPUSHX list v1 v2
(integer) 0
127.0.0.1:6379> LPUSHX mylist k4 k5 # 向mylist中 左边 PUSH k4 k5
(integer) 5
127.0.0.1:6379> LRANGE mylist 0 -1
1) "k5"
2) "k4"
3) "k2"
4) "k1"
5) "k3"
---------------------------LINSERT--LLEN--LINDEX--LSET----------------------------
127.0.0.1:6379> LINSERT mylist after k2 ins_key1 # 在k2元素后 插入ins_key1
(integer) 6
127.0.0.1:6379> LRANGE mylist 0 -1
1) "k5"
2) "k4"
3) "k2"
4) "ins_key1"
5) "k1"
6) "k3"
127.0.0.1:6379> LLEN mylist # 查看mylist的长度
(integer) 6
127.0.0.1:6379> LINDEX mylist 3 # 获取下标为3的元素
"ins_key1"
127.0.0.1:6379> LINDEX mylist 0
"k5"
127.0.0.1:6379> LSET mylist 3 k6 # 将下标3的元素 set值为k6
OK
127.0.0.1:6379> LRANGE mylist 0 -1
1) "k5"
2) "k4"
3) "k2"
4) "k6"
5) "k1"
6) "k3"
---------------------------LPOP--RPOP--------------------------
127.0.0.1:6379> LPOP mylist # 左侧(头部)弹出
"k5"
127.0.0.1:6379> RPOP mylist # 右侧(尾部)弹出
"k3"
---------------------------RPOPLPUSH--------------------------
127.0.0.1:6379> LRANGE mylist 0 -1
1) "k4"
2) "k2"
3) "k6"
4) "k1"
127.0.0.1:6379> RPOPLPUSH mylist newlist # 将mylist的最后一个值(k1)弹出,加入到newlist的头部
"k1"
127.0.0.1:6379> LRANGE newlist 0 -1
1) "k1"
127.0.0.1:6379> LRANGE mylist 0 -1
1) "k4"
2) "k2"
3) "k6"
---------------------------LTRIM--------------------------
127.0.0.1:6379> LTRIM mylist 0 1 # 截取mylist中的 0~1部分
OK
127.0.0.1:6379> LRANGE mylist 0 -1
1) "k4"
2) "k2"
# 初始 mylist: k2,k2,k2,k2,k2,k2,k4,k2,k2,k2,k2
---------------------------LREM--------------------------
127.0.0.1:6379> LREM mylist 3 k2 # 从头部开始搜索 至多删除3个 k2
(integer) 3
# 删除后:mylist: k2,k2,k2,k4,k2,k2,k2,k2
127.0.0.1:6379> LREM mylist -2 k2 #从尾部开始搜索 至多删除2个 k2
(integer) 2
# 删除后:mylist: k2,k2,k2,k4,k2,k2
---------------------------BLPOP--BRPOP--------------------------
mylist: k2,k2,k2,k4,k2,k2
newlist: k1
127.0.0.1:6379> BLPOP newlist mylist 30 # 从newlist中弹出第一个值,mylist作为候选
1) "newlist" # 弹出
2) "k1"
127.0.0.1:6379> BLPOP newlist mylist 30
1) "mylist" # 由于newlist空了 从mylist中弹出
2) "k2"
127.0.0.1:6379> BLPOP newlist 30
(30.10s) # 超时了
127.0.0.1:6379> BLPOP newlist 30 # 我们连接另一个客户端向newlist中push了test, 阻塞被解决。
1) "newlist"
2) "test"
(12.54s)
小结
- list实际上是一个链表,before Node after , left, right 都可以插入值
- 如果key不存在,则创建新的链表
- 如果key存在,新增内容