第02课:API

第02课:API 的理解与使用

前面我们大致了解了 Redis 是什么,接下来我们就来使用 Redis。

如何使用 Redis?首先我们需要根据 Redis 提供的 7 种数据类型来了解它,分别是字符串 String、哈希 Hash、列表 List、集合 Set、有序集合 Sorted Set、发布订阅 Pub/Sub、事务 Transactions。

我们先来讲一下 Redis 的内部实现和运行机制。在使用 Redis 时,命令多使用就会很容易记住,这归功于 Redis 的命令简单。但是内部实现和它的单线程,你必须先了解原理,再了解 7 种数据类型,这样才能在实际开发中游刃有余。

内部实现

接下来,我们简单了解下 Redis 的内部实现。Redis 内部会封装一个 redisObject 实例。由这个 redisObject 来表示所有的 key 和 value。redisObject 所包含的字段中,最主要的是 type 和 encoding。

1.type 代表一个 value 对象具体是何种数据类型,包括 String、Hash、List、Set和Sorted set等数据类型。

2.encoding 则表示不同数据类型在 Redis 内部的存储方式,包括 Raw、Int、Ziplist、LinkedList、HashMap 和 Intset 存储方式。

上面说的比较抽象,为了帮助大家理解,我举个例子,比如 type 为 String 代表的是 value 存储了一个字符串,那么对应的 encoding 可以是 Int 或者 Raw。如果是 Int 则代表实际 Redis 内部是按数值类型存储的,比如你要存储“1234”、“2018”等字符串。

还有一个特别的内部字段,vm 字段。这个字段默认是关闭的,只有打开了 Redis 的虚拟内存功能,才能真正分配内存。有同学要问了,这个字段有什么用呢?因为 Redis 是 key/value 存储,是非常浪费内存的,这些内存成本主要是为了给Redis 上面的 7 种数据类型提供统一管理接口。

单线程

我们再来看下为什么 Redis 中单线程快。很多程序员应该深有体会,其实其他很多语言单线程是非常慢的,但是为什么 Redis 的单线程快呢?

我觉得最大的原因是纯内存存储。正因为这个是主要原因,所以后面两个原因显得有点不太重要,即非阻塞 IO 和避免线程切换和竞态消耗

你要清楚,首先 Redis 一次只运行一条命令。其次我们应该减少长命令,哪些是长命令呢?如 KEYS、FLUSHALL、FLUSHDB、Slow Lua Script、MULTI/EXEC、Operate Big Value(Collection)。最后说明一点,其实 Redis 不只是单线程,如果你去读源码,你就会发现有些指令绝不是单线程能够做的。如 Fysnc File Descriptor、Close File Descriptor等。

7 种数据类型的使用

字符串 String

Redis 的 key 没什么好说,值得一提的就是 value 的五种数据类型。分别是字符串类型、数字、二进制、和 JSON 类型的数据。

那我们在实际生产环境中有哪些场景使用呢?如缓存、计数器(每次加 1 的计数)、分布式锁等场景都能看到。

接着我将列出与该数据类型相关的命令及使用说明。

1.GET、SET 和 DEL

这是 Redis 最简单的命令,如果你要得到一个 value 的值,只需要 GET key,就 ok 了。如果你要设置某个 key 的值,那就 SET key value,搞定。

2.INCR、DECR、INCRBY、DECRBY

  • INCR key:就是 key 自增 1,不存在,自增后 get(key)=1;
  • DECR key:就是 key 自减 1,不存在,自减后返回 -1;
  • INCRBY key k:自增 k ,不存在,则返回 k;
  • DECRBY key k:自减 k 。
实际使用

如果你想访问某个主页的访问量,那可以用 INCR id:pageview。

我们实际开发中,常常会使用下面的伪代码。

public VideoInfo get(long id){
    String redisKey = redisPrefix + id;
    VideoInfo videoInfo = redis.get(redisKey);
    if(videoInfo == null){
        videoInfo = mysql.get(id);
        if(videoInfo != null){
            // 序列化
            redis.set(redisKey, serialize(videoInfo));
        }
    }
    return videoInfo;
}

3.SET、SETNX、SET xx

  • SET key value:不管 key 是否存在,都设置;
  • SETNX key value:key 不存在,才设置(相当于 add);
  • SET key value xx:key 存在,才设置(相当于 update)。

实际操作,见如下代码。

exists php --> 0
set php good -->OK
setnx php bad -->0
set php best xx -->ok

exists lua --> 0
set lua hehe xx -->(nil)

4.MGET、MSET

  • MGET key1 key2 key3 …:批量获取 key,原子操作;
  • MSET key1 val2 key2 val2 key3 val3:批量设置 key-value。

实际开发的过程中,我们通常使用 MGET,因为 MGET 只有 1 次网络时间和 n 次命令时间。但是如果你使用 GET 的话,就是 n 次网络时间和 n 次命令时间。

使用 MGET 效率更高。

5.GETSET、APPEND、STRLEN

  • GETSET key newvalue:set key newvalue 并返回旧的 value,旧的 value 会被删除;
  • APPEND key value:将 value 追加到旧的 value 上;
  • STRLEN key:返回字符串的长度(注意中文)。

6.INCRBYFLOAT、GETRANGE、SETRANGE

  • INCRBYFLOAT key 3.5:在 key 上追加对应的值 3.5;
  • GETRANGE key start end:获取字符串指定下标所有的值;
  • SETRANGE key index value:设置指定下标所有对应的值。

