Redis 事务及连接池

文章详细介绍了Redis的事务机制,包括Multi、Exec、Discard命令的使用,以及事务的错误处理和事务冲突的两种锁策略——乐观锁和悲观锁。此外,还讨论了Redis连接池的概念,解释了连接池参数的作用,并提供了Scala代码示例来展示如何创建和管理Jedis连接池。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

事务
Redis事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序的执行。事务的执行过程中,不会被其它客户端发送来的命令请求所打断
Redis的主要作用是串联多个命令防止别的命令插队

Multi、Exec、discard

从输入Multi命令开始,输入的命令都会依次进入命令队列中,但不会执行,直到输入Exec后,Redis会将之前的命令队列中的命令依次执行
组队的过程中可以通过discard来放弃组队

Aredis:0>multi
"OK"
Aredis:0>set e1 v1
"QUEUED"
Aredis:0>set e2 v2
"QUEUED"
Aredis:0>set e3 v3
"QUEUED"
Aredis:0>exec
1) "OK"
2) "OK"
3) "OK"
4) "OK"
5) "OK"
6) "OK"
7) "OK"
# 组队成功,提交成功

Aredis:0>multi
"OK"
Aredis:0>set v2 m2
"QUEUED"
Aredis:0>set v3
"ERR wrong number of arguments for 'set' command"
Aredis:0>set v1 m1
"QUEUED"
Aredis:0>exec
"EXECABORT Transaction discarded because of previous errors."
# 组队阶段报错,提交失败

Aredis:0>multi
"OK"
Aredis:0>set mm1 v1
"QUEUED"
Aredis:0>incr mm1
"QUEUED"
Aredis:0>set mm2 v2
"QUEUED"
Aredis:0>exec
1) "OK"
2) "OK"
3) "OK"
4) "ERR value is not an integer or out of range"
5) "OK"
6) "OK"
7) "OK"
# 组队成功,提交有成功有失败

事务的错误处理

组队中某个命令出现了报告错误,执行时整个队列都会被取消
如果执行阶段某个命令出现了错误,则只有报错的命令不会被执行,其余命令正常执行,不会回滚

事务冲突问题

悲观锁

悲观锁(Pessimistic Lock),认为每次取数据时都认为会被修改,所以每次取数据都会上锁,这样别人取这个数据就会block 知道它拿到锁。
传统关系型数据库用到了很多这种锁机制。如行锁、表锁、读锁、写锁等,都是在操作前先上锁

乐观锁

乐观锁(Optimistic Lock),认为每次取数据时不会被修改,所以不会上锁,但是在更新的时候会判断在此期间有没有去更新这个数据。
乐观锁适用于多读的应用类型,可以提高吞吐量。Redis利用的就是这猴子那个 check-and-set机制实现事务

WATCH key [key…]

在执行multi 之前,先执行 watch key, 可以监视一个或多个key,如果在事务 执行之前这些key被其它命令所改动,那么事务会被打断

Aredis:0>watch b1
"OK"
Aredis:0>multi
"OK"
Aredis:0>decrby b1 10
"QUEUED"
Aredis:0>incrby debt 10
"QUEUED"
Aredis:0>exec
1) "OK"
2) "-10"
3) "OK"
4) "10"
5) "OK"
UNWATCH

取消watch命令对key的监视
如果在执行watch命令之后,exec命令或discard命令先被执行,那么就不需要再执行unwatch

事务三大特性

  • 单独隔离操作
    • 事务中的所有命令都会序列化、按顺序的执行。事务在执行过程中,不会被其它客户端发送来的请求打断
  • 没有隔离级别
    • 队列中的命令没有提交之前都不会实际地被执行,事务提交前任何指令都不会被实际执行
  • 不保证原子性
    • 事务中如果有一条命令执行失败,其后地命令依然会被执行,不会回滚

连接池

节省每次lianjieredis服务带来的损耗,把连接好的实例反复利用
通过参数管理连接的行为

  • 连接池参数
    • MaxTotal:控制一个pool可分配多少个jedis实例,通过pool.getResource()来获取;如果赋值为 -1,则表示不限制;如果pool已经分配了MaxTotal个jedis实例,则此时pool的状态为exhausted
    • maxIdle:控制一个pool最多有多少个状态为idl(空闲)的jedis实例;
    • MaxWaitMillis:表示当borroow一个jedis实例时,最大的等待毫秒数,如果超过等待时间,则直接抛出JedisConnectionException;
    • testOnBorrow:获得一个jedis实例的时候是否检查连接的可用性(ping());如果为true,则得到的jedis实例均是可用的;
scala 实例代码如下:
package com.github.utils

import redis.clients.jedis.{Jedis, JedisPool, JedisPoolConfig}

object RedisPoolUtil {

  private var jedisPool: JedisPool = _
  private val jedisPoolConfig = new JedisPoolConfig
  jedisPoolConfig.setMaxTotal(100)
  jedisPoolConfig.setMaxIdle(20)
  jedisPoolConfig.setMinIdle(20)
  jedisPoolConfig.setTestOnBorrow(true)
  jedisPoolConfig.setTestOnReturn(true)
  jedisPoolConfig.setMaxWaitMillis(10000)


  def makePool(redisHost: String, port: Int, timeout: Int, password: String): JedisPool = {
    if (jedisPool == null) {
      jedisPool = new JedisPool(jedisPoolConfig, redisHost, port, timeout, password)
    }
    jedisPool
  }

  def getJedisClient(jedisPool: JedisPool): Jedis = {
    assert(jedisPool != null)
    val jedis: Jedis = jedisPool.getResource
    jedis
  }

  def releasePool(): Unit = {
    jedisPool.close()
  }

}

使用如下:

import com.github.utils.RedisPoolUtil
import redis.clients.jedis.{Jedis, JedisPool}

import java.util


object redist {
  private val redisHost = "127.0.0.1"
  private val redisPort = 6379
  private val password = "xxxxxxx"

  def main(args: Array[String]): Unit = {
    val pool: JedisPool = RedisPoolUtil.makePool(redisHost, redisPort, 10000, password)

    val jedis: Jedis = RedisPoolUtil.getJedisClient(pool)

    val strings: util.List[String] = jedis.mget("m1", "m2", "m3")

    println(strings)
    RedisPoolUtil.releasePool()
  }

}

// 输出结果
[ms1, v2, v3]

Process finished with exit code 0
简短写法
package com.dev.util

import redis.clients.jedis.{Jedis, JedisPool, JedisPoolConfig}

object MyRedisUtils {

  /**
   * Redis工具类,用于获取Jedis连接, 操作Redis
   */

  var jedisPool: JedisPool = null

  def getJedisFromPool(): Jedis = {
    if (jedisPool == null) {
      //创建连接池对象
      //连接池配置
      val jedisPoolConfig = new JedisPoolConfig()
      
      jedisPoolConfig.setMaxTotal(100) //最大连接数
      jedisPoolConfig.setMaxIdle(20) //最大空闲
      jedisPoolConfig.setMinIdle(20) //最小空闲
      jedisPoolConfig.setBlockWhenExhausted(true) //忙碌时是否等待
      jedisPoolConfig.setMaxWaitMillis(5000) //忙碌时等待时长 毫秒
      jedisPoolConfig.setTestOnBorrow(true) //每次获得连接的进行测试
      
      val host: String = "localhost"
      val port: String = 6379
      val auth: String = "123456"
		
      jedisPool = new JedisPool(jedisPoolConfig, host, port.toInt, 10000, auth)
    }
    val jedis: Jedis = jedisPool.getResource
    // 返回jedis
    jedis
  }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值