美文网首页
Redis第2课:API 的理解与使用

Redis第2课:API 的理解与使用

作者: 米饭超人 | 来源:发表于2018-08-27 16:56 被阅读319次

    前面我们大致了解了 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 来优化。

    相关文章

      网友评论

          本文标题:Redis第2课:API 的理解与使用

          本文链接:https://www.haomeiwen.com/subject/vjdtwftx.html