首页 > 编程知识 正文

数据库优化面试题及答案,数据库优化

时间:2023-05-04 17:55:28 阅读:159258 作者:130

事务MULTI、EXEC、DISCARD和WATCH是与Redis事务相关的命令。 事务可以一次执行多个命令,有以下两个重要保证:

事务是独立的隔离操作。 事务中的所有命令都将序列化并按顺序执行。 在事务执行过程中,不会被来自其他客户端的命令请求中断。

事务是原子操作。 事务中的所有命令都将执行或不执行。

EXEC命令触发并执行事务中的所有命令。

如果客户端使用MULTI打开事务后,EXEC由于脱机而未成功运行,则事务中的所有命令都不会执行。 另一方面,如果客户端在事务启动后成功运行EXEC,则会执行事务中的所有命令。 使用AOF方法进行持久化时,Redis使用单个write(2)命令将事务写入磁盘。

但是,如果Redis服务器因某种原因被管理员杀死或发生硬件故障,则只有部分事务命令可能会成功写入磁盘。

如果Redis在重新启动时发现AOF文件出现此类问题,它将退出并报告错误。

可以使用redis-check-aof程序解决此问题。 不完整事务的信息将从AOF文件中删除,服务器将正常启动。

从2.2版开始,Redis还允许通过“乐观锁定”(optimistic lock )执行CAS (校验和与集)操作。 具体信息请参考文档的后半部分。

使用MULTI命令打开事务时,总是返回OK。 执行MULTI后,客户端可以继续向服务器发送任意数量的命令。 这些命令不会立即执行,而是在调用EXEC命令时排队以执行所有队列中的命令。

另一方面,通过调用DISCARD,客户端可以清空事务队列并中止事务的执行。

以下是以原子方式增加foo和bar两个键值的事务示例:

multiokincrfooqueuedincrbarqueuedexec1(integer ) 12 ) (integer )1 EXEC命令的回复是数组,数组中的每个元素都是执行事务中的命令后的回复。 这里,回复要素的优先顺序与命令发送的优先顺序一致。

如果客户端处于事务状态,则收到的所有命令都会返回内容为QUEUED的状态响应status reply,在调用EXEC命令时执行。

如果在事务中错误地使用事务,可能会出现以下两种错误:

在事务执行EXEC之前,入队的指令可能是错误的。 例如,命令可能会生成语法错误(参数计数错误、参数名称错误等)或其他更严重的错误(如果服务器使用maxmemory设置了最大内存限制)。 命令在EXEC调用之后可能会失败。 例如,事务中的命令可能处理了错误类型的键,例如,使用列表命令作为字符串键时。 对于在执行EXEC之前发生的错误,客户端以前会检查通过命令入队获得的返回值。 如果命令入队时返回QUEUED,则入队成功。 否则,入队失败。 如果入队期间有任何命令失败,大多数客户端都会停止并取消此事务处理。

但是,从Redis 2.6.5开始,服务器会记录命令入队失败,并在客户端调用EXEC命令时拒绝并自动丢弃此事务。

在Redis 2.6.5之前,Redis仅运行事务中入队成功的命令,而忽略入队失败的命令。 新的处理方法使在管线中包括事务变得简单。 这是因为发送事务和回复事务读取只需要与服务器进行一次通信。

执行EXEC命令后发生的错误未进行特殊处理。 即使事务中的某些命令在执行过程中发生错误,事务中的其他命令也会继续执行。

从协商的角度看这个问题,会更容易理解。 在以下示例中,尽管调用LPOP命令的语法正确,但在运行时会出现错误。

trying 127.0.0.1 . connectedtolocalhost.escapecharacteris ' ^ ].multiokseta3abcqueuedlpopaqueuedexec * 2ok-erroperopereroperis 第一个可以,第二个是-err。 至于如何用合适的方法表示事务中的错误,由客户自己决定。

最重要的是,记住,即使事务中的任何/部分命令失败,事务队列中的其他命令也会继续执行—— Redis,而不会停止事务中的命令的执行。

以下示例显示了另一种情况,其中如果命令在入队时发生错误,该错误会立即返回到客户端。

multiokincrabc-errwrongnumberof

arguments for 'incr' command

因为调用 INCR 命令的参数格式不正确, 所以这个 INCR 命令入队失败。

为什么 Redis 不支持回滚(roll back)

如果你有使用关系式数据库的经验, 那么 “Redis 在事务失败时不进行回滚,而是继续执行余下的命令”这种做法可能会让你觉得有点奇怪。

以下是这种做法的优点:

Redis 命令只会因为错误的语法而失败(并且这些问题不能在入队时发现),或是命令用在了错误类型的键上面:这也就是说,从实用性的角度来说,失败的命令是由编程错误造成的,而这些错误应该在开发的过程中被发现,而不应该出现在生产环境中。因为不需要对回滚进行支持,所以 Redis 的内部可以保持简单且快速。

有种观点认为 Redis 处理事务的做法会产生 bug , 然而需要注意的是, 在通常情况下, 回滚并不能解决编程错误带来的问题。 举个例子, 如果你本来想通过 INCR 命令将键的值加上 1 , 却不小心加上了 2 , 又或者对错误类型的键执行了 INCR , 回滚是没有办法处理这些情况的。

放弃事务

当执行 DISCARD 命令时, 事务会被放弃, 事务队列会被清空, 并且客户端会从事务状态中退出:

> SET foo 1OK> MULTIOK> INCR fooQUEUED> DISCARDOK> GET foo"1" 使用 check-and-set 操作实现乐观锁

WATCH 命令可以为 Redis 事务提供 check-and-set (CAS)行为。

被 WATCH 的键会被监视,并会发觉这些键是否被改动过了。 如果有至少一个被监视的键在 EXEC 执行之前被修改了, 那么整个事务都会被取消, EXEC 返回nil-reply来表示事务已经失败。

举个例子, 假设我们需要原子性地为某个值进行增 1 操作(假设 INCR 不存在)。

首先我们可能会这样做:

val = GET mykeyval = val + 1SET mykey $val

上面的这个实现在只有一个客户端的时候可以执行得很好。 但是, 当多个客户端同时对同一个键进行这样的操作时, 就会产生竞争条件。举个例子, 如果客户端 A 和 B 都读取了键原来的值, 比如 10 , 那么两个客户端都会将键的值设为 11 , 但正确的结果应该是 12 才对。

有了 WATCH , 我们就可以轻松地解决这类问题了:

WATCH mykeyval = GET mykeyval = val + 1MULTISET mykey $valEXEC

使用上面的代码, 如果在 WATCH 执行之后, EXEC 执行之前, 有其他客户端修改了 mykey 的值, 那么当前客户端的事务就会失败。 程序需要做的, 就是不断重试这个操作, 直到没有发生碰撞为止。

这种形式的锁被称作乐观锁, 它是一种非常强大的锁机制。 并且因为大多数情况下, 不同的客户端会访问不同的键, 碰撞的情况一般都很少, 所以通常并不需要进行重试。

了解 WATCH

WATCH 使得 EXEC 命令需要有条件地执行: 事务只能在所有被监视键都没有被修改的前提下执行, 如果这个前提不能满足的话,事务就不会被执行。了解更多->

WATCH 命令可以被调用多次。 对键的监视从 WATCH 执行之后开始生效, 直到调用 EXEC 为止。

用户还可以在单个 WATCH 命令中监视任意多个键, 就像这样:

redis> WATCH key1 key2 key3OK

当 EXEC 被调用时, 不管事务是否成功执行, 对所有键的监视都会被取消。

另外, 当客户端断开连接时, 该客户端对键的监视也会被取消。

使用无参数的 UNWATCH 命令可以手动取消对所有键的监视。 对于一些需要改动多个键的事务, 有时候程序需要同时对多个键进行加锁, 然后检查这些键的当前值是否符合程序的要求。 当值达不到要求时, 就可以使用 UNWATCH 命令来取消目前对键的监视, 中途放弃这个事务, 并等待事务的下次尝试。

使用 WATCH 实现 ZPOP

WATCH 可以用于创建 Redis 没有内置的原子操作。举个例子, 以下代码实现了原创的 ZPOP 命令, 它可以原子地弹出有序集合中分值(score)最小的元素:

WATCH zsetelement = ZRANGE zset 0 0MULTIZREM zset elementEXEC

程序只要重复执行这段代码, 直到 EXEC 的返回值不是nil-reply回复即可。

Redis 脚本和事务

从定义上来说, Redis 中的脚本本身就是一种事务, 所以任何在事务里可以完成的事, 在脚本里面也能完成。 并且一般来说, 使用脚本要来得更简单,并且速度更快。

因为脚本功能是 Redis 2.6 才引入的, 而事务功能则更早之前就存在了, 所以 Redis 才会同时存在两种处理事务的方法。

不过我们并不打算在短时间内就移除事务功能, 因为事务提供了一种即使不使用脚本, 也可以避免竞争条件的方法, 而且事务本身的实现并不复杂。

不过在不远的将来, 可能所有用户都会只使用脚本来实现事务也说不定。 如果真的发生这种情况的话, 那么我们将废弃并最终移除事务功能。

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。