[Redis] Redis基本命令与数据类型+单线程模型

🌸个人主页:https://blog.csdn.net/2301_80050796?spm=1000.2115.3001.5343

🏵️热门专栏:

🧊 Java基本语法(97平均质量分)https://blog.csdn.net/2301_80050796/category_12615970.html?spm=1001.2014.3001.5482

🍕 Collection与数据结构 (92平均质量分)https://blog.csdn.net/2301_80050796/category_12621348.html?spm=1001.2014.3001.5482

🧀线程与网络(96平均质量分) https://blog.csdn.net/2301_80050796/category_12643370.html?spm=1001.2014.3001.5482

🍭MySql数据库(93平均质量分)https://blog.csdn.net/2301_80050796/category_12629890.html?spm=1001.2014.3001.5482

🍬算法(97平均质量分)https://blog.csdn.net/2301_80050796/category_12676091.html?spm=1001.2014.3001.5482

🍃 Spring(97平均质量分)https://blog.csdn.net/2301_80050796/category_12724152.html?spm=1001.2014.3001.5482

🎃Redis(96平均质量分)https://blog.csdn.net/2301_80050796/category_12777129.html?spm=1001.2014.3001.5482

感谢点赞与关注~~~

目录

  • [1. Redis常见指令](#1. Redis常见指令)
  • [2. redis中的常见数据类型](#2. redis中的常见数据类型)
    • [2.1 常见的数据类型](#2.1 常见的数据类型)
    • [2.2 对数据类型的优化](#2.2 对数据类型的优化)
  • [3. Redis中的单线程模型](#3. Redis中的单线程模型)

1. Redis常见指令

Redis中有上百个指令,但是我们只要记住常见的指令即可,剩下的指令如果不记得,可以去官方网站的文档上查阅即可.

  • Set
    set key value
    我们知道,Redis是以键值对的方式来存储数据的,set用来在Redis中设置键值对,把key和value存入.

    其中redis中的value可以支持多种类型的数据结构.
  • get
    get key
    可以根据key获取到对应的value.
  • keys
    查询当前服务器上符合条件的所有keys.在keys操作中,我们引入了通配符,通过通配符来描述通配符的模样,就可以把key查询出来.类似与MySQL中的模糊查询.
    keys patten
    其中patten中的通配符可以有多重类型.
    • ? 匹配任意一个字符
    • *匹配0个或任意多个字符
    • [abc]只能匹配abc三个字符中的任意一个字符,其他字符不可以匹配
    • [^e]除了e不能匹配,其他的任意一个字符都可以匹配.
    • [a-c]只能匹配a~c之间的任意一个字符,其他的字符都不可以匹配.

我们给Redis中设置一些key和value

shell 复制代码
127.0.0.1:6379> set hello 1
OK
127.0.0.1:6379> set hallo 2
OK
127.0.0.1:6379> set hbllo 3
OK
127.0.0.1:6379> set heeeeeeeeello 4
OK
127.0.0.1:6379> set hpllo 5
OK

我们接下来使用keys指令进行查询

shell 复制代码
127.0.0.1:6379> keys *
1) "heeeeeeeeello"
2) "hello"
3) "hbllo"
4) "hpllo"
5) "hallo"
6) "key1"
127.0.0.1:6379> keys h?llo
1) "hello"
2) "hbllo"
3) "hpllo"
4) "hallo"
127.0.0.1:6379> keys h*llo
1) "heeeeeeeeello"
2) "hello"
3) "hbllo"
4) "hpllo"
5) "hallo"
127.0.0.1:6379> keys h[a-c]llo
1) "hbllo"
2) "hallo"
127.0.0.1:6379> keys h[^a]llo
1) "hello"
2) "hbllo"
3) "hpllo"
127.0.0.1:6379> keys h[o-p]llo
1) "hpllo"
127.0.0.1:6379> keys h[abc]llo
1) "hbllo"
2) "hallo"

注意事项\] keys命令的时间复杂度是O(N),也就是把redis服务器中的所有key全部都遍历一遍,所以在公司生产环境的服务器上,我们严禁使用`keys *`这样的指令.**这时候就会使得其他客户端的redis指令超时(发生阻塞)**,超时之后,就会去查询MySQL数据库,这时候MySQL就会收到一大波请求,使得MySQL搓手不及,就容易把MySQL搞挂. > 拓展知识: 企业中的几种环境 > > 1. 办公环境: 办公环境就是公司给你发的那台电脑的本机操作系统的环境. > 2. 开发环境: 在项目未上线之前,我们需要对一款软件进行开发,我们在开发的时候所在的操作系统的环境叫做开发环境.有些情况下,我们会使用本机的环境进行开发,也就是办公环境和开发环境是一样的,但是很多时候,公司也有专门的开发环境的服务器.有些情况下,一些软件的业务逻辑比较复杂,这时候本机环境是带动不起来的,就需要性能更高的服务器来带动. > 3. 测试环境: 测试工程师用来测试项目的操作系统环境. > 4. 生产环境(线上环境): 开发完成之后项目上线,部署到生产环境上之后,项目就可以用来给用户提供服务,为外界用户直接提供服务的环境叫做生产环境,这个环境外界用户可以直接访问到.生产环境会直接影响到公司的营收. * exists `exists key [key...]` 判断一个key是否存在于服务器上.\*\*一次可以查询一个key,也可以查询多个key.最终返回的是存在key的个数,查询的时间复杂度是O(1) ```shell 127.0.0.1:6379> exists hello (integer) 1 127.0.0.1:6379> exists hello hallo (integer) 2 ``` > 一般在想要查询多个key是否存在的时候,我们一般不使用分开多次查询的做法,我们一般使用写在一起的写法.这是因为,这其中每一次查询就是一次网络请求,网络会对数据报进行封装和分用,这其中封装和分用会占用网络资源,降低性能,所以我们建议把查询的数据放在一次网络请求中全部查询完成.\*\*其中除了这个操作之外,redis中还有好多指令都支持一次操作多个key的操作. * del `del key [key...]` 删除一个key或者是多个key. ```shell 127.0.0.1:6379> del key1 hallo (integer) 2 127.0.0.1:6379> del hpllo (integer) 1 127.0.0.1:6379> keys * 1) "heeeeeeeeello" 2) "hello" 3) "hbllo" ``` > 这里注意,在redis中删除键值对并不想没有MySQL中那样危险,即使不小心误删了之后依然可以从MySQL中同步过来,但是redis一次不可以删除大量的数据,这样在从MySQL恢复数据的时候,就会向MySQL发送大量请求,很容易把MySQL搞挂. * expire `expire key seconds` 给一个key设置过期时间,其中时间是秒级别.这个key必须在redis中存在.在给一个key设置过期时间之后,这个key就会在指定的时间之后过期. > 如果想设置毫秒级别,可以用`pexpire key 时间`来设置. ```shell 127.0.0.1:6379> set key1 value1 OK 127.0.0.1:6379> EXPIRE key1 10 (integer) 1 127.0.0.1:6379> get key1 "value1" 127.0.0.1:6379> get key1 (nil) ``` 这个功能使用的场景非常广泛,比如我们常用的手机验证码,还有美团优惠劵的到期时间,都是使用了redis的过期时间. * ttl `ttl key` 获取指定key的过期时间,时间为秒级,如果想获取毫秒级别的,可以使用`pttl`. **其中,如果key不存在过期时间的时候,返回-1,如果没有找到这个key,返回-2** ```shell 127.0.0.1:6379> keys * 1) "heeeeeeeeello" 2) "hello" 3) "hbllo" 127.0.0.1:6379> ttl hello (integer) -1 127.0.0.1:6379> set key1 val1 OK 127.0.0.1:6379> EXPIRE key1 10 (integer) 1 127.0.0.1:6379> ttl key1 (integer) 5 127.0.0.1:6379> ttl key1 (integer) -2 ``` \[常考面试题\] redis中的key的过期时间是基于什么来实现的呢? redis中有好多的键值对,redis怎么知道哪个过期了,哪个没有过期呢?绝对不是一个一个的遍历来实现的,这样的效率非常低下.redis采用了一下的策略: 1. 惰性删除 假设一个key已经到了过期的时间,**但是我们先不要删除这个key,当下次访到这个key之后,查看这个key是否过期**,如果过期了,就删除掉. 2. 定期删除 redis会对过期的key进行定期删除,但是这里的删除并不是吧key全部遍历之后在找到过期的key进行删除,**而是抽取一些key来判断是否过期**,之后进行对应的删除. 3. 内存淘汰策略 我们后面再详细介绍. > 举例说明: 去商店买东西 > > 当我们拿了一件商品之后,在结账的时候,我们发现这件商品过期了,这时候我们会告诉老板这件商品过期了,老板这时候就会把这件商品扔掉,这就是惰性删除.其次老板会定期抽取一部分的商品来查看商品是否过期,过期就会人掉,这就是定期删除. \[注意事项\] 网上有很多人说redis的过期机制是定时删除,在redis中有一个定时器,定时器到达时间之后,redis就会将之删除,但是我们注意,redis并没有采取这种删除的方式. > 拓展: 比较高效的定时器实现方式 > > 1. 优先级队列方式 > 这个方式我们在之前多线程的部分曾经向大家介绍过. > 首先我们按照key的过期时间把这些数据组成一个优先级队列,过期时间越短,优先级越高,之后就会有一个线程去不停地检查队首的元素,只要队首的元素到达过期时间,我们就把队首结点和最后一个结点进行交换,之后删除最后一个结点,之后再对这个优先级队列进行向下调整. > 当然该线程也不是一直在对一个队首的元素进行扫描,这样就会出现忙等的状态,我们会根据过期时间设置一个线程休眠的时间,在时间差不多的时候,就会检查一次队首的元素是否过期,之后如果等待的中间来了新的请求,就唤醒一次线程,顺便检查一下队首的元素是否过期. > 2. 基于时间轮的方式实现定时器 > 我们可以把时间轮上的时间划分为多个小段(具体的划分粒度,看具体的业务场景),其中每一个小段上都挂着一个链表,这个链表中是需要执行的任务,时间轮上有一个指针,指针每隔一段时间就需要向下走一格.没走一个,就要检查链表中有没有需要执行的任务. > ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/d1511eb90e4946e3a5acc9bf5855703c.png) * type `type key` 获取该key对应value的数据类型.虽然redis中的key只有String类型,但是value可以有多种类型.常见的数据类型有:string ,list ,set ,zset ,hash, stream,如果没有这个key的时候,返回none.具体如何创建不同的数据类型,我们后面再详细介绍. ```shell 127.0.0.1:6379> type hello string 127.0.0.1:6379> type hallo none ``` ## 2. redis中的常见数据类型 ### 2.1 常见的数据类型 redis目前支持的数据类型有10种,常用的数据结构有5种,剩下的5种事针对特殊场景的特殊的数据结构. ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/939a1bbb34484090a3051e20a2be4ed8.png) 上面的几种数据类型,首先是字符串,对应的是我们java中的String,其次Hash表对应的是java中的HashMap,列表对应的是Java中的List,集合对应的是Java中的Set,有序集合比较特殊,其中除了存储成员之外,还会存储该成员(member)的权重(score),有点像Java中的PriorityQueue. ### 2.2 对数据类型的优化 有时候redis在实现这些数据结构的时候,底层并不一定使用的是相应的数据结构,会在对应的数据结构上视情况进行一些优化.但是redis承诺,你在用起来的时候,和原来的数据结构是一样的效果. 比如我们现在创建的一个key其中的数据结构是Hash表,但是在数据比较少的时候,也在底层也不一定使用的是Hash表来实现的,但是我们用起来和Hash表没有什么区别,时间复杂度都是O(1). > 这和前段时间的"指鼠为鸭"事件有些相似,虽然说外表长得不太像鸭脖,但是商家承诺,这个"鸭脖"吃起来和正常的"鸭脖"没有什么区别 具体优化情况如下: ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/9fae2adb9bda4a958df71340eacc322b.png) * raw 这个是最基本的字符串,类似与Java中的byte\[ \]数组. * int redis有时会实现一些计数的功能,当value是整数的时候,redis就会直接用int类型来保存. * embstr 当字符串比较短的时候,redis就会把字符串优化为embstr. * hashtable 最基本的hash表,这里不要误会,这和Java中那个线程安全的HashTable不一样. * ziplist 当hash表中的元素比较少的时候,会被压缩为ziplist. * linkedlist 链表 * ziplist 压缩列表,但是在redis3.2之后就引入了quicklist,它兼顾了linkedlist和ziplist两者的优点. * intset 当set中存储的都是整数的时候,会被优化为intset. * skiplist 跳表,这个跳表有点类似与链表中的复杂链表,结点中存储着下一个结点的引用,然后还存储着随机一个结点的引用. 我们可以使用`object encoding`来查看value中存储数据的实际类型. ```shell 127.0.0.1:6379> set key1 1 OK 127.0.0.1:6379> object encoding key1 "int" ``` ## 3. Redis中的单线程模型 redis中的单线程模型指的就是,redis在处理命令的时候,都只使用一个线程来处理,说redis只有单线程,其实也有使用多线程的地方,在网络io中使用的就是多路复用的情况. 如果redis不使用单线程的情况下,就会出现线程安全问题.假如有两个客户端在连接redis服务器,他们同时向redis服务器发送了一个incr请求. ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/04bc23750f2640ae819d457e046a9503.png) 这时候就会出现两个客户端同时对一个变量进行++的操作.客户端先获取到服务器的value值,两个线程都获取到的是0,在线程中++之后都是1,之后再把value++之后的值赋值给value,此时两个线程++之后的值都是1,赋值之后也自然就是1. 幸好我们的redis服务器是单线程模式的,它保证了这两个客户端发送过来的请求是串行化执行的.宏观上redis同时处理了客户端的请求,微观上是串行化执行的. ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/42f4e23364fe4e34b4f2027df2c6f7c7.png) ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/50cfaa0c54ad42628bb4eda349faca1e.png) > 就像我们在食堂打饭一样,假如说一个食堂只有一个窗口开放,这时候学生和老师就不得不排队一个一个的打饭. 因为redis是使用单线程模型的,所以我们在使用redis指令的时候要额外小心,避免一个命令长期占用redis服务器的线程. \[面试题\] 既然redis是单线程的,为什么redis还是如此之快呢? * 首先,redis是在内存中存储数据的,内存中存储的数据要相对于硬盘中要快好几个数量级. * 其次,redis的业务功能主要突出的就是一个"短平快".业务逻辑比较简单.所以比较快. * 既然说redis是单线程的,它也避免了多线程中的线程竞争现象,线程竞争的现象会拖慢执行的效率. * 处理网络请求io的时候,使用的是epoll多路复用的机制.就是一个线程管理了多个Socket.

相关推荐
阿里云大数据AI技术25 分钟前
云栖实录|MaxCompute全新升级:AI时代的原生数据仓库
大数据·数据库·云原生
不剪发的Tony老师1 小时前
Valentina Studio:一款跨平台的数据库管理工具
数据库·sql
weixin_307779131 小时前
在 Microsoft Azure 上部署 ClickHouse 数据仓库:托管服务与自行部署的全面指南
开发语言·数据库·数据仓库·云计算·azure
六元七角八分1 小时前
pom.xml
xml·数据库
Achou.Wang1 小时前
源码分析 golang bigcache 高性能无 GC 开销的缓存设计实现
开发语言·缓存·golang
虚行1 小时前
Mysql 数据同步中间件 对比
数据库·mysql·中间件
奥尔特星云大使2 小时前
mysql读写分离中间件Atlas安装部署及使用
数据库·mysql·中间件·读写分离·atlas
牛马baby2 小时前
【mysql】in 用到索引了吗?
数据库·mysql·in
杀气丶2 小时前
L2JBR - 修复数据库编码为UTF8
数据库·sql·oracle
-Xie-2 小时前
Mysql杂志(三十)——索引失效情况
数据库·mysql