Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

关于 redis 没有事务回滚? #491

Open
fangjunpang opened this issue Oct 2, 2019 · 8 comments
Open

关于 redis 没有事务回滚? #491

fangjunpang opened this issue Oct 2, 2019 · 8 comments
Labels
discuss discuss a problem enhancement New feature or request or suggestion perfect Improve knowledge points or descriptions

Comments

@fangjunpang
Copy link

不一定吧 -> https://blog.csdn.net/yangshangwei/article/details/82866216

@Rylynnnnn
Copy link

看一下官方文档 https://redis.io/topics/transactions

Errors inside a transaction

During a transaction it is possible to encounter two kind of command errors:
A command may fail to be queued, so there may be an error before EXEC is called. For instance the command may be syntactically wrong (wrong number of arguments, wrong command name, ...), or there may be some critical condition like an out of memory condition (if the server is configured to have a memory limit using the maxmemory directive).
A command may fail after EXEC is called, for instance since we performed an operation against a key with the wrong value (like calling a list operation against a string value).
Clients used to sense the first kind of errors, happening before the EXEC call, by checking the return value of the queued command: if the command replies with QUEUED it was queued correctly, otherwise Redis returns an error. If there is an error while queueing a command, most clients will abort the transaction discarding it.

However starting with Redis 2.6.5, the server will remember that there was an error during the accumulation of commands, and will refuse to execute the transaction returning also an error during EXEC, and discarding the transaction automatically.

Before Redis 2.6.5 the behavior was to execute the transaction with just the subset of commands queued successfully in case the client called EXEC regardless of previous errors. The new behavior makes it much more simple to mix transactions with pipelining, so that the whole transaction can be sent at once, reading all the replies later at once.

Errors happening after EXEC instead are not handled in a special way: all the other commands will be executed even if some command fails during the transaction.

This is more clear on the protocol level. In the following example one command will fail when executed even if the syntax is right:

Why Redis does not support roll backs?

If you have a relational databases background, the fact that Redis commands can fail during a transaction, but still Redis will execute the rest of the transaction instead of rolling back, may look odd to you.

However there are good opinions for this behavior:

Redis commands can fail only if called with a wrong syntax (and the problem is not detectable during the command queueing), or against keys holding the wrong data type: this means that in practical terms a failing command is the result of a programming errors, and a kind of error that is very likely to be detected during development, and not in production.
Redis is internally simplified and faster because it does not need the ability to roll back.
An argument against Redis point of view is that bugs happen, however it should be noted that in general the roll back does not save you from programming errors. For instance if a query increments a key by 2 instead of 1, or increments the wrong key, there is no way for a rollback mechanism to help. Given that no one can save the programmer from his or her errors, and that the kind of errors required for a Redis command to fail are unlikely to enter in production, we selected the simpler and faster approach of not supporting roll backs on errors.

@fangjunpang
Copy link
Author

没懂他的意思,是说redis不支持回滚,但是又保持原子性,这个我没理解

@LiWenGu
Copy link
Contributor

LiWenGu commented Oct 11, 2019

没懂他的意思,是说redis不支持回滚,但是又保持原子性,这个我没理解

首先原子性的定义:事务中的命令要么全部被执行,要么全部都不执行。

然后再看官方文档关键段:
Redis commands can fail during a transaction, but still Redis will execute the rest of the transaction instead of rolling back

Redis 在事务失败时不进行回滚,而是继续执行余下的命令


我根据Redis文档理解,认为事务过程中失败有两种可能:

  1. Redis 命令只会因为错误的语法而失败(并且这些问题不能在入队时发现),或是命令中用在了错误类型的键上面,所以如果在生产环境中你使用的正常命令,那么在 Redis 事务中,是不会出现错误而导致回滚的。

来自文档:Redis commands can fail only if called with a wrong syntax...

  1. 事务执行一半,Redis宕机
    如果 Redis 服务器因为某些原因被管理员杀死,或者遇上某种硬件故障,那么可能只有部分事务命令会被成功写入到磁盘中。
    如果 Redis 在重新启动时发现 AOF 文件出了这样的问题,那么它会退出,并汇报一个错误。
    使用redis-check-aof程序可以修复这一问题:它会移除 AOF 文件中不完整事务的信息,确保服务器可以顺利启动

  2. 而且回滚实现很复杂,需要解决事务回滚覆盖的情况。

综上:所以Redis 没有事务回滚

这是中文文档地址:http://www.redis.cn/topics/transactions.html

@Snailclimb Snailclimb added enhancement New feature or request or suggestion discuss discuss a problem perfect Improve knowledge points or descriptions labels Oct 11, 2019
@dingweihua
Copy link

