事务
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
}
}