Redis对象——字符串

文章导航-readme

前言

    上一篇文章Redis之对象篇——Redis对象系统简介简单介绍了Redis的对象系统。Redis使用对象来表示数据库中的键和值每一个对象都由一个redisObject结构表示,该结构中和保存数据有关的三个属性分别是type属性、 encoding属性和ptr属性。html

typedef struct redisObiect{
    //类型
    unsigned type:4;
    //编码
    unsigned encoding:4;
    //指向底层数据结构的指针
    void *ptr;
}

    字符串对象是 Redis 中最基本的数据类型,也是咱们工做中最经常使用的数据类型。redis中的键都是字符串对象,并且其余几种数据结构都是在字符串对象基础上构建的。字符串对象的值实际能够是字符串、数字、甚至是二进制,最大不能超过512MB 。redis

1、内部实现

    Redis字符串对象底层的数据结构实现主要是int和简单动态字符串SDS(这个字符串,和咱们认识的C字符串不太同样,了解具体请看图解Redis之数据结构篇——简单动态字符串SDS),其经过不一样的编码方式映射到不一样的数据结构。shell

字符串对象的内部编码有3种 :intrawembstr。Redis会根据当前值的类型和长度来决定使用哪一种编码来实现。数据库

  1. 若是一个字符串对象保存的是整数值,而且这个整数值能够用long类型来表示,那么字符串对象会将整数值保存在字符串对象结构的ptr属性里面(将void*转换成1ong),并将字符串对象的编码设置为int后端

  2. 若是字符串对象保存的是一个字符串值,而且这个字符串值的长度大于32字节,那么字符串对象将使用一个简单动态字符串(SDS)来保存这个字符串值,并将对象的编码设置为raw缓存

  3. 若是字符串对象保存的是一个字符串值,而且这个字符申值的长度小于等于32字节,那么字符串对象将使用一个简单动态字符串(SDS)来保存这个字符串值,并将对象的编码设置为embstr服务器

    embstr编码是专门用于保存短字符串的一种优化编码方式,咱们能够看到embstrraw编码都会使用SDS来保存值,但不一样之处在于embstr会经过一次内存分配函数来分配一块连续的内存空间来保存redisObjectSDS。而raw编码会经过调用两次内存分配函数来分别分配两块空间来保存redisObjectSDS。Redis这样作会有不少好处。网络

  • embstr编码将建立字符串对象所需的内存分配次数从raw编码的两次下降为一次
  • 释放 embstr编码的字符串对象一样只须要调用一次内存释放函数
  • 由于embstr编码的字符串对象的全部数据都保存在一块连续的内存里面能够更好的利用CPU缓存提高性能。

    Redis中根据数据类型和长度来使用不一样的编码和数据结构存储存在于Redis中的每一种对象类型上。其这种小细节上的优化令我叹服不止,后续咱们会看到Redis中处处都是这种内存与性能上的小细节优化!session

2、经常使用命令

    字符串类型的命令比较多 ,咱们先来了解几个平常开发中经常使用的。数据结构

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

返回keyvalue。若是key不存在,返回特殊值nil。若是keyvalue不是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是字符串的长度)

3、经常使用场景

    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-----

相关文章
相关标签/搜索