Redis对象——字符串
文章导航-readme
前言
上一篇文章Redis之对象篇——Redis对象系统简介简单介绍了Redis
的对象系统。Redis
使用对象来表示数据库中的键和值每个对象都由一个redisObject
结构表示,该结构中和保存数据有关的三个属性分别是type
属性、 encoding
属性和ptr
属性。
typedef struct redisObiect{
//类型
unsigned type:4;
//编码
unsigned encoding:4;
//指向底层数据结构的指针
void *ptr;
}
字符串对象是 Redis 中最基本的数据类型,也是我们工作中最常用的数据类型。redis中的键都是字符串对象,而且其他几种数据结构都是在字符串对象基础上构建的。字符串对象的值实际可以是字符串、数字、甚至是二进制,最大不能超过512MB 。
一、内部实现
Redis字符串对象底层的数据结构实现主要是int和简单动态字符串SDS(这个字符串,和我们认识的C字符串不太一样,了解具体请看图解Redis之数据结构篇——简单动态字符串SDS),其通过不同的编码方式映射到不同的数据结构。
字符串对象的内部编码有3种 :int
、raw
和embstr
。Redis会根据当前值的类型和长度来决定使用哪种编码来实现。
如果一个字符串对象保存的是整数值,并且这个整数值可以用
long
类型来表示,那么字符串对象会将整数值保存在字符串对象结构的ptr
属性里面(将void*
转换成1ong
),并将字符串对象的编码设置为int
。如果字符串对象保存的是一个字符串值,并且这个字符串值的长度大于32字节,那么字符串对象将使用一个简单动态字符串(SDS)来保存这个字符串值,并将对象的编码设置为
raw
。如果字符串对象保存的是一个字符串值,并且这个字符申值的长度小于等于32字节,那么字符串对象将使用一个简单动态字符串(SDS)来保存这个字符串值,并将对象的编码设置为
embstr
embstr
编码是专门用于保存短字符串的一种优化编码方式,我们可以看到embstr
和raw
编码都会使用SDS
来保存值,但不同之处在于embstr
会通过一次内存分配函数来分配一块连续的内存空间来保存redisObject
和SDS
。而raw
编码会通过调用两次内存分配函数来分别分配两块空间来保存redisObject
和SDS
。Redis这样做会有很多好处。
embstr
编码将创建字符串对象所需的内存分配次数从raw编码的两次降低为一次- 释放
embstr
编码的字符串对象同样只需要调用一次内存释放函数 - 因为
embstr
编码的字符串对象的所有数据都保存在一块连续的内存里面可以更好的利用CPU缓存提升性能。
Redis中根据数据类型和长度来使用不同的编码和数据结构存储存在于Redis中的每一种对象类型上。其这种小细节上的优化令我叹服不止,后续我们会看到Redis中到处都是这种内存与性能上的小细节优化!
二、常用命令
字符串类型的命令比较多 ,我们先来了解几个日常开发中常用的。
1 设置值
redis> set testKey testValue
OK
set key value [ex seconds] [px milliseconds] [nx|xx]
ex seconds
:为键设置秒级过期时间。如命令:
set username xiaoming ex 100
相当于执行下面两条命令SET username xiaoming
EXPIRE username 100
set key value [ex seconds]
操作是原子性的,相比连续执行上面两个命令,它更快。px milliseconds
:为键设置毫秒级过期时间。nx
:键必须不存在,才可以设置成功,用于添加。//mykey 不存在
redis> set mykey "Hello" nx
(integer) 1
//mykey 已经存在
redis> set mykey "World" nx
(integer) 0
redis> GET mykey
"Hello"
redis>
由于
set key value nx
同样是原子性的操作,因此可以作为分布式锁的一种实现方案。xx
:与nx相反,键必须存在,才可以设置成功,用于更新
以上几个命令的替代命令是SETNX
, SETEX
,PSETEX
,但是由于SET
命令加上选项已经可以完全取代SETNX
, SETEX
,PSETEX
的功能,所以在将来的版本中,redis可能会不推荐使用并且最终抛弃这几个命令。
2 获取值
get key
返回key
的value
。如果key不存在,返回特殊值nil
。如果key
的value
不是string,就返回错误,因为GET
只处理string类型的values
。
redis> GET nokey
(nil)
redis> SET mykey "Hello World"
OK
redis> GET mykey
"Hello World"
3 批量设置值
由于Redis目前的应用非常广泛,目前大多数公司对Redis的调用基本都会有一层自己的封装,看起来就像是在调用本地缓存一样,对于批量性的操作,一些对于Redis不太了解的可能就像使用本地缓存一样进行循环set。这样对性能是有很大的损耗的。实际上Redis提供了批量操作的命令。
MSET key value [key value ...]
对应给定的keys到他们相应的values上。MSET
会用新的value替换已经存在的value,就像普通的SET
命令一样。如果不想覆盖已经存在的values,可以使用MSETNX key value [key value ...]
注意:MSET
是原子的,所以所有给定的keys是一次性set的。客户端不可能看到这种一部分keys被更新而另外的没有改变的情况。
redis> MSET key1 "Hello" key2 "World"
OK
redis> GET key1
"Hello"
redis> GET key2
"World"
4 批量获取值
MGET key [key ...]
结果是按照传入键的顺序返回所有指定的key的value。对于每个不对应string或者不存在的key,都返回特殊值nil。
redis> SET key1 "Hello"
OK
redis> SET key2 "World"
OK
redis> MGET key1 key2 nokey
1) "Hello"
2) "World"
3) (nil)
Redis可以支撑每秒数万的读写操作,但是这指的是Redis服务端的处理能力,对于客户端来说,一次命令除了命令时间还是有网络时间,如n次get操作
使用get
命令
n次get时间 = n次网络时间 + n次命令时间
而mget
操作
n次get时间 = 1次网络时间 + n次命令时间
而在实际开发中因为Redis的处理能力已经足够高,性能瓶颈的因素往往是网络。
学会使用批量操作,有助于提高效率,但是要掌握一个平衡的度,每次批量操作所发送的命令数并不是无节制的由于Redis是单线程架构,如果数量过多可能造成Redis阻塞或者网络拥塞。
5 计数
incr key
对存储在指定key
的数值执行原子的加1操作。
如果指定的key不存在,那么在执行incr操作之前,会先将它的值设定为0
。
如果指定的key中存储的值不是字符串类型(fix:)或者存储的字符串类型不能表示为一个整数,
那么执行这个命令时服务器会返回一个错误(eq:(error) ERR value is not an integer or out of range)。
这个操作仅限于64位的有符号整型数据。
注意: 由于redis并没有一个明确的类型来表示整型数据,所以这个操作是一个字符串操作。
执行这个操作的时候,key对应存储的字符串被解析为10进制的64位有符号整型数据。
事实上,Redis 内部采用整数形式(Integer representation)来存储对应的整数值,所以对该类字符串值实际上是用整数保存,也就不存在存储整数的字符串表示(String representation)所带来的额外消耗。
redis> SET mykey "1"
OK
redis> INCR mykey
(integer) 2
redis> GET mykey
"3"
redis>
除了incr
命令, Redis提供了decr(自减)
、 incrby(自增指定数字)
、decrby(自减指定数字)
、 incrbyfloat(自增浮点数)
decr key
incrby key increment
decrby key decrement
incrbyfloat key increment
6 其它
对于常用的redis字符串命令和一些其它的命令我们列一个表格以便来更直观的看到。
命令 | 描述 | 时间复杂度 |
---|---|---|
set key value [ex seconds] [px milliseconds] [nx|xx] |
设置值 | O(1) |
get key |
获取值 | O(1) |
del key [key ...] |
删除key | O(N)(N是键的个数) |
mset key [key value ...] |
批量设置值 | O(N)(N是键的个数) |
mget key [key ...] |
批量获取值 | O(N)(N是键的个数) |
incr key |
将 key 中储存的数字值增一 | O(1) |
decr key |
将 key 中储存的数字值减一 | O(1) |
incrby key increment |
将 key 所储存的值加上给定的增量值(increment) | O(1) |
decrby key increment |
key 所储存的值减去给定的减量值(decrement) | O(1) |
incrbyfloat key increment |
将 key 所储存的值加上给定的浮点增量值(increment) | O(1) |
append key value |
如果 key 已经存在并且是一个字符串, APPEND 命令将指定的 value 追加到该 key 原来值(value)的末尾 | O(1) |
strlen key |
返回 key 所储存的字符串值的长度。 | O(1) |
setrange key offset value |
用 value 参数覆写给定 key 所储存的字符串值,从偏移量 offset 开始 | O(1) |
getrange key start end |
返回 key 中字符串值的子字符 | O(N)(N是字符串的长度) |
三、常用场景
reids字符串的使用场景应该是最为广泛的,甚至有些对redis其它几种对象不太熟悉的人,基本所有场景都会使用字符串(序列化一下直接扔进去)。在众多的使用场景中总结一下大概分以下几种。
1. 作为缓存层
如上图,Redis经常作为缓存层,来缓存一些热点数据。来加速读写性能从而降低后端的压力。一般在读取数据的时候会先从Redis中读取,如果Redis中没有,再从数据库中读取。在Redis作为缓存层使用的时候,必须注意一些问题,如:缓存穿透、雪崩以及缓存更新问题......
2. 计数器\限速器\分布式系统ID
计数器\限速器\分布式ID等主要是利用Redis字符串自增自减的特性。
- 计数器:经常可以被用来做计数器,如微博的评论数、点赞数、分享数,抖音作品的收藏数,京东商品的销售量、评价数等。
- 限速器:如验证码接口访问频率限制,用户登陆时需要让用户输入手机验证码,从而确定是否是用户本人,但是为了短信接口不被频繁访问,会限制用户每分钟获取验证码的频率,例如一分钟不能超过5次。
- 分布式ID:由于Redis自增自减的操作是原子性的因此也经常在分布式系统中用来生成唯一的订单号、序列号等。
3. 分布式系统共享session
通常在单体系统中,Web服务将会用户的Session信息(例如用户登录信息)保存在自己的服务器中。但是在分布式系统中,这样做会有问题。因为分布式系统通常有很多个服务,每个服务又会同时部署在多台机器上,通过负载均衡机制将将用户的访问均衡到不同服务器上。这个时候用户的请求可能分发到不同的服务器上,从而导致用户登录保存Session是在一台服务器上,而读取Session是在另一台服务器上因此会读不到Session。
这种问题通常的做法是把Session存到一个公共的地方,让每个Web服务,都去这个公共的地方存取Session。而Redis就可以是这个公共的地方。(数据库、memecache等都可以各有优缺点)。
4. 二进制存储
由于Redis字符串可以存储二进制数据的特性,因此也可以用来存储一些二进制数据。如图片、 音频、 视频等。
参考
《Redis设计与实现》
《Redis开发与运维》
《Redis官方文档》
-----END-----
Redis对象——字符串的更多相关文章
- redis数据类型-字符串类型
Redis数据类型 字符串类型 字符串类型是Redis中最基本的数据类型,它能存储任何形式的字符串,包括二进制数据.你可以用其存储用户的邮箱.JSON化的对象甚至是一张图片.一个字符串类型键允许存储的 ...
- 【redis源码阅读】redis对象
结构定义 在redis中,对象的数据结构定义如下: typedef struct redisObject { unsigned type:4; unsgined encoding:4; uns ...
- 第二百九十五节,python操作redis缓存-字符串类型
python操作redis缓存-字符串类型 首先要安装redis-py模块 python连接redis方式,有两种连接方式,一种是直接连接,一张是通过连接池连接 注意:以后我们都用的连接池方式连接,直 ...
- Redis对象类型
Redis对象类型 Redis基于基础的数据结构创建的对象: 字符串对象. 列表对象. 哈希对象. 集合对象 有序集合对象. 对象回收:Redis对象系统实现了基于引用计数技术的内存回收机制,当程序不 ...
- Redis 数据结构-字符串源码分析
相关文章 Redis 初探-安装与使用 Redis常用指令 本文将从以下几个部分进行介绍 1.前言 2.常用命令 3.字符串结构 4.字符串实现 5.命令是如果操作字符串的 前言 平时在使用 Redi ...
- Redis底层探秘(五):Redis对象
前面几篇文章,我们一起学习了redis用到的所有主要数据结构,比如简单动态字符串(sds).双端链表.字典.压缩列表.整数集合等等. redis并没有直接使用这些数据结构来实现键值对数据库,而是基于这 ...
- Redis对象的设计与实现
一.Redis对象结构Redis中的每个对象都由一个redisObject结构表示: typedef struct redisObject { unsigned type;//类型 unsigned ...
- redis之字符串命令源代码解析(二)
形象化设计模式实战 HELLO!架构 redis命令源代码解析 在redis之字符串命令源代码解析(一)中讲了get的简单实现,并没有对 ...
- 面试官:你了解过Redis对象底层实现吗
上一章我们讲了Redis的底层数据结构,不了解的人可能会有疑问:这个和平时用的五大对象有啥关系呢?这一章我们就主要解释他们所建立的联系. 看这个文件之前,如果对ziplist.skiplist.int ...
随机推荐
- CF980C Posterized 贪心 二十五
Posterized time limit per test 1 second memory limit per test 256 megabytes input standard input out ...
- Flask大型项目框架结构理解
导语:前段时间学习狗书的flask大型项目框架结构的时候有点混乱,到现在也知道是个啥了,想着,把关系理一理,写一篇博客.也方便后来学习的人查阅.以下是我创建项目时候的结构. myproject --- ...
- 【Offer】[48] 【最长不含重复字符的子字符串】
题目描述 思路分析 测试用例 Java代码 代码链接 题目描述 请从字符串中找出一个最长的不包含重复字符的子字符串,计算该最长子字符串的长度.假设字符串中只包含'a'~'z'的字符.例如,在字符串&q ...
- 运维核心基础知识之——MD5sum校验文件
如何使用MD5sum工具校验你的文件. 演示过程截图: 先给文件创建一个md5值 md5sum oldboy.txt 然后将md5sum生成的md5值写入到一个文件police.log md5sum ...
- StackOverflow 周报 - 高质量问题的问答(Java、Python)
这是 Stack Overflow 第三周周报,本周加入了 Python 的内容,原计划两篇 Java.两篇 Python.但明天过节所以今天就先把周报发了,两篇 Java.一篇 Python.公众号 ...
- sql 多行、一行 互转
原始数据: 期望数据: IF OBJECT_ID('temp_20170701','u') IS NOT NULL DROP TABLE temp_20170701 CREATE TABLE temp ...
- Unity基础:AR(增强现实)的学习
版权申明: 本文原创首发于以下网站: 博客园『优梦创客』的空间:https://www.cnblogs.com/raymondking123 优梦创客的官方博客:https://91make.top ...
- win7 安装mysql5.7
Windows 64 位 mysql 5.7以上版本包解压中没有data目录和my-default.ini以及服务无法启动的解决办法以及修改初始密码的方法 LZ初学SQL,本来以为开源的安装很简单,但 ...
- Oracle创建自增主键表
1.创建表 /*第一步:创建表格*/ create table t_user( id int primary key, --主键,自增长 username varchar(), password va ...
- servlet 的基础学习
Servlet是sun公司提供的一门用于开发动态web资源的技术. Sun公司在其API中提供了一个servlet接口,用户若想用发一个动态web资源(即开发一个Java程序向浏览器输出数据),需要完 ...