一、简介
redis能干嘛
- 内存存储、持久化。内存中是断电即失,所有持久化很重要(持久化包括rdb、aof)
- 效率高,可以用于高速缓存。(如:可以将最新的10条评论的ID放在Redis的List集合里面)
- 发布订阅系统。
- 地图信息分析
- 计数器、计数器(浏览量)
Redis特性
- 多样化的数据类型
- 持久化
- 集群
- 事务
计算向数据偏移
二、入门
2.1 安装
2.2 配置
配置文件redis.config
中的属性
daemonize
redis.conf配置文件中daemonize守护线程,默认是NO
daemonize是用来指定redis是否要用守护线程的方式启动。
- daemonize:yes
redis采用的是单进程多线程的模式。当redis.conf中选项daemonize设置成yes时,代表开启
守护进程模式。在该模式下,redis会在后台运行,并将进程pid号写入至redis.conf选项
pidfile设置的文件中,此时redis将一直运行,除非手动kill该进程。
- daemonize:no
当daemonize选项设置成no时,当前界面将进入redis的命令行界面,exit强制退出或者关闭
连接工具(putty,xshell等)都会导致redis进程退出。
2.3 基本数据库常识
默认16个数据库,类似数组下标从零开始,初始默认使用零号库
在redis的配置文件中:
使用select切换数据库
127.0.0.1:6379> select 3 # 切换数据库
OK
127.0.0.1:6379[3]> dbsize # 查看数据库大小
(integer) 0
127.0.0.1:6379[3]> set name dong
OK
127.0.0.1:6379[3]> dbsize
(integer) 1
2.3 redis是单线程的,为什么这么快?
误区1: 高性能的服务器一定是多线程吗?
核心:redis是将所有数据放在内存中的,所以用单线程去操作效率就是最高的。
多线程会涉及到CPU的上下文切换,这是要耗时间的。对于内存来说,没有上下文切换效率就是最高的。
- redis是单线程的,所以是线程安全的。
三、基本命令
getset
:先get,再set
keys *
: 查看当前数据库所有的key
flush
:
Select
命令切换数据库
Dbsize
查看当前数据库的key的数量
Flushdb
:清空当前库
Flushall
:清空全部的库
expire
:给值设置过期时间,例:expire name 10
ttl
:查看过期时间。例子:ttl name
,结果为-2代表已经过期,-1代表永不过期
type
: 查看key的类型
四、五大数据类型
1. String
set name
:设置key-value一组值(单一的键值对,key重复时,覆盖原来的value)
get name
:获取指定key的value
del key的名称(可以是多个)
:删除指定key的值
append
:向字符串追加内容, append key hello
strlen
: 获取某个字符串的长度
incr
:给key值加1(数字才行)
decr
:给key值减1(数字才行)
increby
:有步长的加,incrby views 10
decreby
:有步长的减,decrby views 10
getrange
:截取字符串,类似于substring,但是这里是含头含尾
getrange key1 0 -1
:是获取全部字符串,类似于get key
setrange
:从某个下标开始替换字符
setex
(set with expire):设置值并且有过期时间
setnx
(set if not exists):不存在则创建(在分布式锁中会经常使用),存在则不会创建
mset
:批量创建,键 值 键 值
的形式
mget
: 批量获取
msetnx
:批量的如果不存在则创建 (这是一个原子性操作,如果其中有一个已经存在,那么都不会创建成功,会返回0)
- 对象
user:{id}:filed
:设置的是一个对象,值为json字符串来保存一个对象
比如设置一个user:1
对象:
mset user:1:name zhangsan user:1:age 13
获取时:
2. list
列表,一个key对应多个value
-
LPUSH key values(多个值)
:创建集合设置一组值
注意:使用lpush
指令向集合中添加元素时,添加顺序与显示顺序相反,
Lpush
:将一个或多个值插入到列表头部。(左) -
RPUSH key values(多个值)
:创建集合设置一组值
注意:使用RPUSH
指令向集合中添加元素时,添加顺序与显示顺序相同
Rpush
:将一个或多个值插入到列表尾部。(右) -
LRANGE key 起始下标 结束下标
:获取集合元素
LRANGE key 0 -1
表示获取全部 -
Lindex
:按照索引下标获得元素(-1代表最后一个,0代表是第一个)
Lindex 0
:表示获取第一个元素 -
LLEN key
:获取指定集合的长度,当列表 key 不存在时,返回 nil -
LPOP key
:移除集合中的第一个元素,返回值为移除的元素。 -
Ltrim key
对一个列表进行修剪(trim),就是说,让列表只保留指定区间内的元素,不在指定区 间之内的元素都将被删除。
性能总结 -
它是一个字符串链表,left,right 都可以插入添加
-
如果键不存在,创建新的链表
-
如果键已存在,新增内容
-
如果值全移除,对应的键也就消失了
-
链表的操作无论是头和尾效率都极高,但假如是对中间元素进行操作,效率就很惨淡了。
3. Set
与list类似,但不重复,无序
-
SADD key 元素(可以是多个元素)
:向集合中添加元素,set集合无序,元素不可重复 -
SMEMBERS key
:查看集合中元素 -
SCARD key
:返回集合长度 -
SREM key
:删除集合中指定的元素 -
SPOP key 元素个数
:随机删除集合中指定个数的元素 -
SMOVE key1 key2 元素
:将某一个集合中的元素移动到另一个集合,如果说目标集合(目标key2)不存在,则会创建集合接收移动过来的元素 -
SDIFF 差集
-
SINTER 交集
-
SUNION 并集
4. 有序集合ZSet
在set基础上,加一个score值,根据score值排序。之前set是k1 v1 v2 v3,现在zset是 k1 score1 v1 score2 v2
-
ZADD vips 500 v1 1000 v2 1500 v3 2000 v4
:添加有序集合
-
ZRANGE vips 0 -1
:查询所有value
-
ZRANGE vips 0 -1 withscores
:查询结果带着分数
-
ZRANGEBYSCORE vips 500 1500
:返回有序集合中指定分数区间的成员列表。有序集成员按分数值递增(从小到大) 次序排列。
-
ZREM vips v4
:删除元素v4 -
ZCARD vips
:统计元素个数 -
ZCOUNT vips 500 2000
:统计分数区间的元素的个数 -
ZRANK vips v2
:统计元素的下标,如果元素不存在返回空值 -
ZSCORE vips v3
:查看元素的分数
5. Hash
用来描述对象类型的,也可以理解成key—value中的value也是一个键值对
-
HSET key filed value
:添加一组键值对 指定对象(key)的指定属性(filed)设置值(value) -
HGET key field
:获取对象的属性值 -
HMSET key field1 value1...
:同时为一个对象指定多个属性值 -
HMGET key field
…:同时获取对象的多个属性值 -
HGETALL key
:获取对象的全部属性及值 -
HDEL key field...
:删除对象的属性(一个或多个) -
HLEN key
:返回对象的属性个数 -
HEXISTS key field
:判断当前key中是否有指定名称(field)的键值对 -
HKEYS key
:获取对象的全部属性 -
HVALS key
:获取对象全部属性的值 -
HINCRBY key field 值
:为当前的数字属性增加值(可以是负数,表示减少) -
HINCRBYFLOAT key field 值
:为当前的数字属性增加值(小数) -
HSETNX key field value
:添加值(同hset),重复元素不覆盖
五、java操作redis
六、Spring整合Redis
七、SpringBoot整合Redis
八、Redis的事务
8.1 理论
-
Redis事务的概念:
Redis 事务的本质是一组命令的集合。事务支持一次执行多个命令,一个事务中所有命令都会被序列化。在事务执行过程,会按照顺序串行化执行队列中的命令,其他客户端提交的命令请求不会插入到事务执行命令序列中。
总结说:redis事务就是一次性、顺序性、排他性的执行一个队列中的一系列命令。 -
Redis事务没有隔离级别的概念:
批量操作在发送 EXEC 命令前被放入队列缓存,并不会被实际执行!
-
Redis不保证原子性:
Redis中,单条命令是原子性执行的,但事务不保证原子性,且没有回滚。事务中任意命令执行失败(运行时错误),其余的命令仍会被执行。
-
Redis事务的三个阶段:
- 开始事务(
multi
) - 命令入队
- 执行事务
- 开始事务(
-
Redis事务相关命令:
watch key1 key2 ...
:监视一或多个key,如果在事务执行之前,被监视的key被其他命令改动,则 事务被打断 ( 类似乐观锁 )
multi
:开启事务
exec
:执行所有事务块的命令 ( 一旦执行exec后,之前加的监控锁都会被取消掉 )
discard
:放弃事务,放弃事务后事务队列中的事务都不会执行了
watch
:监控
- 错误
- 若在事务队列中存在命令性错误(类似于java编译性错误,语法错误),则执行EXEC命令时,所有命令都不会执行
- 若在事务队列中存在逻辑性错误(类似于java的1/0的运行时异常),则执行EXEC命令时,其他正确
命令会被执行,错误命令抛出异常。
8.2 案例
- 执行案例
127.0.0.1:6379[1]> multi # 开启事务
OK
127.0.0.1:6379[1]> set k1 v1 # 命令
QUEUED # 入队
127.0.0.1:6379[1]> keys *
QUEUED
127.0.0.1:6379[1]> select 0
QUEUED
127.0.0.1:6379> keys *
QUEUED
127.0.0.1:6379> EXEC # 执行事务
1) OK # 从开启事务后的第一个命令开始,依次执行的结果
2) 1) "k1"
3) OK
4) 1) "jedisDemo"
2) "\xac\xed\x00\x05t\x00\x04key1"
3) "age"
4) "set1"
5) "\xac\xed\x00\x05t\x00\x06person"
6) "set2"
7) "person1"
8) "person"
9) "set4"
10) "\"personSerializable\""
11) "list"
12) "set3"
13) "vips"
14) "name1"
15) "personJson"
16) "lsit"
127.0.0.1:6379>
- 取消执行案例
127.0.0.1:6379[1]> multi
OK
127.0.0.1:6379[1]> set k2 v2
QUEUED
127.0.0.1:6379[1]> set k3 v3
QUEUED
127.0.0.1:6379[1]> set k4 v4
QUEUED
127.0.0.1:6379[1]> DISCARD # 取消事务
OK
127.0.0.1:6379[1]> keys *
1) "k1"
127.0.0.1:6379[1]>
8.3 Watch
监控
使用watch
可以当做Redis的乐观锁操作
-
悲观锁
悲观锁(Pessimistic Lock),顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿到这个数据就会block直到它拿到锁。传统的关系型数据库里面就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在操作之前先上锁。 -
乐观锁
乐观锁(Optimistic Lock),顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁。但是在更新的时候会判断一下再此期间别人有没有去更新这个数据,可以使用版本号等机制,乐观锁适用于多读的应用类型,这样可以提高吞吐量,乐观锁策略:提交版本必须大于记录当前版本才能执行更新
使用watch
监控变量
# 窗口一
127.0.0.1:6379> watch balance OK
127.0.0.1:6379> MULTI
127.0.0.1:6379> decrby balance 20 QUEUED
127.0.0.1:6379> incrby debt 20 QUEUED
127.0.0.1:6379> exec # 执行之前,执行窗口二代码测试 OK
(nil) # 修改失败! (nil)
# 窗口二
127.0.0.1:6379> get balance
"80"
127.0.0.1:6379> set balance 200
OK
可以看出来,在窗口一中监视balance后,开启事务修改数据,在exec之前,窗口二对数据进行了修改,此时窗口一则会执行失败
然后可以使用unwatch
取消监控(但是好像无论事务是否执行成功,redis都会自动取消监控)