Redis 是一个高性能的key-value数据库。既可内存存储又可持久化,着实牛逼。它提供五种数据类型:string, hash, list, set及zset(sorted set)。

安装

好烦,每次学习一项新工具,总要先捣腾好一阵子安装和配置。其实我已经迫不及待地想要一窥其风骚的走位和操作了。可是稍安勿躁,该做的还是一步都不能省哪!

获取源码、解压、进入源码目录、编译安装:

1
2
3
4
$ wget http://download.redis.io/releases/redis-3.0.6.tar.gz
$ tar xzf redis-3.0.6.tar.gz
$ cd redis-3.0.6
$ make

编译好后执行:

1
$ src/redis-server &

启动客户端shell:

1
2
3
4
5
6
$ src/redis-cli
127.0.0.1:6379> set foo bar
OK
127.0.0.1:6379> get foo
"bar"
redis>

我擦,我没有看错吧,已经可以进行存取了!简单到辱没智商哪!

数据结构

没想到,这么快就步入正题了,开头说过,redis是一种高性能key-value存储系统,有五种数据类型,跟mysql一样,redis中的操作指令并不区分大小写。

  1. 字符串(string)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    127.0.0.1:6379> set mynum "2"
    OK
    127.0.0.1:6379> get mynum
    "2"
    127.0.0.1:6379> incr mynum
    (integer) 3
    127.0.0.1:6379> get mynum
    "3"
    127.0.0.1:6379> incrby mynum 5
    "8"

    set key value 存储一个键值对,get key 获取一个键值对。

    可知,在遇到数值操作时,redis会将字符串类型转成数值。incr等指令本身具有原子操作的特性,我们可以利用redis的incrincrbydecrdecrby等指令来实现原子计数的效果。

  2. 字符串列表(list)

    redis中的list在底层实现上不是数组而是链表,这说明了什么呢?说明了在list上插入一个新元素(无论行首行尾行中),其时间复杂度为O(1)。比如用lpush在10个元素的list头部插入新元素,和在上千万元素的list头部插入新元素的速度是相同的。

    然而有利总有弊嘛,list取元素的速度就没数组那么快了,时间复杂度为O(n)。

    list的常用操作包括lpushrpushlrange等。我们可以用lpush在list的左侧插入一个新元素,用rpush在list的右侧插入一个新元素,用lrange命令从list中指定一个范围来提取元素。例如:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    #新建一个list叫做mylist,并在列表头部插入元素"1" 
    127.0.0.1:6379> lpush mylist "1"
    #返回当前mylist中的元素个数
    (integer) 1
    #在mylist右侧插入元素"2"
    127.0.0.1:6379> rpush mylist "2"
    (integer) 2
    #在mylist左侧插入元素"0"
    127.0.0.1:6379> lpush mylist "0"
    (integer) 3
    #列出mylist中从编号0到编号1的元素
    127.0.0.1:6379> lrange mylist 0 1
    1) "0"
    2) "1"
    #列出mylist中从编号0到倒数第一个元素
    127.0.0.1:6379> lrange mylist 0 -1
    1) "0"
    2) "1"
    3) "2"

    list的应用相当广泛,例如:

    1. 可以利用list来实现一个消息队列,而且可以确保先后顺序,不必像mysql那样还需要通过order by来进行排序。
    2. 利用lrange可以很方便的实现分页功能。
    3. 在博客系统中,每篇博文的评论也可以存入一个单独的list中。
  3. 字符串集合(set)

    redis的集合,是一种无序的集合,集合中的元素没有先后顺序,集合中没有重复元素。

    集合相关的操作也很丰富,如添加新元素(sadd)、删除已有元素(srem)、取交集(sinter)、取并集(sunion)、取差集(sdiff)、返回集合大小(scard)等。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    #向集合myset中加入一个新元素"one",返回1存储成功,返回0表示元素已存在不能再存储
    127.0.0.1:6379> sadd myset "one"
    (integer) 1
    127.0.0.1:6379> sadd myset "two"
    (integer) 1
    127.0.0.1:6379> sadd myset "two"
    (integer) 0
    #列出集合myset中的所有元素
    127.0.0.1:6379> smembers myset
    1) "one"
    2) "two"
    #判断元素'one'是否在集合myset中,返回1表示存在
    127.0.0.1:6379> sismember myset "one"
    (integer) 1
    #判断元素'three'是否在集合myset中,返回0表示不存在
    127.0.0.1:6379> sismember myset "three"
    (integer) 0
    #新建一个新的集合yourset
    127.0.0.1:6379> sadd yourset "1"
    (integer) 1
    127.0.0.1:6379> sadd yourset "2"
    (integer) 1
    127.0.0.1:6379> smembers yourset
    1) "1"
    2) "2"
    #对两个集合求并集
    127.0.0.1:6379> sunion myset yourset
    1) "1"
    2) "one"
    3) "2"
    4) "two"
    #求集合大小
    127.0.0.1:6379> scard yourset
    (integer) 2

    常见的投票功能可以使用集合来实现,比如有个投票功能,每个用户只能投一次,将投过票的用户的id存在集合里,获取集合的大小可以获取投票数。

  4. 有序字符串集合(sorted set)

    redis体贴地提供了有序集合,其中的每个元素都关联一个序号(score),这是排序的依据。

    通常,我们将有序集合叫做zset,是因为在redis中,有序集合相关的操作指令都是以z开头的,如zrange、zadd、zrevrange、zrangebyscore等。

    话不多说,看栗子吧:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    #向myzset中新增一个元素'java',赋予序号1
    127.0.0.1:6379> zadd myzset 1 java
    (integer) 1
    #向myzset中新增一个元素'python',赋予序号3
    127.0.0.1:6379> zadd myzset 3 python
    (integer) 1
    #向myzset中新增一个元素'php',赋予序号2
    127.0.0.1:6379> zadd myzset 2 php
    (integer) 1
    #列出myzset的所有元素,同时列出其序号,可以看出myzset是有序的。
    127.0.0.1:6379> zrange myzset 0 -1 withscores
    1) "java"
    2) "1"
    3) "php"
    4) "2"
    5) "python"
    6) "3"
    #只列出myzset的元素
    127.0.0.1:6379> zrange myzset 0 -1
    1) "java"
    2) "php"
    3) "python"

    说个使用场景吧。比如你的博客文章,以发布时的时间戳为score,以文章id为值存入zset中,就可以按时间顺序进行正序或倒序(zrevrange)的排列了,还可以获取指定时间区间的博客文章(使用zrangebyscore)。

  5. 哈希(hash)

    hash存的是字符串和字符串值之间的映射,比如一个用户要存储其全名、姓氏、年龄等,就特别适合使用哈希。举个栗子:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    #建立hash,并赋值
    127.0.0.1:6379> hmset user:001 username iwhynot password memeda age 25
    OK
    #列出hash的内容
    127.0.0.1:6379> hgetall user:001
    1) "username"
    2) "iwhynot"
    3) "password"
    4) "memeda"
    5) "age"
    6) "25"
    #更改hash的某一个值
    127.0.0.1:6379> hset user:001 password 12345
    (integer) 0
    #再次列出hash的内容
    127.0.0.1:6379> hgetall user:001
    1) "username"
    2) "iwhynot"
    3) "password"
    4) "12345"
    5) "age"
    6) "25"
    #获取hash的某一个键的值
    127.0.0.1:6379> hget user:001 password
    "12345"

另外,说一下一些常用的命令:

  • TYPE key — 用来获取某key的类型
  • KEYS pattern — 匹配所有符合模式的key,比如KEYS * 就列出所有的key了,当然,复杂度O(n)
  • RANDOMKEY - 返回随机的一个key
  • RENAME oldkey newkey — key也可以改名

列表操作,精华

  • RPUSH key string — 将某个值加入到一个key列表末尾
  • LPUSH key string — 将某个值加入到一个key列表头部
  • LLEN key — 列表长度
  • LRANGE key start end — 返回列表中某个范围的值,相当于mysql里面的分页查询那样
  • LTRIM key start end — 只保留列表中某个范围的值
  • LINDEX key index — 获取列表中特定索引号的值,要注意是O(n)复杂度
  • LSET key index value — 设置列表中某个位置的值
  • LPOP key
  • RPOP key — 和上面的LPOP一样,就是类似栈或队列的那种取头取尾指令,可以当成消息队列来使用了

集合操作

  • SADD key member — 增加元素
  • SREM key member — 删除元素
  • SCARD key — 返回集合大小
  • SISMEMBER key member — 判断某个值是否在集合中
  • SINTER key1 key2 ... keyN — 获取多个集合的交集元素
  • SMEMBERS key — 列出集合的所有元素

详尽的指令介绍可以在这里尽情查看。http://redis.io/commands

redis配置

当然,配置是一个很核心的部分。然而,我并不想多说。

默认安装的redis.conf配置文件,里面的英文注释已经非常详细了,就不需要我翻译了(其实是我英文不好)。

redis在nodejs中的使用

安装node_redis模块:

1
$ npm install redis

使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var redis = require("redis"),
client = redis.createClient();

client.on("error", function (err) {
console.log("Error " + err);
});

client.set("string key", "string val", redis.print);
client.hset("hash key", "hashtest 1", "some value", redis.print);
client.hset(["hash key", "hashtest 2", "some other value"], redis.print);
client.hkeys("hash key", function (err, replies) {
console.log(replies.length + " replies:");
replies.forEach(function (reply, i) {
console.log(" " + i + ": " + reply);
});
client.quit();
});

更多风骚的用法,查看这里吧https://github.com/NodeRedis/node_redis