写给redis零基础同学的上手攻略

2,178 阅读10分钟

工作以来,一直做前端相关的事情,对后端了解很少,之前经常听server同学讨论中会提到redis,只大概知道是个关于缓存的东西。最近在升级一个内部平台的时候。遇到了些问题拿不准方案,跟组内大佬讨论过后,大佬的建议是通过redis解决,于是简单学习了一波,基本包括了一个对redis零基础的前端同学,简单进行上手使用的过程,整理出来,希望对其他零基础同学有帮助。

简介

可能你还不知道redis是个啥,想使用它肯定要有个大概了解。简单来说,redis是一个key-value的存储系统,更通俗一点,你可以把它理解成一个将数据存在内存的数据库。

使用redis和使用数据库也有点像,它由两部分组成:服务端和客户端,多个客户端可以连接一个服务端,客户端可以读取和更改服务端存储的内存数据,以此就能够达到记录和共享数据的目的。

服务端相关内容

下载和安装

redis的下载安装很简单,找到你想安装的目录,然后依次执行下面的命令就搞定了:

$ wget http://download.redis.io/releases/redis-5.0.5.tar.gz
$ tar xzf redis-5.0.5.tar.gz
$ cd redis-5.0.5
$ make

执行完上面的几个命令后,在**redis-5.0.5/目录下,会出现一个/src/**目录,记住这个目录,之后我们会用到它里面的文件。

配置

redis服务端已经下载和安装到机器上了,接下来是进行配置。

首先要了解,redis服务的所有配置信息,都记录在redis.conf文件中,因此只需要关心这个一个文件的内容就ok了,这个文件的位置在**redis-5.0.5/**内,通过vim打开conf文件,可以看到内容包含大段注释,并包含着一些配置信息。

下面举几个需要了解的配置,上手阶段应该够用了:

# 设置redis服务的监听端口,默认为6379
port 6666

# 设置连接密码,设置后通过客户端连接时,需要携带该密码,增加一层安全限制
requirepass 'your password'

# 绑定ip,含义是在哪个ip启动服务
# 注意:如果设置为127.0.0.1,则只有本机可以访问当,设置为0.0.0.0,则为任何机器均可访问
bind 0.0.0.0

# 是否开启守护进程,yes表示启动服务时在后台运行,默认为no
daemonize yes

别嫌少,够用就行,实践中用到新配置会补充进来。

启动服务

终于安装和配置完了,下面就可以启动服务了,是不是很开心,启动服务有2种常用方式:

  • 正常启动
  • 通过后台进程方式启动

正常启动

先说正常启动,进入到之前让你记住的**/src/目录下,可以找到一个名为redis-server**的文件,直接运行这个文件就ok了,启动时可以通过指定配置文件,来规定redis服务的相关参数,如果不指定配置文件,会走默认配置:

# 直接启动redis,没有指定配置文件,因此会走默认配置
./redis-server

# 指定了配置文件,会根据配置文件内容启动服务
./redis-server ./redis.conf

启动后在终端中看到如下界面,说明启动成功了:

这种启动方式,启动后需要一直保持终端窗口运行,关闭窗口后服务也会停止,因此不太方便。

通过后台进程方式启动

通过后台进程方式启动redis,关闭终端窗口后redis服务也能正常运行,会方便很多。

redis提供了一个开启守护进程的能力,只需要将上文介绍过的配置项daemonize设置为yes就ok了。

需要注意的一点是,修改配置项后,在启动redis服务时,一定要指定redis.conf文件为刚才设置配置的文件,此时启动服务不会出现之前截图的字符画,但通过客户端可以正常连接。

关闭服务

如果是正常启动,ctrl+c就可以关闭服务。

如果通过后台进程方式启动,可以通过redis-cli来完成服务的关闭和重启:

# 关闭redis服务,需要携带一些参数
# -h 启动服务绑定的ip,对应redis.conf中的bind
# -p 端口号,对应redis.conf中的port
# -a 含义是auth,当redis.conf中requirepass设置密码时需要
# 提示:如果在redis.conf设置了密码而没有携带-p参数,会提示(error) NOAUTH Authentication required.
./redis-cli -h 0.0.0.0 -p 6666 -a 'your password' shutdown

客户端相关

redis服务端已经在机器中跑起来了,接下来介绍下客户端,因为这是一篇写给前端同学的上手攻略,因此这里介绍的客户端是基于node的,使用非常简单和方便。

直接上代码:

let redis = require("redis");

let client = redis.createClient({
    host: 'your host or ip',
    port: 6666,
    password: 'your password'
});

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

client.on("ready", function (res) {
    console.log("Ready " + res);
});

看代码应该很容易理解,引入redis模块,通过createClient建立连接,连接时可以携带参数,需要携带哪些参数取决于服务端在redis.conf中的配置,都是一一对应的,比如上面的例子中,指定了连接的ip、端口和密码。