dingweihua commented Apr 28, 2021

没懂他的意思,是说redis不支持回滚,但是又保持原子性,这个我没理解

首先原子性的定义:事务中的命令要么全部被执行,要么全部都不执行。

然后再看官方文档关键段:
Redis commands can fail during a transaction, but still Redis will execute the rest of the transaction instead of rolling back

Redis 在事务失败时不进行回滚,而是继续执行余下的命令

我根据Redis文档理解,认为事务过程中失败有两种可能:

  1. Redis 命令只会因为错误的语法而失败(并且这些问题不能在入队时发现),或是命令中用在了错误类型的键上面,所以如果在生产环境中你使用的正常命令,那么在 Redis 事务中,是不会出现错误而导致回滚的。

来自文档:Redis commands can fail only if called with a wrong syntax...

  1. 事务执行一半,Redis宕机
    如果 Redis 服务器因为某些原因被管理员杀死,或者遇上某种硬件故障,那么可能只有部分事务命令会被成功写入到磁盘中。
    如果 Redis 在重新启动时发现 AOF 文件出了这样的问题,那么它会退出,并汇报一个错误。
    使用redis-check-aof程序可以修复这一问题:它会移除 AOF 文件中不完整事务的信息,确保服务器可以顺利启动
  2. 而且回滚实现很复杂,需要解决事务回滚覆盖的情况。

综上:所以Redis 没有事务回滚

这是中文文档地址:http://www.redis.cn/topics/transactions.html

这里第2条有一个疑问:如果发生了这种情况,redis会使用redis-check-aof程序移除AOF中不完整事务的信息,顺利启动。但这个时候是不是事务中的命令就只执行了一半?假设事务中有4个命令,完整的场景是:

  1. 执行命令1
  2. 执行命令2
  3. redis宕机
  4. 移除AOF中的不完整事务 & 顺利重启

这个时候是不是就只执行了一半的命令?重启之后还会对刚刚执行了一半的这个事务做额外处理吗?

@LiWenGu
Copy link
Contributor

LiWenGu commented Apr 29, 2021

我本地启动 redis 试了下,Mac 下的 redis-6.2.2
config 配置为:appendfsync always
客户端命令步骤顺序如下:

  1. set "name" “liwenguang”
  2. MULTI
  3. SET "age" “18"
  4. EXEC

此时查看 aof 文件:

红框框内为 MULTI 的命令,此时模拟 aof 文件损坏,也就是将 23~25 行手动删除,此时的 aof 文件:

redis-server 重启,会有警告日志:

当 redis 正常启动后,发现 aof 文件已被修复:13~23 行被删除:

结论:redis 会根据 aof 日志恢复,但是当 aof 日志有损坏的 MULTI 事务片段时,该片段会被删除,不存在执行一半的情况。
具体你想了解的各种情况,你可以自己试试。
@dingweihua

@dingweihua
Copy link

我本地启动 redis 试了下,Mac 下的 redis-6.2.2
config 配置为:appendfsync always
客户端命令步骤顺序如下:

  1. set "name" “liwenguang”
  2. MULTI
  3. SET "age" “18"
  4. EXEC

此时查看 aof 文件:

红框框内为 MULTI 的命令,此时模拟 aof 文件损坏,也就是将 23~25 行手动删除,此时的 aof 文件:

redis-server 重启,会有警告日志:

当 redis 正常启动后,发现 aof 文件已被修复:13~23 行被删除:

结论:redis 会根据 aof 日志恢复,但是当 aof 日志有损坏的 MULTI 事务片段时,该片段会被删除,不存在执行一半的情况。
具体你想了解的各种情况,你可以自己试试。
@dingweihua

谢谢 @LiWenGu 这个实验已经基本回答我的疑问了。换个方式,假设命令顺序如下:

MULTI
SET "age" "17"
SET "age" “18"
EXEC

aof被修复的时候,由于上面的整个multi的指令都会被删除,所以redis server重启之后,就不存在 set age相关的操作,所以age这个key也就不存在了,这个时候也就保持了原子性。这样理解对吗?

@LiWenGu
Copy link
Contributor

LiWenGu commented May 7, 2021

我觉得你理解没毛病。要么都执行要么都不执行。

@dingweihua
Copy link

我觉得你理解没毛病。要么都执行要么都不执行。

OK 谢谢!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
discuss discuss a problem enhancement New feature or request or suggestion perfect Improve knowledge points or descriptions
Projects
None yet
Development

No branches or pull requests

5 participants