写点什么

Redis 浅析(一)

作者:andy
  • 2022-10-29
    北京
  • 本文字数:11985 字

    阅读完需:约 39 分钟

1-NoSql


一、NoSQL 数据库简介

1.1、关系型数据的“问题”

对于数据库的发展,最初只有关系型数据库,但是,关系型数据库并不是数据库一直以来的发展。

关系型数据库核心语法是 SQL。但是 SQL 语法要求严格,同时,一旦处理不当,SQL 会出现笛卡儿积的情况。因此,有许多人不太认可 SQL 语句,而这部分人主张使用命令来完成数据库的操作。90 年代,微软旗下的 FoxPro 便使用命令进行数据库的操作。但是,随着时代的发展,SQL 语句的大行其道,FoxPro 后期也支持了 SQL 语句。但是这也使得 FoxPro 丧失了自己的特点。

当今时代有以下的关系型数据库:DB2、Oracle、SQL Server、MySQL、MariaDB(MySql 的分支)。

不太认同 SQL 语句的这部分人,认为关系型数据库具有以下“问题”:

  • 域定义结构,即需提前定义数据库结构;

  • 行列的规范化存储,即没有定义的列,是无法存储该列数据的;

  • 利用 ACID 原则(原子性、一致性、隔离性、持久性)保证数据完整性,体现在数据库的事务控制上;

  • 结构化查询,SQL 处理不当,出现笛卡儿积,甚至会中断程序运行;

  • 存储数据量“小”,无法承受上亿的数据量。

关系型数据库有一个很好的机制,就是事务控制。但事务控制最大的缺点就是速度慢。当一个进程没有提交数据和释放数据库资源时,那么,其他的进程就需要等待,这就影响了数据库的性能。单节点的数据库再好,服务器的硬件性能更好,在当今时代,也无法承受几亿条数据。

通俗的说,传统开发和互联网开发,最大的区别在于数据量的问题,这也是为什么传统开发没有涉及到集群的问题


1.2、NoSQL 数据库

NoSQL(Not Only SQL),即“不仅仅是 SQL”。NoSQL 具有以下特点:

  • 动态结构定义;

  • 采用数据集存储;

  • 存储精简;

  • 可扩展性强;

  • 适合云计算;

  • 访问速度极快。

对于传统型的关系型数据库,因其存储了结构化持久化的数据,是不可能被遗弃的。但是,对于系统当中频繁访问的数据,该如何处理呢?比如,当今时代的电商平台,用户使用的购物车功能,PC 端和移动端的数据要保持一致,如何做到呢?



图 1 传统系统购物车设计


传统的购物车设计,因为关系型数据库事务控制的问题,造成访问缓慢。所以,在当下高并发的时代,不能使用关系型数据库解决这样高频率操作的问题。不应该存的是非高频访问数据。因为 NoSQL 数据库的速度极快的有点,故互联网时代下的购物车设计如下图。



图 2 互联网系统购物车设计


综上所述,关系型数据库仍需要保留,用于存储结构化持久化的数据,而 NoSQL 数据库则负责高并发的数据操作。

1.3、NoSql 分类

国内目前使用居多的 NoSQL 数据有:Memcached、MongoDB、Redis。NoSQL 数据库有以下分类:

  • 键值(key-value)存储数据库:采用 Hash 表结构存储(简单、易部署)Redis、MemcacheDB、Tokyo Cabinet/Tyrant、Voldemort、Oracle BDB

  • 列存储数据库:应对分布式存储的海量数据,采用列族的形式保存数据 HBase、Cassandra、Riak

  • 文档型数据库:灵感是来自于 Lotus Notes,采用类似 JSON 的形式存储 MongoDB、CouchDB、SequoiaDB

  • 图形(Graph)数据库:Neo4j、FlockDB

  • 对象存储:通过类似面向对象语言的语法操作数据库 Versant、db4o

  • xml 数据库:高效的存储 XML 数据,并支持 XML 的内部查询语法,比如 XQuery、XpathBerkeley DB XML、BaseX


插曲:

曾经我就想设计一个将数据存储至文件的方式去实现数据持久化,从而避免与关系型数据库打交道。但实际上,MongoDB 正是基于这一点,进行了数据库的设计。