客户端连接后,就可以对redis内的数据进行读取和修改了,下面会单独介绍redis中的数据类型及相关操作。

数据类型及常用API

有了服务端和客户端,当然就是要上手使用了,基本使用了解两个大方面就OK,一方面是数据类型,一方面是常用API。

redsi内共有5中数据类型,分别是:

  • String 字符串
  • List 双向链表
  • Hash
  • Set
  • Sortedset

String

String类型很容易理解,和JS中的string类似,是动态的,定义之后可以修改。

常用的操作API:

设置字符串内容

// [API] set(stringKey, stringValue)
client.set('dogName', 'my dog name is timo');

获取字符串内容

//  [API] get(stringKey, callback)
client.get('dogName', (err, res)=> {
    console.log(res); // log => 'my dog name is timo'
});

获取字符串内容长度

// [API] strlen(stringKey, callback)
client.strlen('dogName', (err, res) => {
    console.log(res); // log => 19
});

获取字符串中指定位置的内容(子串)

// [API] getrange(stringKey, start, end, callback)
client.getrange('dogName', 0, 5, (err, res) => {
    console.log(res); // log => 'my dog'
});

覆盖字符串中子串内容

// [API] setrange(stringKey, start, newValue, callback)
// 说明:API仅指定起始位置,会根据传入新内容的长度,自动覆盖对应长度的内容
// API返回修改后的字符串长度
client.get('dogName', (err, res) => {
    console.log(res); // 先获取内容 log => 'my dog name is timo'
});
client.setrange('dogName', 0, 'My cat', (err, res) => {
    console.log(res); // 覆盖子串内容 log => 19
});
client.get('dogName', (err, res) => {
    console.log(res); // 再次获取内容 log => 'My cat name is timo'
});

追加子串

// 
client.get('dogName', (err, res) => {
    console.log(res); // log => 'my dog name is timo'
});
client.append('dogName', '. It is very lovely.', (err, res) => {
    console.log(res); // log => 39
});
client.get('dogName', (err, res) => {
    console.log(res); // log => 'my dog name is timo. It is very lovely.'
});

删除字符串

// [API] del(stringKey, callback)
client.del('dogName', (err, res) => {
    
});

过期时间

// [API] expire(stringKey, callback)
// 设置过期时间后,到时间将自动删除
client.del('dogName', (err, res) => {
    
});

List

list数据结构本质是个双向链表,这就表示它可以方便的从两端进行数据增加和删除。

常用的一些API:

从两侧增加或删除数据

// 从右侧插入
client.rpush('listData', 'timo', 'anni', (err ,res) => {
    console.log(res); // log => 2 
    // 此时listData数据为:['timo', 'anni']
});

// 从左侧插入
client.lpush('listData', 'hock', 'jack', (err ,res) => {
    console.log(res); // log => 4
    // 此时listData数据为:['jack', 'hock', timo', 'anni']
});

// 从右侧删除
client.rpop('listData', (err ,res) => {
    console.log(res);  // log => 3
    // 此时listData数据为:['jack', hock', timo']
});

// 从左侧删除
client.lpop('listData', (err ,res) => {
    console.log(res); // log => 2
    // 此时listData数据为:[hock', timo']
});

获取长度

client.llen('listData', (err, res) => {
    console.log(res); // log => Number
});

获取list内指定位置元素

// listData => ['jack', 'hock', timo', 'anni']

// 读取指定元素
client.lrange('listData', 1, 2, (err, res) => {
    console.log(res);  // log => ['hock', timo']
});

// 通过负下表获取全部元素
client.lrange('listData', 0, -1, (err, res) => {
    console.log(res);  // log => ['jack', 'hock', timo', 'anni']
});

Hash

Hash是二维数据,一维是个数组,二维是个链表,常用API:

增加

// 增加单个元素
client.hset('hashData', 'name', 'timo', (err ,res) => {
    console.log(res); // log => 1  
});

// 增加多个元素
client.hmset('hashData', 'name', 'timo', 'age', '3', (err ,res) => {
    console.log(res); //log => OK
});

查询

// hashData => {'name': 'timo', 'age': '3'}

// 查询单个元素value
client.hget('hashData', 'name', (err ,res) => {
    console.log(res); // 'timo'
});

// 查询多个元素的value
client.hmget('hashData', 'name', 'age', (err ,res) => {
    console.log(res); // ['timo', '3']
});

// 查看整个Hash数据
client.hgetall('hashData', (err ,res) => {
    console.log(res); //  {'name': 'timo', 'age': '3'}
});

// 查看所有key
client.hkeys('hashData', (err ,res) => {
    console.log(res); // ['name', 'age']
});

// 查看所有value
client.hvals('hashData', (err ,res) => {
    console.log(res); // ['timo', '3']
});

删除

// 指定单个key进行删除 
client.hdel('hashData', 'name', (err ,res) => {
    console.log(res); // log => 1
});

// 指定多个key进行删除 
client.hdel('hashData', 'name', 'age', (err ,res) => {
    console.log(res); 
   // log => 1
   // log => 1
});

