前言:
📕作者简介:热爱编程的敖云岚,致力于C、Java、Python等多编程语言,热爱编程和长板的运动少年!
📘相关专栏:Java基础语法,JavaEE初阶,数据库,数据结构和算法系列等,大家有兴趣的可以看一看。️😇有兴趣的话关注博主一起学习,一起进步吧!😇
一、Redis预备工作
1.1启动Redis
[root@localhost redis]# redis-server /etc/redis/redis.conf
1.2停止Rudis
先查看到 redis-server 的 pid:
[root@localhost redis]# netstat -anp | grep redis
然后通过 kill 命令直接杀死 redis 进程:
kill 进程id
Redis:是一个客户端服务器结构的程序
二、Redis 常见数据类型
2.1Redis两个最核心的命令
get 根据key来取value
set 把key和value存储进去
首先进入redis客户端:
[root@localhost redis]# redis-cli
代码示例:
127.0.0.1:6379> set key1 value1
OK
127.0.0.1:6379> set key2 value2
OK
# 对于上述的key 和 value 不需要加引号,表示字符串的类型
# 当然,加上单引号或者双引号也是可以的
# redis 中的命令不区分大小写
# get 命令直接输入key就能得到value
# 如果当前key不存在,会返回nil(和null/NULL是一个意思)
127.0.0.1:6379> get key1
"value1"
127.0.0.1:6379> get key2
"value2"127.0.0.1:6379> get key5
(nil)
# 对于上述的key 和 value 不需要加引号,表示字符串的类型
# 当然,加上单引号或者双引号也是可以的
# redis 中的命令不区分大小写
2.2 基本全局命令
Redis是键值对结构。key固定就是字符串,value会存在多种类型,如字符串、哈希、列表、集合等等。全局命令:就是能够搭配任意一种数据结构来使用的命令
KEYS:返回所有满足样式(pattern)的 key。
pattern : 表示包含特殊符号的字符串(字符串会通过一些特殊符号,如通配符,来描述key的模样)
# 背景示例
127.0.0.1:6379> set hallo 1
OK
127.0.0.1:6379> set hbllo 1
OK
127.0.0.1:6379> set hcllo 1
OK
127.0.0.1:6379> set hdllo 1
OK
127.0.0.1:6379> set hello 1
OK
127.0.0.1:6379> set hpppppllo 1
OK
- ? 匹配任意一个字符
127.0.0.1:6379> keys h?llo
1) "hcllo"
2) "hdllo"
3) "hbllo"
4) "hello"
5) "hallo"
- * 匹配0个或任意多个字符
127.0.0.1:6379> keys h*llo
1) "hpppppllo"
2) "hcllo"
3) "hdllo"
4) "hbllo"
5) "hello"
6) "hallo"
- [abcde] 只能匹配到 a b c d e ,给出固定选项
127.0.0.1:6379> keys h[ae]llo
1) "hello"
2) "hallo"
- [^e] 排除 e ,只有e匹配不了
127.0.0.1:6379> keys h[^abcd]llo
1) "hello"
- [a-b] 匹配 a - b 这个范围内的字符,包含两侧边界
127.0.0.1:6379> keys h[a-q]llo
1) "hcllo"
2) "hdllo"
3) "hbllo"
4) "hello"
5) "hallo"
注意事项:
(1.) keys 的时间复杂度为:O(N)
所以在生产环境上一般会禁止使用keys命令,尤其是 keys * 。由于生产环境上的key可能会非常多!而Redis是一个单线程的服务器,执行keys *的时间会非常长,导致redis服务器阻塞,从而一影响到给其他客户端提供服务,这样的后果是灾难性的!
情节严重会丢掉年终奖或者自己的工作!!!
EXISTS:判断某个 key 是否存在。
127.0.0.1:6379> keys *
1) "hdllo"
2) "hallo"
3) "hcllo"
4) "hbllo"
5) "hello"
127.0.0.1:6379> exists key hallo
(integer) 1
127.0.0.1:6379> exists key hfllo
(integer) 0
时间复杂度:O(1)
返回值:key 存在的个数。(不存在返回0)
DEL:删除指定的 key。
127.0.0.1:6379> del key hallo
(integer) 1
127.0.0.1:6379> keys *
1) "hdllo"
2) "hcllo"
3) "hbllo"
4) "hello"
时间复杂度:O(1)
返回值:删除掉的 key 的个数。
EXPIRE:为指定的 key 添加秒级的过期时间(Time To Live TTL)
(单位为秒)
127.0.0.1:6379> keys *
1) "hdllo"
2) "hcllo"
3) "hbllo"
4) "hello"
# 指定hdllo有效时间为3秒
127.0.0.1:6379> expire hdllo 3
(integer) 1
127.0.0.1:6379> keys *
1) "hcllo"
2) "hbllo"
3) "hello"
# 3秒后过期
时间复杂度:O(1)
返回值:1 表示设置成功。0 表示设置失败。
TTL:获取指定 key 的过期时间,秒级。
127.0.0.1:6379> expire hcllo 10
(integer) 1
# 获取剩余有效时间
127.0.0.1:6379> ttl hcllo
(integer) 4
127.0.0.1:6379> ttl hcllo
(integer) 1
127.0.0.1:6379> ttl hcllo
(integer) -2
时间复杂度:O(1)
返回值:剩余过期时间。-1 表示没有关联过期时间,-2 表示 key 不存在。
EXPIRE 和 TTL 命令都有对应的支持毫秒为单位的版本:PEXPIRE 和 PTTL,详细用法就不再介绍了。
TYPE:返回 key 对应的 value 的数据类型。
# 新增键值对:默认为字符串
redis> SET key1 "value"
"OK"
# value为链表
redis> LPUSH key2 "value"
(integer) 1
# value为set类型
redis> SADD key3 "value"
(integer) 1
# 查看key对应的value的数据类型
redis> TYPE key1
"string"
redis> TYPE key2
"list"
redis> TYPE key3
"set"
2.3 面试题
2.3.1 redis的key的过期策略是怎么实现的?
一个 reids 中可能同时存在很多 key , 这些 key 中有可能一大部分都有过期时间。此时 redis 服务器如何知道哪些 key 已经过期要被删除,哪些 key 还没过期?
redis 的策略是:两种方式相结合
(1.)定期删除
每次抽取一部分进行验证过期时间,保证抽取检查的过程足够快!因为 redis 是单线程的程序,如果扫描 key 的消耗的时间太多,就可能导致正常处理请求命令收到阻塞!
(2.)惰性删除
假设这个key已经到了过期时间,但是暂时还没有进行删除。当用户后面进行访问时正好用到这个key , 这次访问会让 redis 服务器触发删除 key 的操作,同时返回nil(空)。
2.3.2多线程的两种定时器模式
2.3.2.1 定时器
(1.)定时器:在某个时间到达之后,执行指定的任务。
(2.)原理:基于优先级队列/堆(过期时间越早,优先级越高)。
(3.)过程:队首元素是最早要过期的 key 。此时定时器中只要分配一个线程,让这个线程去检查队首元素,查看是否过期。如果队首元素还没过期,后续元素一定没过期。此时扫描线程不需要遍历所有的key,只要关注队首元素即可。另外,在扫描线程检查队首元素过期时间的时候,不能进行频繁的检察。此时就可以根据当前时刻和队首元素的过期时间设置一个等待时间,当时间接近时,系统再唤醒这个线程,节省了CPU的开销。若中途出现新任务添加,只需要唤醒刚才的线程检查一下队首元素进行重新调整阻塞时间即可。
2.3.2.2 基于时间轮实现的定时器
(1.)时间轮:把时间划分成很多小段(划分的粒度,看实际需求)
(2.)图论理解: