简单地说,我们现在是Redis
任何数据库中的事务都令人生畏。它不仅需要理解存储的内容,还需要理解存储的时间。在快乐的世界中,无数的抽象层可以让你远离复杂性,而交易则需要你更深入。在这方面,Redis并不罕见。事实上,它完全不同的思考交易的方式导致很多人说它根本没有交易。Redis也有,只是采用了一种完全不同于你成长过程中可能遇到的回滚的方法。
要查看10,000英尺高空的交易,你必须了解一些关于Redis的事情。一是它是单线程的(当然,有一系列可能还在增长的例外)。这意味着如果它在做某件事,那就是它所做的一切。当然,使用Redis,“做某事”最好以毫秒或纳秒来衡量。其次,记住Redis有可调的持久性,有些选项提供非常好的持久性,而有些选项则完全是短暂的。这显然对事务有影响。第三,它缺乏回滚,但如果在事务开始之前更改了关键字,则可能导致事务失败。这种控制事务的反向方法允许您将数据拉回客户机,并对其进行逻辑评估,以确保在事务开始之前数据不会更改。
对于大多数人来说,最大的障碍是事务中的单个命令可能有错误。这可能会造成这样的情况:每个命令都执行了,但其中一个或多个命令执行有错误。了解这一点是理解和控制这些情况的关键。
首先,让我们来看看Redis中的语法错误和语义错误之间的区别。语法错误就是——语法错误的方式是不访问数据就知道的。例如,发送不存在的命令或违反参数键/值序列。语法错误导致事务永远不会启动。
把这个例子:
127.0.0.1:6379 >多
好吧
127.0.0.1:6379> STE foo bar
(错误)ERR未知命令' STE ',参数以' foo ', ' bar '开头,
127.0.0.1:6379 >执行
(error) EXECABORT由于以前的错误而丢弃的事务。
Redis知道STE不是一个命令,所以它可以丢弃整个东西,甚至不需要评估底层数据,并将拒绝整个MULTI/EXEC块。其他被Redis立即捕获的语法错误包括参数计数和(一些)参数模式问题。这些事务错误是非常安全的多时,所有后续命令将排队运行执行,因此任何触发EXECABORT的操作都不会运行。
下一个类别(范围更广)是语义事务错误,它的行为与语法错误不同。当Redis无法静态捕获错误时,它们就会出现,通常需要Redis评估底层数据。这种行为的一个典型例子是:
127.0.0.1:6379>设置foo "hello world"
好吧
127.0.0.1:6379 >多
好吧
127.0.0.1:6379 >增加foo
排队
127.0.0.1:6379>设置bar baz
排队
127.0.0.1:6379 >执行
1) (error) ERR值不是整数或超出范围
2)好
显然,现实世界中有能力的开发人员不会故意增加字符串“hello world”。但是,当您考虑一个端到端应用程序时,它接受用户输入,期望一个数字,但没有作为数字进行验证,这个示例就变得更现实了。另一个潜在的问题是集命令被执行,但是增加不是。这不是大多数开发人员想要或期望从交易中得到的ag万博下载万博最新版本下载苹果。好消息是,这种类型的错误可以通过使用看命令。WATCH使您能够观察键的变化,如果发生变化,它将立即向客户端发送错误。这是非常强大的,允许您将数据带给客户并对其进行评估。在这种情况下,你可以计算foo是否可以递增(也就是一个整数)。
看看这个伪代码:
1 >设置foo 1234
2 > WATCH foo
3如果出现watchError,则转到2
4 >得到foo
如果第4行结果不是一个数字,抛出一个错误,否则继续
6 >多
7 > INCR foo
8 > SET bar baz
9 >执行
现在,如果foo在第3行和第9行之间被任何连接的客户端更改(但不是在第9行期间,这允许事务本身更改所监视的数据),进程将跳回第2行。在第4行之后,它将查看应用程序中foo的内容,并确定是否可以递增。因为如果发生更改,它将立即重试,这确保您不会得到任何错误,因为foo的内容是错误的。
这与Redis如何处理整数和浮点数有关。我之前讲过一点,但基本上,如果你的数据是一个浮点数,那么你必须使用INCRBYFLOAT而不是INCR或INCRBY.但是,如果值恰好等于一个整数,则可以再次使用INCR或incby。需要注意的是,您可以使用INCRBYFLOAT通过非浮点数增加非浮点数,而且它不会影响任何东西。本质上,INCR命令家族永远不会创建一个“1.0”。在MULTI / EXEC情况下,使用INCRBYFLOAT比使用INCR或INCRBY更安全,因为它不会产生错误。在这种情况下使用INCR或INCRBY的唯一原因是确保在您有一个浮点数时抛出错误——在这种情况下,您无论如何都必须使用WATCH。
关于以这种方式评估数据的一个注意事项是:它不是免费的。如果你把数字拉到客户端,评估是最小的。但假设你有一个未知的键,你评估它的适用性,但不是5-7字节组成的数字,你有一个500 MB的二进制blob。这将需要一段时间来推过电线并评估。这是一种边缘情况,但需要记住。
同样的模式(WATCH / MULTI / EXEC)可以用来防止命令/类型不匹配(也就是WRONGTYPE)出现在事务的结果中。对于INCR问题,您必须使用某种客户端对数据的评估来跟踪String是否包含可以被INCR或INCRBYFLOAT化的数据。或者,您可以更直接地使用TYPE命令来计算数据的类型,而不是数据本身(很好地避免了意外的500 MB计算)。
让我们来看看它是如何工作的:
1 > HSET foo bar 1234
2 > WATCH foo
3如果出现watchError,则转到2
4 >类型foo
如果第4行结果不是" hash ",抛出一个错误,否则继续
6 >多
7 > HSET foo baz helloworld
8 > HLEN foo
9 >执行
在这种情况下,你知道你HSET和HLEN命令将会工作,因为您已经验证了类型,并且在运行EXEC时它还没有改变。当然,还有问题HINCRBY / HINCRBYFLOAT命令,在这里您将需要使用前面的技术HGET而不是得到评估该领域的incr能力。
有趣的是,设置具有处理越界值的控件。您可以根据命令本身声明的类型进行换行或饱和,也可以使用FAIL选项。奇怪的是,这并不会产生错误,而是忽略了INCR,保持原来的值。然而,BITFIELD还有另一个问题。这个命令相当复杂,Redis只做一些基本的语法检查,所以如果你传递了错误的语法,这将不会在它被添加到事务时进行评估,但当命令在事务内部执行时。这将导致语法错误,该错误不会取消事务,并以值的形式返回。防止这些类型的错误的唯一方法是,在将语法丢入事务之前,确保您的语法在客户机级是正确的。
总的来说,Redis不需要对数据进行初始化。对一些人来说,这令人担忧,但对大多数人来说,他们明白。如果一个键是空的,并向其添加数据,则新创建的数据结构由用于添加数据的命令定义。随着模块的出现,这种情况开始慢慢改变,这使得这种约定变得不那么明确。例如,RedisGraph要求您在查询图之前添加一些节点和关系。
把这个例子:
>多
好吧
>图。查询mygraph "MATCH (r:Rider)-[:rides]->(t:Team) WHERE t.name = 'Yamaha' RETURN r,t"
排队
> LPUSH teamqueries Yamaha
排队
>执行
(错误)键不包含图形对象。
2)(整数)1
事实上,你可以在Redis Streams中看到这种行为。例如:
>多
好吧
创建我的流my-consumer-group
排队
> LPUSH my-stream-groups my-consumer-group
排队
>执行
1) (error) ERR XGROUP子命令需要密钥存在。注意,对于CREATE,您可能希望使用MKSTREAM选项自动创建空流。
2)(整数)1
幸运的是,通过使用上面描述的WATCH、TYPE、MULTI/EXEC模式,这些问题得到了相当简单的解决。您只需检查类型匹配(使用这些示例)并匹配图形数据或流。
Redis与MULTI和EXEC的交易真的没有那么复杂。但是,您确实需要注意一些陷阱,以确保事务的行为符合预期。如果你从这篇文章中得到了什么,请记住,你可以通过不假设引用键的类型、内容或存在性来实现Redis的防弹交易。