哈希 Hash

说到 Hash,就要说到为什么我们要使用 Hash。我们在使用字符串的数据类型的时候,如果我们存储的是个对象,比如某个图书馆的会员,里面存储着会员的姓名、年龄、身份证信息、地址、借阅书籍、借阅时间……一系列的属性。

如果我们用 String 来存储的话,那我们就需要每次都序列化这个字符串,每次只要一修改某个属性,我们就要把一串属性都覆盖一遍。这样是不是非常麻烦?

这个时候,哈希就应运而生了。Hash 相当于 value 是一个 Map,里面的所有属性我们都可以单独新增、修改或者删除,而不需要像字符串那样全部覆盖操作。

常用的命令有HGET、HSET、HGETALL。

  • HGET key field:获取存储在哈希表中指定字段的值。
  • HSET key field value:将哈希表 key 中的字段 field 的值设为 value。
  • HGETALL key:获取在哈希表中指定 key 的所有字段和值,生产环境不常用

列表 List

List 是一种简单的字符串的集合,是有顺序的。在实际生产环境中,我们时常会使用它。比如当我们需要获取某个数据的列表(例如粉丝列表)。

由于 Redis 的 List 是链表结构,我们可以非常轻松的实现消息排行等功能,还能用于消息队列等功能。

常用的命令有LPUSH、RPUSH、LPOP、RPOP、LRANGE。

  • LPUSH key value1 [value2]:将一个或多个值插入到列表头部;
  • RPUSH key value1 [value2]:在列表中添加一个或多个值;
  • LPOP key:移出并获取列表的第一个元素;
  • RPOP key:移除并获取列表最后一个元素;
  • LRANGE key start stop:获取列表指定范围内的元素。

集合 Set

Set 和 List 最大的不同是无序,Set 是没有顺序的。集合成员是唯一的,这就意味着集合中不能出现重复的数据。

常用命令有SADD、SCARD、SNENVERS、SPOP。

  • SADD key member1:向集合添加一个或多个成员;
  • SCARD key:获取集合的成员数;
  • SMEMBERS key:返回集合中的所有成员;
  • SPOP key:移除并返回集合中的一个随机元素。

Sorted Set 有序集合

Sorted Set 和 Set 最大的不同是前者是自动排序的,而后者是无序的。如果你需要一个有序的,但是不重复的数据结构的,就可以使用sorted set。

常用的命令有 ZADD、ZRANGE、ZREM、ZCARD。

  • ZADD key score1 member1:向有序集合添加一个或多个成员,或者更新已存在成员的分数;
  • ZRANGE key start stop:通过索引区间返回有序集合成指定区间内的成员;
  • ZREM key member:移除有序集合中的一个或多个成员;
  • ZCARD key:获取有序集合的成员数。

Pub/Sub 发布订阅

即发布(Publish)与订阅(Subscribe)。在 Redis 中,你可以设定对某一个 key 值进行消息发布及消息订阅,当 key 的值进行了消息发布后,所有订阅它的客户端都会收到相应的消息,这类似于 QQ、微信。

常用的命令有PSUBSCRIBE、PUBSUB、PUBLISH、SUBSCRIBE。

  • PSUBSCRIBE pattern:订阅一个或多个符合给定模式的频道;
  • PUBSUB subcommand:查看订阅与发布系统状态;
  • PUBLISH channel message:将信息发送到指定的频道;
  • SUBSCRIBE channel:订阅给定的一个或多个频道的信息;
  • UNSUBSCRIBE [channel [channel …]]:指退订给定的频道。

Transactions 事务

我们一般认为 NoSQL 数据库都没有事务,恐怕要让你失望了。Redis 就支持事务,但并不是我们一般意义上的事务,如果你执行 exec 命令,途中断电或者服务器挂掉了,我们还是会发现 Redis 里一部分插入了,一部分未插入。

不过 Redis 提供了 WATCH 命令,我们可以对某个 key 来 watch 一下,然后再执行 Transactions。如果这个被Watch 的值进行了修改,那么这个 Transactions 会发现并拒绝执行。

redis 127.0.0.1:6381> MULTI
OK

redis 127.0.0.1:6381> SET book-name "JAVA Programming Mastering Series"
QUEUED

redis 127.0.0.1:6381> GET book-name
QUEUED

redis 127.0.0.1:6381> SADD tag "java" "Programming" "Mastering Series"
QUEUED

redis 127.0.0.1:6381> SMEMBERS tag
QUEUED

redis 127.0.0.1:6381> EXEC
1) OK
2) "JAVA Programming Mastering Series"
3) (integer) 3
4) 1) "java"
   2) "Programming"
   3) "Mastering Series"

常用命令有 MULTI、EXEC、DISCARD。

  • MULTI:标记一个事务块的开始;
  • EXEC:执行所有事务块内的命令;
  • DISCARD:取消事务,放弃执行事务块内的所有命令;
  • UNWATCH:取消 WATCH 命令对所有 key 的监视;
  • WATCH key:监视 key,如果在事务执行之前 key 被其他命令所改动,那么事务将被打断。

Redis 作为一个数据库,很多开发者还可以单独使用它。事实上,更多时候 Redis 是在数据库和代码中间作为一个中间件使用,如果你发现你目前的数据库出现瓶颈,那么你就可以通过 Redis 来优化。

上一篇
下一篇
目录