我们现在就是Redis

了解更多

关于类型和交易



回到博客

任何数据库中的事务都是可怕的。它不仅需要对存储内容的理解,还需要对存储时间的理解。与数不清的抽象层可以保护您免受复杂性影响时所产生的快乐世界不同,事务需要您更深入。Redis在这方面并不罕见。事实上,它对事务的完全不同的思考方式导致很多人说它根本没有事务。Redis拥有它们,只是它的方法与你成长过程中可能遇到的回滚完全不同。

要在10000英尺处查看交易,您必须了解Redis的一些情况。一个是它是单线程的(好吧,有一个例外列表,可能还在继续增长)。这意味着如果它在做什么,那就是它所做的一切。当然,对于Redis,“做事”最好用毫秒或纳秒来衡量。其次,请记住,Redis具有可调的耐用性,有些选项提供非常好的耐用性,有些选项完全是短暂的。这显然会对交易产生影响。第三,它没有回滚功能,但如果密钥在启动前发生更改,则事务可能会失败。这种控制事务的反向方式允许您将数据拉回到客户机,并对其进行逻辑评估,以确保在事务开始之前数据不会更改。

对于大多数人来说,最大的问题是事务中的单个命令可能会有错误。这可能会造成执行每个命令但一个或多个命令执行有错误的情况。了解这一点是理解和控制这些情况的关键。

首先,让我们看看Redis中语法错误和语义错误之间的区别。语法错误就在于此–语法错误的方式是在不访问数据的情况下已知的。例如,发送不存在的命令或违反参数键/值序列。语法错误导致事务无法启动。

举个例子:

127.0.0.1:6379>多功能
好吧
127.0.0.1:6379>圣福酒吧
(错误)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>INCR foo
排队
127.0.0.1:6379>设置条baz
排队
127.0.0.1:6379>执行
1) (错误)错误值不是整数或超出范围
2) 嗯

显然,现实世界中有能力的开发人员永远不会故意增加字符串“hello world”。但是,当您想到一个端到端应用程序,其中用户输入被接受,期望一个数字,但没有验证为数字时,这个示例变得更加现实。另一个阴险的地方是第二个设置命令被执行,但是增量没有。这不是大多数开发人员想要的,也不是期望从事务中得到的。好ag万博下载万博最新版本下载苹果消息是,通过使用命令WATCH使您能够观察键的更改,如果发生更改,它将立即向客户端发送错误。这是非常强大的,允许您将数据带到客户端并对其进行评估。在这种情况下,您可以评估foo是否可以递增(也称为整数)。

看看这个伪代码:

1 >设置foo 1234
2>看福
3如果watchError转到2,否则继续
4>得到食物
如果第4行结果不是一个数字,抛出一个错误,否则继续
6 >多
7>加富
8 > SET bar baz
9 >执行

现在,如果任何连接的客户机在第3行和第9行之间更改了foo(但在第9行期间没有更改,这允许事务本身更改监视的数据),则流程将跳回第2行。在第4行之后,它将查看应用程序中foo的内容,并确定是否可以增加。因为如果发生更改,它将立即重试,这确保您不会因为foo的内容错误而得到任何错误。

这与Redis如何处理整数和浮点数有关。我之前讲过一点,但基本上,如果数据是浮点数,则必须使用INCRBYFLOAT而不是增加或增量. 但是,如果该值恰好等于一个整数,则可以再次使用INCR或INCRBY。需要注意的是,您可以使用INCRBYFLOAT按非浮点数递增非浮点数,这不会影响任何内容。本质上,INCR命令家族永远不会创建“1.0”。在MULTI/EXEC情况下,在INCR或INCRBY上使用INCRBYFLOAT更安全,因为它不会创建错误。在这种情况下使用INCR或INCRBY的唯一原因是,如果您有一个浮点数,则确保抛出一个错误-在这种情况下,您必须使用WATCH。

关于以这种方式评估数据的一个注意事项是:它不是免费的。如果你把数字拉到客户端,评估是最小的。但假设你有一个未知的键,你评估它的适用性,但不是5-7字节组成的数字,你有一个500 MB的二进制blob。这将需要一段时间来推过电线并评估。这是一种边缘情况,但需要记住。

同样的模式(WATCH/MULTI/EXEC)对于防止在事务结果中出现命令/类型不匹配(又称错误类型)非常有用。对于INCR问题,您必须使用某种客户端数据评估来跟踪字符串中是否包含可以被INCR'ed或INCRBYFLOAT'ed的数据。或者,您可以更直接地使用TYPE命令来计算数据的类型,而不是数据本身(很好地避免了意外的500 MB计算)。

让我们看看这是如何工作的:

1>HSET foo bar 1234
2>看福
3如果watchError转到2,否则继续
4>输入foo
5如果第4行的结果不是“散列”,抛出一个错误,否则继续
6 >多
7>HSET foo baz helloworld
8>仁福
9 >执行

在这种情况下,您知道您的HSET和HLEN命令将起作用,因为您已经验证了类型,并且在运行EXEC时它还没有更改。当然,还有一个问题HINCRBY/HINCRBYFLOAT命令,您需要使用HGET而不是收到评估该领域的增量能力。

有趣的是,比特场具有用于处理越界值的控件。您可以根据命令本身中声明的类型进行换行或饱和,也可以使用FAIL选项。奇怪的是,这不会产生错误,而是忽略增量,将值保留为以前的值。然而,比特域还有另一个问题。该命令非常复杂,Redis只执行一些基本的语法检查,因此如果您将错误的语法传递给它,则在将其添加到事务时不会对其进行计算,而是在事务内部执行该命令时进行计算。这会导致语法错误,该错误不会取消事务,并在值中返回。防止这些类型错误的唯一方法是,在将语法放入事务之前,确保您的语法在客户端级别是正确的。

总的来说,Redis不需要数据初始化步骤。对一些人来说,这是令人震惊的,但对大多数人来说,他们明白了。如果某个键为空,并且向其添加数据,则新创建的数据结构由用于添加数据的命令定义。随着模块的出现,这一点已经慢慢开始改变,这使得这种约定变得不那么明确。例如,RedisGraph要求您在查询图形之前添加一些节点和关系。

举个例子:

>多重
好吧
>GRAPH.QUERY mygraph“MATCH(r:Rider)-[:rides]->(t:Team)其中t.name='Yamaha'返回r,t”
排队
>雅马哈大学
排队
>执行官
1) (错误)键不包含图形对象。
2)(整数)1

事实上,你可以在Redis Streams中看到这种行为。例如:

>多重
好吧
>XGROUP创建我的流我的消费者组$
排队
> LPUSH my-stream-groups my-consumer-group
排队
>执行官
1) (错误)ERR XGROUP子命令要求密钥存在。请注意,对于CREATE,您可能希望使用MKSTREAM选项自动创建空流。
2)(整数)1

谢天谢地,通过使用上面描述的WATCH、TYPE、MULTI/EXEC模式,这些问题得到了相当简单的解决。您只需检查类型匹配(使用这些示例)并匹配graphdata或stream。

具有MULTI和EXEC的Redis事务实际上没有那么复杂。但是,您确实需要注意一些问题,以确保您的事务按预期进行。如果您从本文中得到了什么,请记住,您可以通过不假设引用键的类型、内容或存在来进行防弹的Redis事务。

Baidu