但是,存储文件容量非常大时有一个问题,每次进行文件的读取与写入操作,非常消耗 IO 资源。而关系型数据库则像是一个独立运行的程序,在读取与写入数据时,通过自身算法,运行代码,可以较为快速处理数据。


2-Redis


Redis

Redis 是由意大利人 Salvatore Sanfilippo(网名:antirez)开发的一款 key-value 内存高速缓存数据库。Redis 全称为 Remote Dictionary Server(远程数据服务),系统使用 C 语言编写遵守 BSD 协议支持网络可基于内存亦可持久化的日志型数据库。Redis 是一个开源的,先进的 key-value 存储可用于构建高性能,可扩展的 Web 应用程序的解决方案。支持丰富的数据类型,如 String、list、set、zset(sorted set)、hash,故名为数据结构服务器。

在当今高并发的时代,对于 Redis 的版本,选择 3.2 以上的版本,因为这之上才有集群的支持工具 RedisCluster。

Redis 具有如下特点:

  • 异常快速:Redis 是非常快的,每秒可以执行大约 110000 设置操作,81000 个/秒的读取操作;

  • 支持丰富的数据类型:Redis 支持最大多数开发人员已经知道的如列表、集合、可排序集合、哈希等数据类型;

  • 这使得在应用中很容易解决各种问题,因为我们知道哪些问题处理使用哪种数据类型更好解决;

  • 原子性操作:所有 Redis 的操作都是原子,从而确保当两个客户同时访问 Redis 服务器时得到的是更新后的值(最新值);

  • MultiUtility 工具:Redis 是一个多功能使用工具,可以在缓存、消息传递队列(Redis 原生支持发布/订阅)等中使用,在应用程序中使用,如:Web 应用程序会话,网站页面点击数等任何短暂的数据。

相对于第一代 NoSQL 数据库 Memcached,第二代 Redis 与其的区别如下:

  • Redis 支持比 Memcached 更多的数据类型,诸如 list、hash、set、sortedset;

  • Redis 支持主从结构(Master-Slave)可以实现数据备份;

  • Redis 支持数据持久化,可以将数据保存到磁盘上,重启时依然可用。

问题:

Redis 与 Memcached 有哪些不同?

Redis 支持更多的数据类型,同时,数据允许被持久化保存在磁盘中。


3-数据类型


Redis 支持多种数据类型,包括字符串、哈希、列表、集合、有序集合

一、字符串(Strings)

底层数据结构为“简单动态字符串”,即 key-value

常用指令

set key value

get key

二、哈希(Hashes)

存储字段和字段值的映射,就是键值对的集合,底层数据结构主要是“哈希表”,即 key-field-value

常用指令

hset key field value

hmset key field value [field value ...]

hget key field

hmget key field [field ...]

三、列表(Lists)

底层数据结构主要是“双向链表”,可以向列表的两端添加元素,时间复杂度为 O(1),也可以获取列表中的某个片段

常用指令

LPUSH key value [value ...]

RPUSH key value [value ...]

LRANGE key start stop

四、集合(Sets)

字符串类型的集合,集合是无序的,元素也是不能重复的

常用指令

SADD key member [member ...]

SREM key member [member ...]

SMEMBERS key

SISMEMBER key member

五、有序集合(Sorted Sets)

底层数据结构以跳表(SkipList)为主

有序集合理解为集合的升级版,ZSET 在集合基础上增加了分数属性,这个属性在添加修改元素的时候指定,每次指定后,ZSET 按照分数自动排序

常用指令

ZADD key score member [member ...]

ZSCORE key member

ZREM key member [member ...]

ZRANGE key start stop [WITHSCORE]

ZREVRANGE key start stop [WITHSCORE]

六、高级数据类型

位图(Bitmaps)、基数统计(HyperLogLogs)、数据流(Streams)


4-常用指令


keys

key 是二进制安全的,可以使用任何二进制序列作为 key 值,简单如 foo 到复杂如 JEPG 文件内容都可以。空字符串也可作为 key 值。

Redis 可以使用“keys *”命令查看所有键值对。

关于 key 的几条规则

