本文是Redis系列文章的第二篇,将详细介绍Redis的相关知识,包括Redis简介,优势,以及五种数据结构使用实例等,安装和配置请查看《Redis系列(一):安装和配置》。

1. Redis简介

Redis是一个开源的、使用C语言编写的、支持网络交互的、可基于内存也可持久化的Key-Value数据库。它支持5种类型的数据结构,如字符串(strings),散列(hashes),列表(lists),集合(sets),有序集合(sorted sets)。除此之外,通过复制、持久化(persistence)和客户端分片(client-side sharding)等特性,用户可以很方便地将Redis扩展成一个能够包含数百GB数据、每秒处理上百万次请求的系统。
Redis的作者叫Salvatore Sanfilippo,来自意大利的西西里岛,现在居住在卡塔尼亚;目前供职于Pivotal公司。Redis开源项目的源码在GitHub的地址《Redis》,有兴趣的朋友可以看看。

2. Redis优势

  1. Redis是非常快的,由于Redis只使用单核,而Memcached可以使用多核,所以在比较上,平均每一个核上Redis在存储小数据时比Memcached性能更高。每秒可以执行大约110000设置操作,81000个/每秒的读取操作。
  2. 支持丰富的数据类型:Redis支持最大多数开发人员已经知道如列表,集合,可排序集合,哈希等数据类型。这使得在应用中很容易解决的各种问题,因为我们知道哪些问题处理使用哪种数据类型更好解决。
  3. 操作都是原子的:所有 Redis 的操作都是原子,从而确保当两个客户同时访问 Redis 服务器得到的是更新后的值(最新值)。
  4. 数据的一致性方面:Memcached提供了cas命令来保证.而Redis提供了事务的功能,可以保证一串 命令的原子性,中间不会被任何操作打断
  5. 数据持久化:Redis提供了两种持久化的方式,分别是RDB(Redis DataBase)和AOF(Append Only File);可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用。

3. Redis与其他数据库对比

Redis属于非关系数据库,Redis不使用表,它的数据库也不会预定义或者强制去要求用户对Redis存储的不同数据进行关联。
高性能键值缓存服务器Memcached也经常被拿来与Redis进行比较:这两者都可用于存储键值映射,彼此的性能也相差无几,但是Redis能够自动以两种不同的方式将数据写入硬盘,并且Redis除了能存储普通的字符串键之外,还可以存储其他4种数据结构,而Memcached只能存储普通的字符串键。这些不同之处使得Redis可以用于解决更为广泛的问题,并且既可以用作主数据库(primary database)使用,又可以作为其他存储系统的辅助数据库(auxiliary database)使用。

下面表格将介绍Redis与其他数据库的功能区别:

名称 类型 数据存储选项 查询类型 附加功能
Redis 使用内存存储(in-memory)的非关系数据库 字符串、列表、集合、散列表、有序集合 每种数据类型都有自己的专属命令,另外还有批量操作(bulk operation)和不完全(partial)的事务支持 发布与订阅,主从复制(master/slave replication),持久化,脚本(存储过程,stored procedure)
memcached 使用内存存储的键值缓存 键值之间的映射 创建命令、读取命令、更新命令、删除命令以及其他几个命令 为提升性能而设的多线程服务器
MySQL 关系数据库 每个数据库可以包含多个表,每个表可以包含多个行;可以处理多个表的视图(view);支持空间(spatial)和第三方扩展 SELECTINSERTUPDATEDELETE、函数、存储过程 支持ACID性质(需要使用InnoDB),主从复制和主主复制 (master/master replication)
PostgreSQL 关系数据库 每个数据库可以包含多个表,每个表可以包含多个行;可以处理多个表的视图;支持空间和第三方扩展;支持可定制类型 SELECTINSERTUPDATEDELETE、内置函数、自定义的存储过程 支持ACID性质,主从复制,由第三方支持的多主复制(multi-master replication)
MongoDB 使用硬盘存储(on-disk)的非关系文档存储 每个数据库可以包含多个表,每个表可以包含多个无schema(schema-less)的BSON文档 创建命令、读取命令、更新命令、删除命令、条件查询命令等 支持map-reduce操作,主从复制,分片,空间索引(spatial index)

4. Redis数据结构

Redis可以存储键与5种不同数据结构类型之间的映射,这5种数据结构类型分别为STRING(字符串)、LIST(列表)、SET(集合)、HASH(散列)和ZSET(有序集合)。有一部分Redis命令对于这5种结构都是通用的,如DEL、TYPE、RENAME等;但也有一部分Redis命令只能对特定的一种或者两种结构使用。

下面表格将介绍5种数据结构:

结构类型 结构存储的值 结构的读写能力
String 可以是字符串、整数或者浮点数 对整个字符串或者字符串的其中一部分执行操作;对整数和浮点数执行自增(increment)或者自减(decrement)操作
List 一个链表,链表上的每个节点都包含了一个字符串 从链表的两端推入或者弹出元素;根据偏移量对链表进行修剪(trim);读取单个或者多个元素;根据值查找或者移除元素
Set 包含字符串的无序收集器(unordered collection),并且被包含的每个字符串都是独一无二、各不相同的 添加、获取、移除单个元素;检查一个元素是否存在于集合中;计算交集、并集、差集;从集合里面随机获取元素
Hash 包含键值对的无序散列表 添加、获取、移除单个键值对;获取所有键值对
Zset(有序集合) 字符串成员(member)与浮点数分值(score)之间的有序映射,元素的排列顺序由分值的大小决定 添加、获取、删除单个元素;根据分值范围(range)或者成员来获取元素
4.1 String—字符串

