Py学习  »  Redis

redis使用事务获取和设置密钥到期

Abhay • 5 年前 • 855 次点击  

我正在使用etaty rediscala( https://github.com/etaty/rediscala )客户。这是我的功能

private def getVersionTime(db: RedisClient, interval: Long)(implicit ec: ExecutionContext): Future[Long] = {

import akka.util.ByteString
import redis.ByteStringFormatter

implicit val byteStringLongFormatter = new ByteStringFormatter[Long] {
  def serialize(data: Long): ByteString = ByteString(data.toString.getBytes)
  def deserialize(bs: ByteString): Long = bs.utf8String.toLong
}

db.get[Long]("versionTime").map {
  case Some(v) => loggerF.info(s"Retrieved version time ${v}")
    v
  case None => val current = System.currentTimeMillis()
    db.setex[Long]("versionTime", (current / 1000) + interval, current)
    loggerF.info(s"set version time ${current}")
    current
}

}

这是我的测验。此测试调用上述方法

it("check with multiple tasks"){
  val target = 10
  val latch = new java.util.concurrent.CountDownLatch(target)
  (1 to target).map{t =>
    getVersionTime(prodDb, 10).map{r => print("\n" + r); latch.countDown()}
  }
  assert(latch.await(10, TimeUnit.SECONDS))
}

试验输出

14:52:46.692[池-1-线程-12]信息结束指示-设置版本时间1548062566687 14:52:46.693[池-1-线程-6]信息结束指示-设置版本时间1548062566687 14:52:46.693[池-1-线程-20]信息结束指示-设置版本时间1548062566687 14:52:46.692[池-1-线程-2]信息结束指示-设置版本时间1548062566668 14:52:46.692[池-1-线程-10]信息结束指示-设置版本时间1548062566687 14:52:46.693[池-1-线程-8]信息结束指示-设置版本时间1548062566687 14:52:46.692[池-1-线程-4]信息结束指示-设置版本时间1548062566668 14:52:46.692[池-1-线程-11]信息结束指示-设置版本时间1548062566687 14:52:46.692[池-1-线程-9]信息结束指示-设置版本时间1548062566687 14:52:46.692[池-1-线程-7]信息结束指示-设置版本时间1548062566687

预期的行为是-设置版本时间应该出现一次,对于其余线程,应该打印检索到的版本时间。我想我需要在这里使用事务,以便get和setex包装在watch和exec中

  private def getVersionTimeTrans(db: RedisClient, interval: Long): Long = {
    import akka.util.ByteString
    import redis.ByteStringFormatter

    implicit val byteStringLongFormatter = new ByteStringFormatter[Long] {
      def serialize(data: Long): ByteString = ByteString(data.toString.getBytes)
      def deserialize(bs: ByteString): Long = bs.utf8String.toLong
    }

    val redisTransaction = db.transaction()
    redisTransaction.watch("versionTime")
    val result: Future[Long] = redisTransaction.get[Long]("versionTime").map {
      case Some(v) => loggerF.info(s"Retrieved version time ${v}")
        v
      case None => val current = System.currentTimeMillis()
        redisTransaction.setex[Long]("versionTime", (current / 1000) + interval, current)
        loggerF.info(s"set version time ${current}")
        current
    }
    redisTransaction.exec()
    val r = for {
      i <- result
    } yield {
      i
    }
    Await.result(r, 10 seconds)
  }

测试

it("check with multiple threads "){
  val target = 10
  val latch = new java.util.concurrent.CountDownLatch(target)
  (1 to target).map{t =>
    Future(getVersionTimeTrans(prodDb, 10)).map{r => latch.countDown()}
  }
  assert(latch.await(10, TimeUnit.SECONDS))
}

对于这个测试,输出也是一样的。我想不出如何在事务中正确地包装它。请帮忙。

Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/47881
 
855 次点击  
文章 [ 2 ]  |  最新文章 5 年前
Abhay
Reply   •   1 楼
Abhay    6 年前

我用lua脚本解决了这个问题

SergGr
Reply   •   2 楼
SergGr    6 年前

看一下rediscala的实现,您似乎不能使用我在原始答案中建议的乐观锁(见下文),因为在rediscala中 TransactionBuilder 不会发送 WATCH 命令直到 EXEC 令它变得毫无用处的命令。有一个旧的关闭 bug on GitHub 指的是另一个 SO question 在这种情况下,答案是

在rediscala中,您无法读取事务内部的内容,因为您将阻止客户端执行其他请求。 我建议你试试看你是否能签入lua脚本。(在lua脚本中转换事务)

几个月后

它关闭是因为 不建议实施
你应该用 http://redis.io/commands#scripting

从2014年开始,情况似乎是一样的,我认为不会改变。


原始答案

我没有活生生的redis来测试它,但是看看 Redis transaction docs 看起来redis不像您想象的那样支持sql风格的事务。它支持原子操作,但不能执行“启动事务获取数据检查可能修改提交”循环。所有“获取数据”命令都将在 执行官 命令到了。这意味着您不能在同一事务中的get和set之间进行任何检查。

如果你看 使用检查和设置的乐观锁定 “在文档中,您可以看到实现您的行为的正确方法是:

  1. 创建事务 watch 为了钥匙
  2. 获取值 外部 交易的
  3. 检查该值,如果需要更新,则在事务内部发出更新。(别忘了 UNWATCH 如果不需要更新)
  4. 执行交易
  5. 检查事务是否失败,如果失败,则重复整个周期。如果没有-你是写价值的赢家。