1、太长的健值不太好,这需要消耗内存,同时,增加了查找该类健值的计算成本;

2、太短的健值也不合适,尤其是碰到不易阅读的健值。

3、建议 key 采取一定格式,这样方便存储和阅读。

EXISTS、DEL、TYPE、EXPIRE、TTL

exists 判断 key 是否存在。del 删除指定 key。type 获取 key 的 value 类型。expire 设置 key 的超时时间,与数据类型无关。短信验证码的有效期便可以采用 Redis 超时特性实现。ttl 查看 key 对应的值剩余存活时间。

exists key

del key

type key

expire key 10

ttl key

字符串 Strings

字符串是字节序列,同时是二进制安全的,可以存储高达 512M 字节的任何内容。单用字符串类型,Redis 类似于可持久化的 memcached 服务器。

SET、GET

设置数据,如果设置 key 相同的不同数据,默认情况下会发生数据覆盖:

set org-own fuys

取得数据:get org-own

清空数据库:flushdb

不覆盖内容设置,返回结果有两种,0 表示设置失败,1 表示设置成功:

setnx org-own fuys

setnx org-owndb fuys

INCR、INCRBY、DECR、DECRBY

这类命令将字符串解析为整型,经过计算,然后又保存为新的字符串值。

这类命令是原子操作的,多个客户端发出同一个 key 的操作指令时,不会出现竞争问题,例如 A 和 B 同时读取 value 为 10 的值,然后进行 INCR 计算,最终结果只会是 12,而不是 11。

该方式可以进行访问用户的统计。

设置数字的存储:

set org-ownnosql 108

普通数据类型的数字操作:

自增 1 处理:incr org-ownnosql

自增指定的数据:incrby org-ownnosql -7

自减 1 处理:decr org-ownnosql

自减指定的数据:decrby org-ownnosql -7

若设置数字为浮点型的小数,那么,存储的数据是无法进行数字操作的,会报出以下错误:(error) ERR value is not an integer or out of range

MSET、MGET

通过命令,可以同时设置多个值。

覆盖设置多个 key

mset a 10 b 20 c 30

获取多个 key

mget a b c

不覆盖的设置多个 key,包含有一个以上的重复内容,则所有内容不允许设置,返回结果则为失败,即 0,设置成功,则返回 1:

msetnx org-own ysfu org-util common

SETEX、TTL、PERSIST

设置有效时间下的数据,实际环境中,验证码的信息就可以使用此类型数据保存,因为到了一定时间之后,数据会自动消失:

setex mobile-code 10 345276

设置某个 key 的有效时间之后,可以使用以下指令查看剩余时间,如果内容已经消失,则返回“-2”,如果没有消失,则返回剩余时间:

ttl mobile-code

当设置有效时间的 key 处于有效期内,可以使用一下执行取消有效时间,随后使用 ttl 查看,则返回-1:

persist mobile-code

APPEND、STRLEN

key 追加内容:append org-own java

取得指定 key 的长度:strlen org-own

应用

链表 Lists

Lists 是一个插入顺序排序的字符串元素集合,同时也是链表结构,时间复杂度为 n,链表结构的数据处理主要完成内容的保存、节点的设置、递归遍历。之所以采用链表结构,主要目的在于快速进行元素的添加和删除操作。

Lists 的最大长度为 2^32 - 1 个元素(4294967295,每个列表可容纳超过 40 亿个元素)。

LPUSH、RPUSH、LRANGE

创建 key 的 LIst 集合,push 为入栈,采用先进后出算法,也即栈顶压入:

lpush org-owndb jdbc ejb hibernate jpa ibatis mybatis

获取指定范围的链表数据:lrange org-owndb 0 -1

创建 key 的 List 集合,进行栈底入栈算法:

rpush org-ownutil string date number

POP

元素栈顶出栈:lpop org-owndb

元素栈底出栈:rpop org-owndb

将一个 List 的栈底出栈元素压入另一个 List 的栈顶入栈:

rpoplpush org-ownutil org-owndb

LTRIM、LINSERT、LREM、LSET

在指定 List 的元素前追加,若指定元素有重复数据,以第一个为准:

linsert org-ownutil before date file

在制定 List 的元素后追加,若指定元素有重复数据,以第一个为准:

