目录
- 常用数据结构(类型)
- Redis单线程模型
- Reids为啥效率这么高?速度这么快?(参照于其他数据库)
- string
- set
- get
- MSET 和 MGET
- SETNX,SETEX,PSETEX
- incr,incrby,decr,decrby,incrbyfloat
- 其他命令
- 内部编码
- string类型的应用场景
- 计数功能
- 共享会话
- 手机验证码
常用数据结构(类型)
数据结构(类型) | 内部编码 | 描述 |
---|---|---|
string | raw | 最基本的字符串,类似于C++的char数组,或者java的byte数组 |
int | reds通常也可以用来实现一些“计数”这样的功能,当value就是一个整数的时候,此时可能redis会直接使用int来保存 | |
embstr | 针对短字符串进行的特殊的优化 | |
hash | hashtable | 最基本的哈希表 |
ziplist | 压缩列表,一般在哈希表元素比较少的时候可能就优化成ziplist 为啥要压缩?可能某些key的value是hash,此时,如果key特别多,对应的hash也特别多,但是每个hash又不大的情况下,就尽量去压缩,压缩之后就可以让整体占用的内存更小了 | |
list | linkedlist | 链表 |
ziplist | 压缩链表(从redis 3.2开始,引入了新的实现方式quicklist,同时兼顾了linkedlist和ziplist的优点 quicklist就是一个链表,每个元素又是一个ziplist把空间和效率都折中得兼顾到,比较类似于C++的deque(双端队列)) | |
set | hashtable | 哈希表 |
intset | 集合中存的都是整数 | |
zset | skiplist | 跳表,不同于普通的链表,每个节点都有多个指针域,从跳表上查询的元素的时间复杂度是O(logN) |
ziplist | 压缩链表 |
查看key对应的value的实际编码方式
object encoding key
Redis会根据当前的实际情况选择内部的编码方式
Redis单线程模型
- Redis只使用一个线程处理所有的命令请求,但不是说Redis服务器进程内部真的只有一个线程,其实也有多个线程,多个线程是在处理网络IO
假设现在有多个客户端,同时操作一个Redis服务器
当这两个客户端,同时并发的发起上述请求,是否会意味着服务器这边也会存在类似的线程安全问题呢?并不会,Redis服务器是一个单线程模型,保证了当前收到的这么多请求是串行执行的。 在多个请求同时到达Redis服务器,也是要先在队列中排队,再等待Redis服务器一个一个的去处里面的命令再执行,从另一方面看,Redis服务器是串行执行这么多个命令的
注意:Redis能够使用单线程模型很好的工作,原因主要在于redis的核心业务逻辑都是短平快的,消耗cpu资源也就不太吃多核。
Reids为啥效率这么高?速度这么快?(参照于其他数据库)
- Redis访问内存,数据库则是访问硬盘
- Redis核心功能,比数据库的核心功能更简单(数据库对于数据的增删查改这些功能,势必要花费更多的开销)
- 单线程模型,避免了一些不必要的线程竞争开销。
- 处理网络IO的时候,使用了epoll这样的IO多路复用机制
string
redis所有的key都是字符串,value的类型是存在差异的,redis中的字符串,直接就是按照二进制的方式存储的(二进制数据(图片,视频,文本,音频,…)),(不建议存储)音视频的体积可能会比较大,Redis对于string类型,限制了大小的最大是512M。
set
SET key value [expiration EX seconds|PX milliseconds] [NX|XX]
NX:如果key不存在,才设置 ,如果key存在,则不设置(返回nil)
XX:如果key存在,才设置(相当于更新key的value) ,如果key不存在,则不设置(返回nil)
//[]相当于一个独立的单元,表示可选项 可有可无
//set key value ex 10
//相当于 set key value + expire key 10
- 如果key存在,创建新的键值对
- 如果key存在,则是让新的value覆盖旧的value,可能会改变原来的数据类型,原来的这个key的ttl也会失效
get
GET key
//对于GET来说,只是支持字符串类型的value,如果value是其他类型,使用GET就会出错
//例
//127.0.0.1:6379> lpush key3 11 22 33
//127.0.0.1:6379>get key3
(error) WRONGTYPE Operation against akey holding the wrong kind of value
MSET 和 MGET
一次操作多次键值对
MGET key [key ...]
//一次获取对个key的值。如果对应的key不存在或者对应的数据类型不是string,返回nil、
//时间复杂度:O(n) n是key的数量
//返回值:对应value的列表
MSET key value [key value ...]
//一次设置多个键值对
SETNX,SETEX,PSETEX
- setnx:不能存在才设置
- setex:[setex key seconds value] 设置value的同时设置过期时间(秒)
- psetex:[setex key millionseconds value] 设置value的同时设置过期时间(毫秒)
- 以上相当于针对set的一些常见写法,进行了缩写
incr,incrby,decr,decrby,incrbyfloat
- incr:针对value+1
INCR key
//此时的key对应的value必须是整数,如果不是整数会报错
//支持的整数返回为64位的整数,相当于C++中的long long
//返回值:value+1之后的值
//操作的key如果不存在,就会把这个key当做0来使用
- incrby:针对value+n
INCRBY key n //用法和incr一样,n可以是负数
- decr:针对value-1
DECR key // 用法和incr一样
- decrby:针对value-n
DECRBY key //用法和INCRBY一样
- incrbyfloat:针对value +/- 小数
INCRBYFLOAT key floatnum
//把key对应的value进行+-运算,运算的操作数可以是浮点数
上述操作的时间复杂度都是O(1) 由于redis处理命令的时候是单线程模型,多个客户端同时针对同一个key进行incr操作,不会近期“线程安全”问题
其他命令
- append
APPEND key value
//如果key已经存在,并且是一个string,命令会将value追加到原有的string的后面。如果key不存在,则效果等同于set命令
//时间复杂度:O(1)
//返回值:追加完成之后的string的长度(单位是字节,不会对字符编码做处理,如果是中文字符,就按该终端的编码方式来计算,如utf8,一个汉字占3个字节)
//如果要在redis通过get获取中文字符串,需要在redis启动的时候加上一个 --raw这样的选项
//在linux操作的时候,不要乱按ctrl+s(xshell里是冻结当前画面的快捷键)--通过ctrl+q 解冻
- getrange
GETRANGE key start end
//返回值:string类型的字符串,时间复杂度O(n)
//相当于获取字符串的子串 相当于C++中的substr
//在C++中和java中,大多数区间是左闭右开[),但是在redis中 是左闭右闭[start,end]
//正常下标是从0开始的整数,redis的下标是可以支持负数的(表示倒数第几个)
- setrange
SETRANGE key offset value
//返回值:替换之后的新的字符串的长度
//offset为偏移量
//如果当前是中文字符串,进行setrange的时候可能会出现问题
//如果字符串不存在偏移量之前的内容会自动补上0x00 之后内容添加到最后一个0x00的后面
- strlen
STRLEN key
//获取到的字符串的长度 单位是字节
内部编码
字符串类型的内部编码有三种:
- int:8个字节的长整形
- embstr:小于等于39个字节的字符串
- raw:大于39个字节的字符串
查看key对应的value的编码方式
OBJECT encoding key
Redis会根据当前值的类型和长度动态决定内部使用哪种编码方式实现
- 如果某个业务场景,有很多很多的key,类型都是string,但是每个value的string长度都是100左右,如果更关注与整体的内存空间,也可以考虑使用emstr,所以39这个数字只是对于通用的,对于不用的场景,可能会有不同的需求
string类型的应用场景
整体思路:
应用服务器访问数据的时候,先查询redis,如果redis上的数据已经存在了,就直接从redis取数据交给应用服务器,不继续访问数据库了
如果Redis上数据不存在,再读取Mysql,把读到的结果,返回给应用服务器同时,把这个数据也写入到Redis中
但是随着时间的推移,肯定会有越来越多的key在redis上访问不到,从而mysql读取并写入redis,此时数据就越来越多了
所以可与在把数据写给redis的同时,给这个key设置一个过期时间,当然Redis也在内存不足的时候,提供了淘汰策略
计数功能
许多应用都会使用Redis作为计数的基础工具,他可以实现快速计数,查询缓存的功能,同时数据可以异步处理或者落地到其他数据源。例如用户每播放一次视频,视频的播放次数就加一
共享会话
什么是会话,客户端和服务端在交互的过程中产生的一些专属于该客户端的中间状态的数据
如果每个应用服务器,维护自己的会话数据,此时彼此之间不共享,用户请求访问到不同的服务器上,就可能会出现一些不能正确处理的情况,这时候就可以用Redis将所有的管理起来
此时所有的会话数据,都被各个服务器共享了
手机验证码
- 生成验证码,用户输入手机号,获取验证码(限制1分钟内,最多获取5次验证码,或者每次获取验证码必须间隔30s)
- 检查验证码,把短信收到的验证码这一串数提交到系统中,系统进行验证验证码是否正确