redis-2-数据结构内部编码-单线程-String命令

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 前言
  • [1. 数据结构和内部编码](#1. 数据结构和内部编码)
  • [2. 单线程模型的工作过程](#2. 单线程模型的工作过程)
    • [2.1 多线程为什么快](#2.1 多线程为什么快)
  • [3. String类型](#3. String类型)
    • [3.1 get和set](#3.1 get和set)
    • [3.2 mset和mget](#3.2 mset和mget)
    • [3.3 setnx,setex,psetex](#3.3 setnx,setex,psetex)
    • [3.4 incr和incrby](#3.4 incr和incrby)
    • [3.5 decr,decrby,incrbyfloat](#3.5 decr,decrby,incrbyfloat)
    • [3.6 append,,](#3.6 append,,)
    • [3.7 getrange](#3.7 getrange)
    • [3.8 setrange,](#3.8 setrange,)
    • [3.9 strlen](#3.9 strlen)
    • [3.10 string编码方式](#3.10 string编码方式)
    • [3.11 应用场景](#3.11 应用场景)
    • [3.12 作为缓存](#3.12 作为缓存)
    • [3.13 计数功能](#3.13 计数功能)
    • [3.14 共享会话](#3.14 共享会话)
    • [3.15 手机验证码](#3.15 手机验证码)
    • [3.16 什么是业务](#3.16 什么是业务)
  • 总结

前言

1. 数据结构和内部编码

type 命令实际返回的就是当前键的数据结构类型,它们分别是:string(字符串)、list(列

表)、hash(哈希)、set(集合)、zset(有序集合),但这些只是 Redis 对外的数据结构

集合就是Set

Zset就是除了存元素之外,还要存储score就是权重,对权重进行优先级排序

raw就是最基本的字符串,int是用来计数的,当value就是一个整数的时候,可能就会使用redis直接使用int来保存,embstr其实就是存储短字符串的,raw是存储长字符串的,这个是redis自动处理的

hash可能是hashtable(不是java中的hashtable)这个是redis中的哈希表,也可能是ziplist:哈希表元素比较少的时候,使用这个,这个是压缩列表,能够节省空间,其实就是一个list,hashtable的内存比较大

list就是链表,ziplist还是压缩列表:节省空间的,但是时间复杂度高,但是元素少,其实不影响,redis3.2开始使用的是quickList,包容了lincklist和ziplist,就是一个链表,每个元素都是一个ziplist

intset:就是集合里面存储的都是int

skiplist就是跳表:就是有道题,复杂链表复制,有一个random指向随机节点,跳表就是这样,有一个类似于random的指针域,搭配这个指针域,就可以做到查询元素时间复杂度是logN

bash 复制代码
object encoding key

查询key对应的value实际编码方式

bash 复制代码
127.0.0.1:6379> object encoding key1
"int"
127.0.0.1:6379> object encoding key3
"listpack"
127.0.0.1:6379> object encoding key4
"intset"
127.0.0.1:6379> 

编码方式是自动适应的

字符串超过多少使用raw--》这个数字没有意义,因为数字是配置的,知道数字是怎么得到的才好

2. 单线程模型的工作过程

redis只使用一个线程来处理所有命令请求

但是不是说一个redis服务器内部就只有一个线程

假设多个客户端来处理redis服务器

这里不会有线程安全问题,因为redis是单线程,所以保证了收到的请求是串行的

为什么使用单线程,因为redis的业务都是短平快的

不太消耗cpu资源,就不吃多核了

弊端就是某个操作时间长,就阻塞住了

2.1 多线程为什么快

这里快的参照物是数据库

通常来讲,单线程处理能⼒要⽐多线程差,例如有 10 000 公⽄货物,每辆⻋的运载能⼒是每次

200 公⽄,那么要 50 次才能完成;但是如果有 50 辆⻋,只要安排合理,只需要依次就可以完成任

务。那么为什么 Redis 使⽤单线程模型会达到每秒万级别的处理能⼒呢?可以将其归结为三点:

a. 纯内存访问。Redis 将所有数据放在内存中,内存的响应时⻓⼤约为 100 纳秒,这是 Redis 达

到每秒万级别访问的重要基础。

b. ⾮阻塞 IO。处理网络io的时候,Redis 使⽤ epoll 作为 I/O 多路复⽤技术的实现,再加上 Redis ⾃⾝的事件处理模型

将 epoll 中的连接、读写、关闭都转换为事件,不在⽹络 I/O 上浪费过多的时间,如图 2-6 所

⽰。

c. 单线程避免了线程切换和竞态产⽣的消耗。单线程可以简化数据结构和算法的实现,让程序模

型更简单;其次多线程避免了在线程竞争同⼀份共享数据时带来的切换和等待消耗。

避免了不必要的线程竞争开销

d.redis的核心功能更简单

io多路复用就是说一个线程可以管理多个socket,针对tcp来说,服务器这边每次要服务一个客户端,都需要给客户端安排一个socket,一个服务器要服务多个客户端,就会同时有多个socket

但是这些socket并不一定是一直在传输数据

虽然socket多,但是大多数都是静默的

所以同一时刻,只有少数socket是活跃的

以前讲tcp的时候,是每个客户端都是一个线程

io多路复用就是一个线程来处理多个socket:linux上提供的io多路复用API是epoll,运行效率最高


喊人的就是epoll事件通知机制,或者回调机制

前提是这三件事交互不频繁,大部分时间都在等待,如果交互频繁,还是多线程好

io多路复用就是,java使用NIO:底层分装了epoll

3. String类型

redis中得到字符串就是按照二进制的方式来存储的,所以还可以存储文本数据,整数,json,xml,二进制数据(图片,视频,音频)都可以存储

但是不要存储视频那些,因为体积太大了

redis限制String类型,大小最大的512M,所以不要存

redis存储,存的utf-8,那么取出来就是utf-8,所以一般不会遇到乱码,redis存储是不会进行编码转化的

3.1 get和set

bash 复制代码
SET key value [expiration EX seconds|PX milliseconds] [NX|XX]

ex相当于设置超时时间

bash 复制代码
set key value ex 10
set key value
expire key 10

这两个意思是相同的

SET 命令⽀持多种选项来影响它的⾏为:

• EX seconds⸺使⽤秒作为单位设置 key 的过期时间。

• PX milliseconds⸺使⽤毫秒作为单位设置 key 的过期时间。

• NX ⸺只在 key 不存在时才进⾏设置,即如果 key 之前已经存在,设置不执⾏。

• XX ⸺只在 key 存在时才进⾏设置,即如果 key 之前不存在,设置不执⾏。

注意:由于带选项的 SET 命令可以被 SETNX 、 SETEX 、 PSETEX 等命令代替,所以之后的版本

中,Redis 可能进⾏合并。

NX和XX不能同时写

• XX ⸺只在 key 存在时才进⾏设置,即如果 key 之前不存在,设置不执⾏。

意思就是key存在,更新value,key不存在,什么都不干了

bash 复制代码
setnx
setex
setpx

对应的还有这些方法

默认的是,key不存在,创建新的,如果key存储,覆盖value(改变数据类型可能,ttl也会失效)

bash 复制代码
flushall

这个是删除所有key

bash 复制代码
127.0.0.1:6379> set key1 123
OK
127.0.0.1:6379> get key1
"123"
127.0.0.1:6379> set key2 123 ex 10
OK
127.0.0.1:6379> ttl key2
(integer) 6
127.0.0.1:6379> ttl key2
(integer) -2
127.0.0.1:6379> set key2 123 nx
OK
127.0.0.1:6379> set key2 123 nx
(nil)
127.0.0.1:6379> set key2 456 nx
(nil)
127.0.0.1:6379> get key2
"123"
127.0.0.1:6379> set key1 789 xx
OK
127.0.0.1:6379> get key1
"789"
127.0.0.1:6379> set key3 123 xx
(nil)
127.0.0.1:6379> 

get只是支持字符串类型的value

如果value是其他类型,那么不能使用get

bash 复制代码
127.0.0.1:6379> lpush key3 11 2 3
(integer) 3
127.0.0.1:6379> get key3
(error) WRONGTYPE Operation against a key holding the wrong kind of value
127.0.0.1:6379> 

3.2 mset和mget

一次操作多组键值对,get和set只能操作一个键值对

一次操作的话,那么就只有一次网络传输了

bash 复制代码
MGET key [key ...]
bash 复制代码
MSET key value [key value ...]
bash 复制代码
127.0.0.1:6379> mset key1 111 key2 222 key3 333
OK
127.0.0.1:6379> mget key1 key2 key3 
1) "111"
2) "222"
3) "333"
127.0.0.1:6379> 

3.3 setnx,setex,psetex

setnx:不存在才设置,存在的话设置失败

setex:就是设置的时候有超时时间

bash 复制代码
127.0.0.1:6379> setex key2 10 222
OK
127.0.0.1:6379> ttl key2
(integer) 6

psetex:设置过期时间为毫秒

bash 复制代码
127.0.0.1:6379> psetex key3 10000 333
OK
127.0.0.1:6379> pttl key3
(integer) 5429
127.0.0.1:6379> ttl key3
(integer) 2

3.4 incr和incrby

incr:表示针对value+1

incrby:针对value+n

decr:-1

decyby:-n

incrbyfloat:±小数

bash 复制代码
INCR key

注意这个key对应的value必须是整数,浮点数也不行,而且整数范围不能超过64位,而且返回值就是+1后的值

bash 复制代码
127.0.0.1:6379> flushall
OK
127.0.0.1:6379> set key 10
OK
127.0.0.1:6379> incr key
(integer) 11
127.0.0.1:6379> get key
"11"
127.0.0.1:6379> set key2 a
OK
127.0.0.1:6379> incr key2
(error) ERR value is not an integer or out of range

incr如果操作的key如果不存在,那么value默认为0,然后返回的就是1了

bash 复制代码
127.0.0.1:6379> incrby key3 3
(integer) 3

这个还可以为负数都是可以的

bash 复制代码
127.0.0.1:6379> incrby key3 -1
(integer) 2

3.5 decr,decrby,incrbyfloat

bash 复制代码
127.0.0.1:6379> decr key3 
(integer) 1
127.0.0.1:6379> decrby key3 -2 
(integer) 3
bash 复制代码
127.0.0.1:6379> incrbyfloat key4 2.3
"2.3"
127.0.0.1:6379> incrbyfloat key4 -2.3
"0"

3.6 append,

append:

如果 key 已经存在并且是⼀个 string,命令会将 value 追加到原有 string 的后边。如果 key 不存在,

则效果等同于 SET 命令

bash 复制代码
APPEND KEY VALUE

返回值:追加完成之后 string 的⻓度。

bash 复制代码
127.0.0.1:6379> set key hello
OK
127.0.0.1:6379> append key world
(integer) 10
127.0.0.1:6379> append key2 world
(integer) 5
bash 复制代码
127.0.0.1:6379> append key3 你好
(integer) 6

append返回值长度的单位是字节,如果redis只认字节,不认字符,因为编码没有处理

因为当前终端默认的编码实时utf-8,,,,如果一个汉字就是三个字节

bash 复制代码
127.0.0.1:6379> get key3
"\xe4\xbd\xa0\xe5\xa5\xbd"

在启动客户端的时候,可以加上一个--raw

那么get的时候,就可以变为utf-8的形式了,就可以尝试翻译了

bash 复制代码
# redis-cli --raw
127.0.0.1:6379> auth 12345
WRONGPASS invalid username-password pair or user is disabled.

127.0.0.1:6379> auth 123456
OK
127.0.0.1:6379> append key3 你好
12
127.0.0.1:6379> get key3
你好你好

3.7 getrange

getrange:获取子串

bash 复制代码
GETRANGE key start end

返回 key 对应的 string 的⼦串,由 start 和 end 确定(左闭右闭)。可以使⽤负数表⽰倒数。-1 代表倒数第⼀个字符,-2 代表倒数第⼆个,其他的与此类似。超过范围的偏移量会根据 string 的⻓度调整成正确的值。

下标从0开始

bash 复制代码
127.0.0.1:6379> set key helloworld
OK
127.0.0.1:6379> getrange key  0 -1
helloworld

如果是中文的话,强制乱切,可能就不知道是什么东西了

但是java中切就没事,因为java中字符串的基本单位是字符,c++中字符串的基本单位也是字节

3.8 setrange,

就是修改子串

bash 复制代码
SETRANGE key offset value

覆盖字符串的⼀部分,从指定的偏移开始。

其实就是从offset开始替换,替换不了的就尾插了

返回值:替换后的 string 的⻓度。

bash 复制代码
127.0.0.1:6379> set key helloworld
OK
127.0.0.1:6379> setrange key 1 aaa
10
127.0.0.1:6379> get key
haaaoworld
127.0.0.1:6379> setrange key 1 bbbbbbbbb
10
127.0.0.1:6379> get key
hbbbbbbbbb
127.0.0.1:6379> setrange key 1 bbbbbbbbbbbbbbbbbb
19
127.0.0.1:6379> get key
hbbbbbbbbbbbbbbbbbb

但是如果是中文字符串的话,可能还会出问题的,因为redis就是存储的二进制数据

bash 复制代码
127.0.0.1:6379> setrange key2 1 bbb
4
127.0.0.1:6379> get key2
bbb

如果不存在这个key就是这样

其实bbb前面有一个字节\x00

前面不存在的字符就是x00

3.9 strlen

这个就是获取字符串的长度,单位是字节

java中一个2字节表示一个char,可以表示汉字,使用unicode编码

java中字符串中汉字是utf-8,三个字节

C++中字符串都是字节为单位

从char到string,从string到char,我们是感知不到的

如果value不是string就会报错

bash 复制代码
127.0.0.1:6379> set key hell你好
OK
127.0.0.1:6379> strlen key
10

3.10 string编码方式

字符串类型的内部编码有 3 种:

• int:8 个字节的⻓整型。

• embstr:⼩于等于 39 个字节的字符串。

• raw:⼤于 39 个字节的字符串。

Redis 会根据当前值的类型和⻓度动态决定使⽤哪种内部编码实现

bash 复制代码
127.0.0.1:6379> set key 123
OK
127.0.0.1:6379> object encoding key
int
127.0.0.1:6379> set key aa
OK
127.0.0.1:6379> object encoding key
embstr
127.0.0.1:6379> set key aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
OK
127.0.0.1:6379> object encoding key
raw

不建议记忆39

为什么是39吗

这个才重要

bash 复制代码
127.0.0.1:6379> set key 1.2
OK
127.0.0.1:6379> object encoding key
embstr
127.0.0.1:6379> incr key
ERR value is not an integer or out of range

存储小数其实本质上还是当做字符串来存储

3.11 应用场景

3.12 作为缓存

防止数据越来越多:1.可以设置一个过期时间

2.redis在内存不足的时候,提供了淘汰机制,会淘汰掉一些不使用的key

与 MySQL 等关系型数据库不同的是,Redis 没有表、字段这种命名空间,⽽且也没有对键名有强制要求(除了不能使⽤⼀些特殊字符)。但设计合理的键名,有利于防⽌键冲突和项⽬的可维护性,⽐较推荐的⽅式是使⽤ "业务名:对象名:唯⼀标识:属性" 作为键名。例如MySQL 的数据库名为 vs,⽤⼾表名为 user_info,那么对应的键可以使⽤"vs:user_info:6379"、"vs:user_info:6379:name" 来表⽰,如果当前 Redis 只会被⼀个业务使⽤,可以省略业务名 "vs:"。如果键名过程,则可以使⽤团队内部都认同的缩写替代,例如"user:6379:friends:messages:5217" 可以被 "u:6379🇫🇷m:5217" 代替。毕竟键名过⻓,还是会导致 Redis 的性能明显下降的。

3.13 计数功能

许多应⽤都会使⽤ Redis 作为计数的基础⼯具,它可以实现快速计数、查询缓存的功能,同时数

据可以异步处理或者落地到其他数据源

例如视频⽹站的视频播放次数可以使⽤Redis 来完成:⽤⼾每播放⼀次视频,相应的视频播放数就会⾃增 1

为什么还要同步数据库呢,因为万一要统计前100点赞呢,所以要弄入数据库,才好统计

异步说明,不是立马就同步数据过去

实际中要开发⼀个成熟、稳定的真实计数系统,要⾯临的挑战远不⽌如此简单:防作弊、按照不同维度计数、避免单点问题、数据持久化到底层数据源等。

3.14 共享会话

⼀个分布式 Web 服务将⽤⼾的 Session 信息(例如⽤⼾登录信息)保存在各⾃的服务器中,但这样会造成⼀个问题:出于负载均衡的考虑,分布式服务会将⽤⼾的访问请求均衡到不同的服务器上,并且通常⽆法保证⽤⼾每次请求都会被均衡到同⼀台服务器上,这样当⽤⼾刷新⼀次访问是可能会发现需要重新登录,这个问题是⽤⼾⽆法容忍的

Cookie:浏览器存储数据的机制

Session:服务器这边存储数据的机制

这两个都是键值对的方式存储键值对


3.15 手机验证码

比如每次获取验证码必须间隔30秒:直接设置过期时间为30秒

或者

一分钟之内,最多五次获取验证码:设置key一分钟过期时间,value为1

3.16 什么是业务

业务就是一个公司或者一个产品,是如何解决一个或者一系列问题的,解决问题的过程就是业务

或者说业务就是系统的功能

比如买票:可以分时段放票:业务上的优化

技术是通用的,但是业务在不同公司就是不同的

总结

相关推荐
二哈喇子!2 小时前
基于SSM框架的公交车查询系统的设计与实现
java·数据库·ssm
Coder_Boy_2 小时前
基于SpringAI的在线考试系统-智能考试系统-学习分析模块
java·开发语言·数据库·spring boot·ddd·tdd
阿杰 AJie2 小时前
MySQL 聚合函数总表(完整版)
数据库·mysql
玄同7652 小时前
Python「焚诀」:吞噬所有语法糖的终极修炼手册
开发语言·数据库·人工智能·python·postgresql·自然语言处理·nlp
cdut_suye2 小时前
解锁函数的魔力:Python 中的多值传递、灵活参数与无名之美
java·数据库·c++·人工智能·python·机器学习·热榜
尽兴-2 小时前
MySQL 8.0高可用集群架构实战深度解析
数据库·mysql·架构·集群·高可用·innodb cluster
遇见火星3 小时前
MySQL常用命令大全(2026最新版)
数据库·mysql·oracle
霖霖总总3 小时前
[小技巧42]InnoDB 索引与 MVCC 的协同工作原理
运维·数据库·mysql
未来之窗软件服务3 小时前
计算机等级考试—数据库完整性进销存—东方仙盟练气期
数据库·oracle·计算机软考·仙盟创梦ide·东方仙盟