linsert org-ownutil after date filter

修改指定索引位置的元素:lset org-ownutil 4 math

删除指定重复个数的元素:lrem org-owndb 1 ejb

截切指定范围的 List 集合,包含边界元素:ltrim org-owndb 0 7

BRPOP、BLPOP

对于消费者和生产者模式,当集合为空时,无法进行消费,则消费者需要阻塞等待,可以采用改类命令实现,区别在于是从头部还是尾部取出数据。

LINDEX、LLEN

取得制定索引的元素:lindex org-owndb 4

取得 List 的元素个数:llen org-owndb

Hashes

简单而言,Hash 就是键值对的集合,用于表示对象。

Hash 保存的数据结构为“key = value”,Hash 作为一种类型,可以理解为 RedisKey = HashValue。更确切的说,很像对象和属性以及属性值的关系。若想要 key 上能够保存更多的数据,则使用 Hash 数据类型。

HSET、HMSET、HGET、HMGET、HGETALL

设置 key 的属性和属性值,默认情况下,设置同一个 key 的相同属性的不同属性值,会进行数据的覆盖:

hset org-own id fu

设置多个属性和属性值:hmset org-own country china corp github

取得指定 key 中的属性内容:hget org-own id

不覆盖的设置 key 中的属性值,当有重复数据时,则返回 0,表示失败,反之:

hset org-own name ysfu

取得 key 中的所有属性以及对应的属性值,返回结果采用的模式是 hash-key、hash-value、hash-key、hash-value:

hgetall org-own

HEXISTS、HDEL、HKEYS、HVALS、HLEN

判断 key 中是否存在属性:hexists org-own state

取得 key 中的属性个数:hlen org-own

删除 key 中的属性:hdel org-own country corp

取得 key 中的所有属性名称:hkeys org-own

取得 key 中的所有属性值:hvals org-own

HINCRBY

Hash 数据类型的数字操作:

hset org-own capital 1000

自增指定的数据:hincrby org-own capital 1000

Sets

Sets 是一个不重复并且无序的集合。

Set 数据类型最大的特点在于实现集合之间的比较处理,例如集合的交集、并集、差集。实际环境中,就如同微博中的关注好友、取消好友、推荐好友等功能。

SADD、SMEMBERS、SISMEMBER、SREM、SPOP、SCARD

向 Set 集合添加新元素:sadd org-weibo a b c d e

查询 Set 集合的所有元素,相比 List 而言,Set 存储的元素是无序的:

smembers org-weibo

判断集合中是否包含指定的元素,若包含,则返回 1,若不包含,则返回 0:

sismember org-weibo a

删除 Set 集合中的元素:srem org-weibo d

从集合中弹出随机选中的元素:spop org-weibo

返回集合的元素个数:scard org-weibo

随机取得 Set 中的指定个数的元素:srandmember org-weibo 3

SDIFF、SDIFFSTORE、SINTER、SINTERSTORE、SUNION、SUNIONSTORE

两个 Set 集合的差集运算:sdiff org-weibo org-weixin

将两个 Set 集合的差集运算结果保存至新的 Set 集合里:

sdiffstore org-weibo-weixin org-weibo org-weixin

两个 Set 集合的交集运算:sinter org-weixin org-weibo

将两个 Set 集合的交集运算结果保存至新的 Set 集合里:

sinterstore org-weixin-weibo org-weixin org-weibo

两个 Set 集合的并集运算:sunion org-weibo org-weixin

将两个 Set 集合的并集运算结果保存至新的 Set 集合里:

sunionstore org-weibo-union-weixin org-weibo org-weixin

SortedSets

Set 是无序的存储数据,则可以采用 SortedSet 进行有序的存储数据,但 SortedSet 并不是顺序存储,排序集合的每一个成员关联一个 score 浮动数值,集合中的元素通过 score 进行排序。

ZADD、ZRANGE、ZRERANGE、ZREM、ZCARD

增加 SortedSet 集合数据:zadd org-mldn 2 spring

显示集合中的所有元素:zrange org-mldn 0 -1

显示集合中的所有元素及其对应的分数:zrange org-mldn 0 -1 withscores

显示集合反转后的所有元素:zrevrange org-mldn 0 -1

显示集合反转后的所有元素及其对应的分数:zrevrange org-mldn 0 -1 withscores

删除集合中的指定元素:zrem org-mldn redis

取得集合中的元素个数:zcard org-mldn

ZINCRBY、ZRANK、ZREVRANK、ZRANGEBYSCRORE、ZRANGEBYSCORE、ZCOUNT、ZREMRANGEBYRANK

增长集合中的指定元素的分数:zincrby org-mldn 10 spring

取得制定元素的索引值:zrank org-mldn mybatis

取得集合反转后的指定元素索引值:zrevrank org-mldn mybatis

根据分数区间取得集合中的元素,其中数字前带有“(”表示不包括分数边界,反之,则包括:

zrangebyscore org-mldn (0 23

根据分数区间取得集合中的元素,并且指定取得的元素个数,其中数字前带有“(”表示不包括分数边界,反之,则包括,limit 后的第一个数字表示偏移量,第二个数字表示个数:

zrangebyscore org-mldn (0 23 limit 1 1

取得指定分数范围的元素个数,其中数字前带有“(”表示不包括分数边界,反之,则包括:

zcount org-mldn (3 23

根据索引范围删除集合中的元素:zremrangebyrank org-mldn 0 1

数据类型实际应用

Strings:在实际开发中,String 可以进行各种复杂的操作,而且字符串可以描述出各种数据类型的含义,因此,字符串更适合用户的短期的数据存储。

实际环境中,可以使用字符串进行 Nginx 的集群数据、Shiro 的集群数据、SpringData 的数据序列化、JSON 数据等保存。

存储用户访问的 session 信息,短信验证码等等。

Lists:Lists 链表,可以存储聊天系统的对话记录,亦可以存储不同进程间的消息队列。也可以存储博客文章之类的评论。只要时符合顺序的集合情况都可以。

Hashs:Hash 类型描述的是一种结构化的信息,但其结构化不如对象序列化方便。可以存储用户的结构化数据信息。

Sets:该类型最大的特点在于集合的运算上,实际开发中可用于相似度检查、好友推荐等功能,实际环境中,类似于新浪微博的用户推荐、关注用户。

Sorted Sets:主要进行数据的流式分析,如用户浏览商品的轨迹、浏览商品的次数等等;其应用更多的地方便是保存分数,然后对分数进行操作处理,得出统计结果。实际环境中,可以应用于用户浏览商品轨迹的统计等等。


5-主从模式


一、数据备份

对于 redis 数据库而言,可以通过使用 save 命令,将会创建 dump.rdb 文件进行数据的备份存储。

二、Redis 主从模式

1、主从模式简介

Redis 具有与传统数据库一样的持久化数据功能,只不过存储数据的方式不同。在实际的生产环境中,Redis 常作为系统架构设计中的缓冲点。但是用 Redis 作为单节点会容易出现以下问题:



图 1 Redis 单节点设计问题


当出现问题时,保证所有数据的恢复就成了关键,因此,数据的备份就成为了重要的主题,有以下两种做法:

  • 离线备份:无需网络环境支持,在本地机器上进行数据备份;

  • 在线备份:在数据做出任何处理操作的时候就进行及时的备份。

Redis 则采用在线备份的方式,实现数据库的主从设计。



图 2 Redis 主从设计


2、主从模式配置

进行主从模式的配置,至少需要三台 Redis 数据库运行。下面就以端口为 6379、6380、6381 的三个本地设置的数据库为例进行讲解。其中,6379 作为主服务,6380 和 6381 作为从服务。

1)进行主从模式配置,主服务上不需要进行任何配置,因为其不需要关心是否有从服务以及从服务所需要做的工作。

2)所有从服务的配置文件(redis-6380.conf、redis-6381.conf)必须具有明确的配置出主服务。配置信息如下:

配置主服务的 IP 地址和端口:slaveof 192.168.6.128 6379

配置主服务的密码:masterauth fuys0822

3)启动所有的 Redis 服务

4)登陆主服务,查看所有副本信息

/app/redis/bin/redis-cli -h 192.168.6.128 -p 6379 -a fuys0822 info replication

返回的成功日志信息为:

# Replication

role:master

connected_slaves:2

slave0:ip=127.0.0.1,port=6380,state=online,offset=112,lag=1

slave1:ip=127.0.0.1,port=6381,state=online,offset=112,lag=1

master_replid:c614efbd58b916a6e2d5e9e27f817a3fe6d7a2de

master_replid2:0000000000000000000000000000000000000000

master_repl_offset:112

second_repl_offset:-1

repl_backlog_active:1

repl_backlog_size:1048576

repl_backlog_first_byte_offset:1

repl_backlog_histlen:112

5)在主服务上进行数据操作,在从服务上可以查看到主服务上设置的数据。通过主节点设置数据,自动同步到从服务上,实现数据备份。但是,如果想要在从服务上进行数据的处理,则登陆从服务,连接拒绝(Could not connect to Redis at 192.168.6.128:6381: Connection refused)。

主从设计的优点:自动进行数据的备份。

主从设计的缺点:只能够做数据的备份,一旦出现灾难,则无法立即恢复(若是这样,我要你有何用,如果主服务只是因为断电的原因,那我可以立马重启主服务即可)。

3、哨兵模式

对于主从模式而言,主服务进行数据的读写和存储操作,从服务进行数据的备份。一旦主服务出现宕机,那么,redis 主从服务将会无法运行。为了解决这一问题,在主从模式的基础上,使用哨兵模式,当主机服务出现宕机时,其中的从机服务自动变为主服务,当原有主机服务恢复时,当前的主机服务则切换回从机服务,并对新主机服务数据进行备份。

1)复制安装完成的 redis 目录下的 redis-sentinel 文件至执行文件目录,包含从服务。

cp -a /src/redis-4.0.6/src/redis-sentinel /app/redis/bin/

2)复制安装完成的 redis 目录下的 sentinel.conf 文件至执行文件配置目录,包含从服务。

cp -a /src/redis-4.0.6/sentinel.conf /app/redis/conf/

3)修改 sentinel.conf 文件,包括从服务,变更以下内容:

port 26379

dir /data/redis/logs

sentinel monitor mymaster 192.168.6.128 6379 2

4)启动哨兵模式,包含从服务

/app/redis/bin/redis-sentinel /app/redis/conf/sentinel.conf


6-性能测试、性能监控


一、性能测试

1.1、redis-benchmark 测试与操作命令

在 Redis 数据库中有一个 redis-benchmark 性能测试工具,可以直接使用这个工具来观察 Redis 的使用。可以使用以下命令获取帮助信息,从而可以得到更多的测试方法。

/app/redis/bin/redis-benchmark --help

执行帮助命令,得到以下信息:


Usage: redis-benchmark [-h <host>] [-p <port>] [-c <clients>] [-n <requests>] [-k <boolean>]
-h <hostname> Server hostname (default 127.0.0.1) -p <port> Server port (default 6379) -s <socket> Server socket (overrides host and port) -a <password> Password for Redis Auth -c <clients> Number of parallel connections (default 50) -n <requests> Total number of requests (default 100000) -d <size> Data size of SET/GET value in bytes (default 3) --dbnum <db> SELECT the specified db number (default 0) -k <boolean> 1=keep alive 0=reconnect (default 1) -r <keyspacelen> Use random keys for SET/GET/INCR, random values for SADD Using this option the benchmark will expand the string __rand_int__ inside an argument with a 12 digits number in the specified range from 0 to keyspacelen-1. The substitution changes every time a command is executed. Default tests use this to hit random keys in the specified range. -P <numreq> Pipeline <numreq> requests. Default 1 (no pipeline). -e If server replies with errors, show them on stdout. (no more than 1 error per second is displayed) -q Quiet. Just show query/sec values --csv Output in CSV format -l Loop. Run the tests forever -t <tests> Only run the comma separated list of tests. The test names are the same as the ones produced as output. -I Idle mode. Just open N idle connections and wait.
复制代码


Examples:


 Run the benchmark with the default configuration against 127.0.0.1:6379:   $ redis-benchmark
Use 20 parallel clients, for a total of 100k requests, against 192.168.1.1: $ redis-benchmark -h 192.168.1.1 -p 6379 -n 100000 -c 20
Fill 127.0.0.1:6379 with about 1 million keys only using the SET test: $ redis-benchmark -t set -n 1000000 -r 100000000
Benchmark 127.0.0.1:6379 for a few commands producing CSV output: $ redis-benchmark -t ping,set,get -n 100000 --csv
Benchmark a specific command line: $ redis-benchmark -r 10000 -n 10000 eval 'return redis.call("ping")' 0
Fill a list with 10000 random elements: $ redis-benchmark -r 10000 -n 10000 lpush mylist __rand_int__
On user specified command lines __rand_int__ is replaced with a random integer with a range of values selected by the -r option.
复制代码


执行测试指令:

/app/redis/bin/redis-benchmark -h 127.0.0.1 -p 6379 -c 1000 -d 10 -n 100000 >> /data/redis.log

测试部分结果如下:

PING_INLINE:44822.95 requests per second

SET:44583.15 requests per second

GET:35448.42 requests per second

INCR:29351.33 requests per second

LPUSH:39525.69 requests per second

RPUSH:31908.10 requests per second


由此得出,Redis 的执行速度确实很快。可以说,单节点的情况下,是最快的。但是,一旦使用代理,多个数据库使用,因为考虑分配的问题,反而多节点的情况还不如单节点快。


二、Redis 性能监控

在当今互联网时代,Redis 作为缓存数据库,被广泛使用,但是也同样面临大量的数据存储,因此,对于 Redis 的监控就显得很重要了。那么,目前阶段,可是使用 redis-stat 工具对 Redis 进行监控,而这个工具可以从 GitHub 上得到。

4.1、为了模拟 Redis 的性能测试,建立两个 Redis 运行程序,模拟方式简单,配置不同端口,也即配置不同的 redis.conf 文件。

4.2、建立 Redis 数据的保存目录,要求可以同时保存两个 Redis 进程。

mkdir -p /data/{redis-6380,redis-6381}/{run,logs,dbcache}

4.3、拷贝 redis.conf 文件至一份,并修改拷贝文件,另一份待修改好此份文件再进行统一修改。

cp -a /app/redis/conf/redis.conf /app/redis/conf/redis-6380.conf

4.4、修改配置文件,以 redis-6380.conf 为例

取消外网访问限制:#bind 127.0.0.1

设置端口:port 6380

设置 pid 保存路径:pidfile /data/redis-6380/run/redis_6380.pid

设置日志文件路径:logfile "/data/redis-6380/logs/redis-6380.logs"

数据文件目录:dir /data/redis-6380/dbcache

4.5、再将 redis-6380.conf 复制一份为 redis-6381.conf

cp -a /app/redis/conf/redis-6380.conf /app/redis/conf/redis-6381.conf

4.6、编辑 redis-6381.conf 文件,统一修改内容

4.7、为了使用 redis-stat 开发包,需要下载 ruby 相关环境:

apt-get install ruby ruby-dev rubygems

问题:

执行下载 ruby 相关环境,报出-bash: apt-get: command not found 错误。

centos 系统的下载命令为 yum,Ubuntu 系统的下载命令则是 apt-get,因此,将指令修改重新执行。

yum install ruby ruby-dev rubygems

4.8、下载下来的 redis-stat 只有一个执行命令(redis-stat),进入到可执行命令目录下,使用 ruby 进行该命令的处理。

cd /app/redis-stat-master/bin/

gem install redis-stat

问题:

执行 gem 安装,报出以下错误:

ERROR: Error installing redis-stat:

unicode-display_width requires Ruby version >= 1.9.3.

查看 ruby 版本,使用指令 ruby -v,显示:

ruby 1.8.7 (2013-06-27 patchlevel 374) [x86_64-linux]


8-事务控制


事务控制

Redis 提供了事务处理的机制。事务处理是原子性的,意味着要么全部处理,要么全部不处理。事务处理中的所有命令按照顺序执行,并且于其他事务相互隔离。根本上说,仍然符合事务的 ACID 原则。打开了一次事务,那么,当提交事务,整个事务过程结束。

可以利用 Redis 事务控制,当其中程序运行出现异常,则取消事务