String是最简单的类型,一个key对应一个value,String类型是二进制安全的;这意味着它们没有任何特殊终端字符来确定长度,所以可以存储任何长度为 512 兆的字符串;可以包含任何数据,比如jpg图片或者序列化的对象;所有常用命令的复杂度都是O(1),普通的Get/Set方法,可以用来做Cache,存Session,为了简化架构甚至可以替换掉Memcached。示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
127.0.0.1:6379> set name 'MinHow' //设置name的值为MinHow
OK
127.0.0.1:6379> get name //获取name的值
"MinHow"
127.0.0.1:6379> del name //删除key
(integer) 1
127.0.0.1:6379> set num 1
OK
127.0.0.1:6379> incr num //将 num 中储存的数字值增一
(integer) 2
127.0.0.1:6379> get num
"2"

4.2 List—列表

Redis 列表是简单的字符串列表,通过插入顺序排序;可以添加一个元素到 Redis 列表的头部或尾部;Redis列表可执行的操作和很多编程语言里面的列表操作非常相似:LPUSH命令和RPUSH命令分别用于将元素推入列表的左端(left end)和右端(right end);LPOP命令和RPOP命令分别用于从列表的左端和右端弹出元素;LINDEX命令用于获取列表在给定位置上的一个元素;LRANGE命令用于获取列表在给定范围上的所有元素。示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
127.0.0.1:6379> lpush minhow redis //新建一个list叫minhow,并在列表头部插入元素redis
(integer) 1
127.0.0.1:6379> rpush minhow mysql //在minhow右侧插入元素mysql
(integer) 2
127.0.0.1:6379> lpush minhow mongodb
(integer) 3
127.0.0.1:6379> rpush minhow oracle
(integer) 4
127.0.0.1:6379> lrange minhow 0 1 //列出编号0到1的元素
1) "mongodb"
2) "redis"
127.0.0.1:6379> lindex minhow 3 //列出编号3的元素
"oracle"

4.3 Set—集合

Redis的集合是string类型的无序集合;集合成员是唯一的,这就意味着集合中不能出现重复的数据;集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)。示例如下:

1
2
3
4
5
6
7
8
9
127.0.0.1:6379> sadd minhow 'redis' //向集合minhow加入redis元素
(integer) 1
127.0.0.1:6379> sadd minhow memcache
(integer) 1
127.0.0.1:6379> smembers minhow //列出集合minhow所有元素
1) "memcache"
2) "redis"
127.0.0.1:6379> sismember minhow redis //判断元素redis是否在集合minhow中,返回1表示存在,返回0表示不存在
(integer) 1

4.4 Zset—集合

Redis的有序集合和集合一样也是string类型元素的集合,且不允许重复的成员;不同的是每个元素都会关联一个double类型的分数;redis正是通过分数来为集合中的成员进行从小到大的排序;有序集合的成员是唯一的,但分数(score)却可以重复;也是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)。示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
127.0.0.1:6379> zadd minhow 1 redis //向minhow中新增序号为1的redis元素
(integer) 1
127.0.0.1:6379> zadd minhow 2 memcache //向minhow中新增序号为2的memcache元素
(integer) 1
127.0.0.1:6379> zadd minhow 3 mysql //向minhow中新增序号为3的mysql元素
(integer) 1
127.0.0.1:6379> zadd minhow 3 mysql //向minhow中新增序号为3的mysql元素
(integer) 0
127.0.0.1:6379> zadd minhow 3 oracle //向minhow中新增序号为3的oracle元素
(integer) 1
127.0.0.1:6379> zrange minhow 0 5 withscores //列出minhow的所有元素,同时列出序号
1) "redis"
2) "1"
3) "memcache"
4) "2"
5) "mysql"
6) "3"
7) "oracle"
8) "3"

4.5 Hash—哈希

Redis的hash是一个string类型的field和value的映射表;相比String类型将这整个对象持久化成JSON格式,Hash将对象的各个属性存入Map里,可以只读取/更新对象的某些属性,hash特别适合用于存储对象。示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
127.0.0.1:6379> hmset minhow name minhow sex male age 24 //建立minhow哈希,并赋值
OK
127.0.0.1:6379> hgetall minhow //列出minhow哈希的所有内容
1) "name"
2) "minhow"
3) "sex"
4) "male"
5) "age"
6) "24"
127.0.0.1:6379> hset minhow age 25 //修改minhow哈希的age值为25
(integer) 0
127.0.0.1:6379> hgetall minhow //列出minhow哈希的所有内容
1) "name"
2) "minhow"
3) "sex"
4) "male"
5) "age"
6) "25"

还有更多的hash操作方法可以查看

5. 使用合适的数据结构

不管是内存使用或者是性能,有的时候数据结构将产生很大的影响,下面是一些可以参考的最佳实践:

  1. 使用Hash取代将数据存储为数千(或者数百万)独立的字符串。哈希表是非常有效率的,并且可以减少你的内存使用(因为小的Hashes会被编码成一个非常小的空间);同时,Hash还更有益于细节抽象和代码可读。
  2. 合适时候,使用List代替Set;如果你不需要使用Set特性,List在使用更少内存的情况下可以提供比Set更快的速度。
  3. Zset(有序集合)是最昂贵的数据结构,不管是内存消耗还是基本操作的复杂性。如果你只是需要一个查询记录的途径,并不在意排序这样的属性,那么建议使用Hash。

6. 总结

本文主要讲解了Redis的简介,优势,与其他数据库的区别,以及五种数据结构的相关知识等;使用Redis存储数据的时候,选用合适的数据结构是非常重要的,必须结合实际情况选择,下一篇文章会介绍Redis的事务,数据持久化,主从复制等知识。

最后更新: 2017年11月19日 20:17

原始链接: http://blog.minhow.com/articles/database/redis-2/

× 请我吃糖~
打赏二维码