判断元素是否存在

// hashData => {'name': 'timo', 'age': '3'}

// 判断元素是否存在 存在返回1 不存在返回0
client.hexists('hashData', 'name', (err ,res) => {
    console.log(res); // log => 1
});

Set 集合

Set类型是String的无序集合,常用API如下:

增加

// 增加单个元素
client.sadd('set', 'timo', (err ,res) => {
    console.log(res); // log => 1
});

// 增加多个元素
client.sadd('set', 'anni', 'jack', (err ,res) => {
    console.log(res); // log => 2
});

读取

//  读取所有元素
client.smembers('set', (err ,res) => {
    console.log(res);   // [ 'timo', 'jack', 'anni']
});

//  读取集合长度
client.scard('set', (err ,res) => {
    console.log(res);  // log => 3
});

// 随机读取count个元素,默认为1
client.srandmember('set', 2, (err ,res) => {
    console.log(res);  // log => ['xxxx', 'xxxx']
});

删除

// 增加单个元素
client.srem('set', 'timo', (err ,res) => {
    console.log(res); // log => 1
});

// 增加多个元素
client.srem('set', 'anni', 'jack', (err ,res) => {
    console.log(res); // log => 2
});

// 随机删除一个元素
client.spop('set', (err ,res) => {
    console.log(res);  // log => ‘xxx’
});

查询元素是否存在

// 判断元素是否存在,存在返回1,不存在返回0
client.sismember('set', 'timo', (err ,res) => {
    console.log(res);  // log => 1
});

sorted set 有序集合

和set类型一样,sorted set也是String的集合,不同点在于sortedset每个元素都会关联一个权重值,元素自身是唯一不允许重复的,但权重可以重复,有个这个权重值,就可以对集合内的元素进行排序了。

常用API:

增加

// 增加单个元素
client.zadd('sortesetData', 0, 'timo', (err ,res) => {
    console.log(res); // log => 0
});

// 增加多个元素
client.zadd('sortesetData', 1.5, 'anni', 2, 'jack', 5, 'bob', 6.5, 'xer', (err ,res) => {
    console.log(res); // log => 3
});

基础查询

// 查询长度
client.zcard('sortesetData', (err ,res) => {
    console.log(res); // log => 5
});

// 查询元素权重
client.zscore('sortesetData', 'anni', (err ,res) => {
    console.log(res); // log => 1.5
});

基础排名

// 查询元素正向排名
client.zrank('sortesetData', 'jack', (err ,res) => {
    console.log(res); // log => 2
});

// 查询元素负向排名
client.zrevrank('sortesetData', 'anni', (err ,res) => {
    console.log(res); // log => 3
});

根据排名获取元素和权重

// 获取所有元素
client.zrange('sortesetData', 0, -1, (err ,res) => {
    console.log(res); // log => [ 'timo', 'anni', 'jack', 'bob', 'xer' ]
});

// 获取所有元素和权重
client.zrange('sortesetData', 0, -1, 'withscores', (err ,res) => {
    console.log(res);  // log => [ 'timo', '0', 'anni', '1.5', 'jack', '2', 'bob', '5', 'xer', '6.5' ]
});

// 按照负向顺序 获取所有元素和权重
client.zrevrange('sortesetData', 0, -1, 'withscores', (err ,res) => {
    console.log(res); // log => [ 'xer', '6.5', 'bob', '5', 'jack', '2', 'anni', '1.5', 'timo', '0' ]
});

根据权重范围获取元素列表

// 获取权重0-5的元素 正向排序(从小到大)
client.zrangebyscore('sortesetData', 0, 5, 'withscores', (err ,res) => {
    console.log(res); // log => [ 'timo', '0', 'anni', '1.5', 'jack', '2', 'bob', '5' ]
});

// 获取权重负无穷到正无穷的元素 正向排序(从小到大)
client.zrangebyscore('sortesetData', '-inf', '+inf', 'withscores', (err ,res) => {
    console.log(res); // log => [ 'timo', '0', 'anni', '1.5', 'jack', '2', 'bob', '5', 'xer', '6.5' ]
});

// 获取权重正无穷到负无穷的元素 负向排序(从大到小)
client.zrevrangebyscore('sortesetData', '+inf', '-inf', 'withscores', (err ,res) => {
    console.log(res); // log => [ 'xer', '6.5', 'bob', '5', 'jack', '2', 'anni', '1.5', 'timo', '0' ]
});

删除

// 删除元素
client.zrem('sortesetData', 'anni', (err ,res) => {
    console.log(res);  // log => 1
});

// 根据排名删除
client.zremrangebyrank('sortesetData', 0, 1, (err ,res) => {
    console.log(res);  // log => 2
});

// 根据权重删除
client.zremrangebyscore('sortesetData', 0, 5, (err ,res) => {
    console.log(res); // log => 4
});

资料

参考的一些文章:

示例放到了github: