Redis系统学习 三、使用数据结构

前言:上一章,简单介绍了5种数据结构,并给出了一些用例。现在是时候来看看一些高级的,但依然很常见的主题和设计模式

一、大O表示法(Big O Notation )

常用时间复杂度O(1)被认为是最快速的,无论我们是在处理5个元素还是5百万个元素,最终都能得到相同的性能。对于sismember命令,其作用是告诉我们一个值是否属于一个集合,时间复杂度为O(1)。sismember命令很强大,强大的一部分原因是其高效的性能特征。许多Redis命令都具有O(1)的时间复杂度

对数的时间复杂度 O(log(N)) 被认为是第二快速的,其通过使需三秒的区间不断皱缩来快速完成处理。使用这种“分而治之”的方式,大量的元素能在几个迭代过程里被快速分解完整。zadd 命令的时间复杂度就是O(log(N)),其中N是在分类集合中的元素数量

再下来就是线性时间复杂度 O(N),在一个表格的非索引列里进行查找就需要O(N)次操作。ltrim命令具有O(N)的时间复杂度,但是,在ltrim命令里,N不是列表所拥有的元素数量,而是被删除的元素数量。从一个具有百万元素的列表里用ltrim命令删除一个元素,要比从一个具有一千个元素列表用ltrim命令删除10个元素来的快速,

根据给定的最小和最大值的标记,zremrangebyscore 命令 会在一个分类集合里进行删除元素操作,其时间复杂度是O(log(N)+M)。这看起来似乎有点儿杂乱,通过阅读文档可以知道,这里的N指的是分类集合里的总元素数量,而M则是被删除的元素数量,可以看出,对于性能而言,被删除的元素数量很可能会比分类即合理的总元素数量更为重要。 (zremrangebyscore 命令的具体构成是 zremrangebyscore key max mix)

对于sort命令,其时间复杂度为O(N+M*log(M)),我们将会在下一章谈论更多的相关细节。从sort命令的性能特征来看,可以说这是Redis里最复杂的一个命令。

还存在其他的时间复杂度描述,包括O(N^2)和O(C^N)。随着N的增大,其性能将急速下降。在Redis里,没有任何一个命令具有这些类型的时间复杂度。

值得指出的一点是,在Redis里,当我们发现一些操作具有O(N)的时间复杂度时,我们可能可以找到更为好的方法去处理。

(译注:对于Big O Notation,相信大家都非常的熟悉,虽然原文仅仅是对该表示法进行简单的介绍,但限于个人的算法知识和文笔水平实在有限,此小节的翻译让我头痛颇久,最终成果也确实难以让人满意,望见谅。)

二、仿多关键字查询

时常,你会想通过不同的关键字去查询相同的值,例如 通过用户ID 获取用户信息,也希望通过用户名获取用户信息,有一种很不实效的解决方法,就是将用户对象分别放置到两个字符串值里去  set users:leto xxxx ; set users:9001 xxx

可以实现功能,但是内存会产生两倍的数量,并且今后维护管理也是个噩梦

Redis 其实已经提供了解决的方法:散列

使用散列数据结构,我们可以摆脱重复的缠绕:

set users:9001 "{id: 9001, email: [email protected], ...}"

hset users:lookup:email [email protected] 9001
简单讲,就是通过散列,模拟搜索功能,进行关联
id = redis.hget(‘users:lookup:email‘, ‘[email protected]‘)  先通过散列 搜索出 ID
user = redis.get("users:{id}")  再通过ID获取数

三、引用和索引

我们已经看过几个关于值引用的用例,包括介绍列表数据结构时的用例,以及在上面使用散列数据结构来使查询更灵活一些。进行归纳后会发现,对于那些值与值间的索引和引用,我们都必须手动的去管理。诚实来讲,这确实会让人有点沮丧,尤其是当你想到那些引用相关的操作,如管理、更新和删除等,都必须手动的进行时。在Redis里,这个问题还没有很好的解决方法。

我们已经看到,集合数据结构很常被用来实现这类索引:

sadd friends:leto ghanima paul chani jessica

这个集合里的每个成员都是一个Redis字符串数据结构的引用,而每一个引用的值则包含着用户对象的具体信息。那么如果chani改变了她的名字,或者删除了她的账号,应该如何处理?从整个朋友圈关系结构来看可能会更好理解,我们知道,chni也有她的朋友

sadd friends_of:chani leto paul

如果你有什么待处理情况像上面那样,那在维护成本之外,还会有对于额外索引值的处理和存储空间的成本。这可能会令你感到有点退缩。在下一小节里,我们会谈论减少使用额外数据交互的性能成本的一些方法。

四、数据交互和流水线(Round Trips and Pipelining)

许多命令能接受一个或更多的参数,也有一种关联命令可以接受更多个参数。例如早前我们看到过mget命令接受多个关键字,然后返回值:

sadd friends:chani pater luch hni

Redsi还支持流水线功能。通常情况下,当一个客户端发送请求到Redis后,在发送下一个请求之前必须等待Redis的答复,使用流水线功能,你可以发送多个请求,而不需要等待Redis响应。不但减少了网络开销,还能获得性能上的显著提高。

值得一提的是,Redis会使用存储器去排列命令,因此批量执行命令是一个好主意

五、事务

每一个Redis命令都具有原子性,包括那些一次处理多项事情的命令。此外,对于使用多个命令,Redis支持事务功能。

你可能不知道,但Redis实际上是单线程运行的,这就是为什么每一个Redis命令都能够保证具有原子性。当一个命令在执行时,没有其他命令会运行(我们会在往后的章节里简略谈论一下Scaling)。在你考虑到一些命令去做多项事情时,这会特别的有用。例如:

incr命令实际上就是一个get命令然后紧随一个set命令。

getset命令设置一个新的值然后返回原始值。

setnx命令首先测试关键字是否存在,只有当关键字不存在时才设置值

虽然这些都很有用,但在实际开发时,往往会需要运行具有原子性的一组命令。若要这样做,首先要执行multi命令,紧随其后的是所有你想要执行的命令(作为事务的一部分),最后执行exec命令去实际执行命令,或者使用discard命令放弃执行命令。Redis的事务功能保证了什么?

· 事务中的命令将会按顺序地被执行

· 事务中的命令将会如单个原子操作般被执行(没有其它的客户端命令会在中途被执行)

· 事务中的命令要么全部被执行,要么不会执行

最后,Redis能让你指定一个关键字(或多个关键字),当关键字有改变时,可以查看或者有条件地应用一个事务。这是用于当你需要获取值,且待运行的命令基于那些值时,所有都在一个事务里。对于上面展示的代码,我们不能去实现自己的incr命令,因为一旦exec命令被调用,他们会全部被执行在一块。我们不能这么做:

redis.multi()
current = redis.get(‘powerlevel‘)
redis.set(‘powerlevel‘, current + 1)
redis.exec()

(译注:虽然Redis是单线程运行的,但是我们可以同时运行多个Redis客户端进程,常见的并发问题还是会出现。像上面的代码,在get运行之后,set运行之前,powerlevel的值可能会被另一个Redis客户端给改变,从而造成错误。)

所以需要指定观察这个powerlevel关键字 如果发生变化,就事务直接失败回滚。

redis.watch(‘powerlevel‘)
current = redis.get(‘powerlevel‘)
redis.multi()
redis.set(‘powerlevel‘, current + 1)
redis.exec()

在我们调用watch后,如果另一个客户端改变了powerlevel的值,我们的事务将会运行失败。如果没有客户端改变powerlevel的值,那么事务会继续工作。我们可以在一个循环里运行这些代码,直到其能正常工作。 很实用

六、关键字反模式

在下一章中,我们将会讨论那些没有确切关联到数据结构的命令,其中的一些是管理或联调工具。然而有一个命令在调试或者追踪BUG的时候非常有用:keys 。这个命令需要一个模式,然后查找所有匹配的关键字,但是因为它是通过线性扫描所有的关键字来进行匹配。所以不能使用在产品代码里,太慢了,消耗也较大

查BUG,比如想查账户号 1233开头的账户  keys bug:1233*  (*号是通配符)

小结:

结合这一章以及前一章,希望能让你得到一些洞察力,了解如何使用Redis支持(Poswer) 实际项目。还有其他的模式可以让你去构建各种类型的东西,但真正的关键是要理解基本的数据结构。你将能领悟到,这些数据结构是如何能够实现你最初的视角之外的东西

时间: 2024-10-23 20:20:11

Redis系统学习 三、使用数据结构的相关文章

Redis系统学习 四、超越数据结构

5种数据结构组成了Redis的基础,其他没有关联特定数据结构的命令也有很多.我们已经看过一些这样的命令:info,select,flushdb,multi,exec,discard,watch,和keys.这一章将看看其他的一些重要命令. 使用期限(Expiration) Redis允许你标记一个关键字的使用期限.你可以给与一个Unix时间戳形式(1970 01 01 起)的绝对时间,或者一个基于秒的存活时间.这是一个基于关键字的命令,因此其不在乎关键字表示的是哪种类型的数据结构. expire

Redis系统学习 五、管理

在最后一章里,我们将集中谈论Redis运行中的一些管理方面内容.这是一个不完整的Redis管理指南,我们将会回答一些基本的问题,初接触Redis的新用户可能会很感兴趣. 配置(Configuration) 当你第一次运行Redis的服务器,它会向你显示一个警告,指redis.conf文件没有被找到.这个文件可以被用来配置Redis的各个方面.一个充分定义(well-documented)的redis.conf文件对各个版本的Redis都有效.范例文件包含了默认的配置选项,因此,对于想要了解设置在

Redis系统学习 一、基础知识

1.数据库 select 1  select 0 2.命令.关键字和值 redis不仅仅是一种简单的关键字-值型存储,从其核心概念来看,Redsi的5种数据结构中的每一个都至少有一个关键字和一个值.在转入其它关于Redis的有用信息之前,我们必须理解关键字和值的概念. 关键字Keys是用来标识数据块,例如users:leto 这个关键字里的冒号没有任何特殊含义,对于Redis而言,使用分隔符来组织关键字是很常见的方法 值 values 是关联与关键字的实际值,可以是任何东西. 3.查询 key就

redis深入学习(三)-----事务、主从复制、jedis

reids事务 概念 可以一次执行多个命令,本质是一组命令的集合.一个事务中的所有命令都会序列化,按顺序地串行化执行而不会被其它命令插入,不许加塞 作用 一个队列中,一次性.顺序性.排他性的执行一系列命令 常用命令 正常操作事务: 放弃事务: 其实redis对于事务是部分支持: 例如incr k1虽然是错误的(类似于java的运行时异常),但是其他几个结果依然可以成功操作. watch监控 悲观锁/乐观锁/CAS(Check And Set) 1.悲观锁 悲观锁(Pessimistic Lock

Scala系统学习(三):Scala基础语法

如果您熟悉Java语言语法和编程,那么学习Scala将会很容易.Scala和Java之间最大的句法差异在于行结束字符的分号(;) 是可选的. 当编写Scala程序时,它可以被定义为通过调用彼此的方法进行通信的对象的集合.现在我们简单地看一下类,对象,方法和实例变量的含义. 对象 - 对象有状态和行为.一个对象是类的一个实例.例如 - 狗有状态:颜色,名称,品种,它行为有:摇摆,吠叫和吃东西. 类 - 可以将类定义为描述与该类相关的行为/状态的模板/蓝图. 方法 - 一个方法基本上是一种行为.一个

Redis基础学习(三)—Key操作

一.key的相关操作 1.删除 del key1 key2 ... Keyn 作用: 删除1个或多个键. 返回值: 不存在的key忽略掉,返回真正删除的key的数量.   2.重命名 rename key newkey 作用: 给key赋一个新的key名 注:如果newkey已存在,则newkey的原值被覆盖.   3.随机key randomkey 作用: 返回随机key.   4.是否存在key exists key 作用: 判断key是否存在,返回1/0.   5.判断key的类型 typ

系统学习redis之一——基础概念

前言: redis集群搭建过很多次,也用过一些基础的命令,能解决一些常规的异常.但是因为平时对数据这块用得不多,一直没有很系统的学习过redis.这次将redis的知识好好学习了一下,记录为学习笔记<系统学习redis>系列. NoSQL介绍 非关系型数据库:NoSQL关系型数据库:MySQL.SQLserver.Oracle 关系型数据库在web2.0上对高并发,暴露出了一些性能问题 NoSQL 是以key-value形式存储,和传统的关系型数据库不一样,不一定遵循传统数据库的一些基本要求,

Python学习(三)数据结构

Python 数据结构 本章介绍 Python 主要的 built-type,包括如下: Numeric types          int float Text Sequence Type       str Boolean              bool Sequence  Types        list tuple range Mapping Types          dict Set Types             set type() 函数 type(object)

系统学习redis之七——redis数据类型之zset数据类型及操作

sourted sets数据类型介绍 sorted set是set的一个升级版本,他在set的基础上增加了一个顺序属性.这一属性在修改元素的时候可以指定,每次指定后,zset会自动按照新的值调整顺序,是有序集合.可以理解为有两列的MySQL表,一列存value,一列存顺序.操作中key理解为zset的名字. zset数据类型方法 zadd:向指定集合zset中添加元素member,score用于排序,如果该元素已经存在,则更新其顺序 zrange:查看sourted sets里面的所有元素 zr