一个事务从开始到执行会经历以下三个阶段:

  • 开始事务。

  • 命令入队。

  • 执行事务。

事务常用命令如下:

multi:打开事务处理

exec:提交事务

discard:取消事务

WATCH key [key ...]:监听 key,如果在事务执行之前,key 被其他命令所改动,则事务被中断;

UNWATCH:取消监听 key

案例一:

执行指令及其结果如下:

127.0.0.1:6379> set org-own fuys

OK

127.0.0.1:6379> multi

OK

127.0.0.1:6379> set org-own ysfu

QUEUED

127.0.0.1:6379> get org-own

QUEUED

127.0.0.1:6379> discard

OK

127.0.0.1:6379> get org-own

"fuys"

当打开事务后,设置的成员值操作进入队列中等待,但是中途发现数据异常,故取消事务,随后,Redis 中的一切 key 的状态则恢复到打开事务之前。

案例二:

执行指令及其结果如下:

127.0.0.1:6379> set org-own fuys

OK

127.0.0.1:6379> multi

OK

127.0.0.1:6379> set org-own ysfu

QUEUED

127.0.0.1:6379> set org-owndb mybatis

QUEUED

127.0.0.1:6379> exec

1) OK

2) OK

127.0.0.1:6379> get org-own

"ysfu"

127.0.0.1:6379> get org-owndb

"mybatis"

打开事务,Redis 数据库的操作进入队列等待状态,执行事务提交后,则队列中等待的操作得到执行,key 得到更新。

案例三:

127.0.0.1:6379> set org-own fuys

OK

127.0.0.1:6379> multi

OK

127.0.0.1:6379> incrby org-own 123

QUEUED

127.0.0.1:6379> set org-owndb hibernate

QUEUED

127.0.0.1:6379> exec

1) (error) ERR value is not an integer or out of range

2) OK

127.0.0.1:6379> get org-own

"fuys"

127.0.0.1:6379> get org-owndb

"hibernate"

打开事务前,设置 key 的值为字符串类型。随后,打开事务,设置 key 的值按照指定数值增加和设置 org-owndb 的值操作进入队列等待状态,执行事务提交后,指定数值增加操作失败,而设置 org-owndb 值成功。正常的事务控制,一旦出现异常,那么,所有的操作都不能提交,但是 Redis 的事务控制却可以。因此,这也是 Redis 事务控制与关系型数据库的事务控制的区别所在。建议合理使用 Redis 事务控制机制

乐观锁

当多个用户对数据库中同一数据进行操作时,需要保证数据的一致性,用户 A 操作数据时,用户 B 无法操作,这也是锁的机制应用。锁在数据库中的设计分为两种:

  • 悲观锁:基于传统数据库的操作实现;SELECT * FROM goal FOR UPDATE;

  • 乐观锁:基于算法的实现,在数据表上增加一个锁的处理列,Redis 隐藏了这样的标志位,而关系型数据库则需要定义这样的标志位。

Redis 里有支持乐观锁机制,可以打开两个会话 session,观察乐观锁的处理。

第一个 session 会话操作:

127.0.0.1:6379> set org-own fuys

OK

127.0.0.1:6379> watch org-own

OK

127.0.0.1:6379> multi

OK

第二个 session 会话操作:

127.0.0.1:6379> get org-own

"fuys"

127.0.0.1:6379> set org-own ysfu

OK

第一个 session 会话操作:

127.0.0.1:6379> set org-own fuyunsong

QUEUED

127.0.0.1:6379> exec

(nil)

由执行结果可以得出,第一个会话先设置了 key 和 value,然后监控,打开事务,但不做命令入队。随后,第二个会话进行了 key 的修改,并修改成功。然后第一个会话此时,再去修改 key 的 value 值,并提交事务。但是,并没有成功。因为第二个会话在修改 key 成功后,该 key 的标记位已经改变了,所以,第一个会话再来修改时,则出现错误,修改失败

品质商城抢购活动时商品库存量或者商品限购数就可以利用 Redis 的乐观锁进行控制。


用户头像

andy

关注

还未添加个人签名 2019-11-21 加入

还未添加个人简介

评论

发布
暂无评论
Redis浅析(一)_andy_InfoQ写作社区