SEO网站公司,手机做任务的网站有哪些内容,网站seo综合公司,深圳网站建设799元起全包域名Redis事务与MySQL事务
不一样。原子性#xff1a;MySQL有Undo Log机制#xff0c;支持强原子性#xff0c;和回滚。Redis只能保证事务内指令可以不被干扰的在同一批次执行#xff0c;且没有机制保证全部成功则提交#xff0c;部分失败则回滚。隔离性#xff1a;MySQL的隔…Redis事务与MySQL事务
不一样。原子性MySQL有Undo Log机制支持强原子性和回滚。Redis只能保证事务内指令可以不被干扰的在同一批次执行且没有机制保证全部成功则提交部分失败则回滚。隔离性MySQL的隔离性指多个事务可以并发执行MySQL有MVCC机制。而Redis没有Redis是事务提交前的指令不会被执行单线程的环境下也就不存在事务未提交时事务内外数据不一致的隔离性问题了。持久性MySQL事务先写Undo Log并有Redo Log的两阶段提交机制可以保证持久性。但是Redis持久化机制只有RDB和AOF持久化策略若事务成功执行且数据刚好被保存则可以满足持久性。一致性MySQL是指数据库从一个合法指符合业务预期状态转换成另一个合法状态这种只要Redis执行不出错可以保证。
Redis事务
官方文档https://redis.io/docs/latest/develop/interact/transactions/极简概括将一批要执行的Redis指令放入Redis的执行队列中事务执行时不包含事务未提交时 使其不被并发过来的任务干扰执行。无法做到严格意义上的ACID 4特性。适用场景 性能优化10条命令传输10次执行10次与1次批量执行10条命令性能有差异。乐观锁实现结合Watch可以实现乐观锁。 优点如上的应用场景就是优点。缺点无法像MySQL那样保证原子性、持久性。关键字mutli开启事务discard停止事务、exec执行事务、watch监视指定key、unwatch取消监视所有key。
事务操作实操
测试multi与exec常规执行
127.0.0.1:6379 multi
OK
127.0.0.1:6379 set a a
QUEUED
127.0.0.1:6379 set b b
QUEUED
127.0.0.1:6379 exec
1) OK
2) OK测试discard事务未提交强行终止则修改不会生效
127.0.0.1:6379 multi
OK
127.0.0.1:6379 set a a1
QUEUED
127.0.0.1:6379 set b b1
QUEUED
127.0.0.1:6379 discard
OK
127.0.0.1:6379 get a
a
127.0.0.1:6379 get b
bRedis事务异常语法错误导致整个事务执行失败非回滚操作
127.0.0.1:6379 multi
OK
127.0.0.1:6379 set a a2
QUEUED
127.0.0.1:6379 sset b b2
(error) ERR unknown command sset, with args beginning with: b, b2,
127.0.0.1:6379 exec
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379 get a
a
127.0.0.1:6379 get b
bRedis事务异常非语法错误引起的部分失败无法保证ACID中的A无回滚机制
127.0.0.1:6379 multi
OK
127.0.0.1:6379 set a aa
QUEUED
127.0.0.1:6379 incr a
QUEUED
127.0.0.1:6379 set b bb
QUEUED
127.0.0.1:6379 exec
1) OK
2) (error) ERR value is not an integer or out of range
3) OK
127.0.0.1:6379 get a
aa
127.0.0.1:6379 get b
bb有Redis事务为什么又出来了Lua
Redis事务和Lua机制并不冲突并且要比Redis事务更加强大。应对并发安全问题虽然有了Lua的加持仍不支持事务回滚或者强原子性要么都成功要么都回滚但是Lua可以保证当前的操作不被打断无间隙执行应对并发例如超卖问题Lua能妥善解决。Redis事务不支持流程控制只支持函数调用配合Lua用于实现无间隙执行的复杂逻辑这样的用法非常多。因为高并发下若单纯利用编程语言多次调Redis实现判断或循环逻辑这中间有间隙会有并发问题发生。 Lua是一门高性能脚本语言Lua由标准C编写而成几乎在所有操作系统和平台上都可以编译、运行。Lua脚本可以很容易的被C/C代码调用也可以反过来调用C/C的函数这使得Lua在应用程序中可以被广泛应用。
关于RedisLua是否是原子性执行的争议问题
https://redis.io/docs/latest/develop/interact/programmability/eval-intro/ 对Redis官网进行搜索出现了原子性的字眼。 原话是 Blocking semantics that ensure the script’s atomic execution. Lua lets you run part of your application logic inside Redis. Such scripts can perform conditional updates across multiple keys, possibly combining several different data types atomically.
但是我想了想有矛盾的地方 MySQL使用了undo log来保证原子性要么成功全部执行要么失败全部回滚。 众所周知Redis不支持回滚的那么ACID的A就没办法全部保证最多是没有执行期间没有间隙不被其它过来的请求影响引起并发问题。
然后我又看了看阿里某架构师对此的剖析跟我设想的一样 Redis会把Lua脚本当做一个整体去执行中间不会被其它的命令插入但是如果执行过程中出现了错误事务是不会回滚的。 也就意味着执行Lua脚本的过程不可被拆分不可被中断但是遇到错误不会回滚。
Redis乐观锁
悲观锁很悲观认为数据大概率会有并发一致性问题首次请求过来时加具有互斥性的锁阻塞其它并发请求但是Redis是高性能组件阻塞会带来性能问题所以不用悲观锁。乐观锁乐观认为数据小概率有并发一致性问题所以读数据时不上锁但是写数据时会判断一下这个数据是否被改动从而在旧值的基础上做修改如果数据被改动则失败掉此次执行。注意redis在事务exec或者discard都会取消对key的watch操作。解决问题高并发读多写少场景下Redis数据一致性问题。演变
假设用户a账户有100元此时要添加10元
127.0.0.1:6379 set a_money 100
OK
127.0.0.1:6379 multi
OK
127.0.0.1:6379 incrby a_money 10
QUEUED
127.0.0.1:6379 exec
1) (integer) 110
127.0.0.1:6379 get a_money
110假设用户a账户有110元此时要添加20元但是事务未提交期间已经被其它请求改为了115然后事务内加了20。 由于是加法所以值正确但是事务内的数据一般是不让改的很多情况下的自增或者自减是需要以原数据为基础基础为准的这也是MySQL隔离级别的用意所以有了当前读和快照读的区分。
终端1
127.0.0.1:6379 get a_money
110
127.0.0.1:6379 multi
OK
127.0.0.1:6379 incrby a_money 20
QUEUED终端2
127.0.0.1:6379 get a_money
110
127.0.0.1:6379 incrby a_money 5
(integer) 115终端1
127.0.0.1:6379 get a_money
110
127.0.0.1:6379 multi
OK
127.0.0.1:6379 incrby a_money 20
QUEUED
127.0.0.1:6379 exec
1) (integer) 135
127.0.0.1:6379 get a_money
135Redis没有事务的隔离机制怎么办使用watch加锁。
终端一
127.0.0.1:6379 watch a_money
OK
127.0.0.1:6379 get a_money
135
127.0.0.1:6379 multi
OK
127.0.0.1:6379 incrby a_money 20
QUEUED终端二模拟其它并发用户
127.0.0.1:6379 incrby a_money 5
(integer) 140终端1
127.0.0.1:6379 exec
(nil)
127.0.0.1:6379 get a_money
140
事务没有成功被执行因为watch监控了a_money的值一旦事务执行期间被事务外的请求锁修改则失败掉此次事务。
乐观锁在此处的体现就是利用watch监控一下事务执行期间a_money的值是否被改动。unwatch 使用
终端1
127.0.0.1:6379 set a a
OK
127.0.0.1:6379 watch a
OK
127.0.0.1:6379 unwatch
OK
127.0.0.1:6379 multi
OK
127.0.0.1:6379 set a a1终端2模拟并发过来的用户请求
127.0.0.1:6379 set a a2
OK终端1执行unwatch后取消了对所有key的监控执行exec时就不是nil了。
127.0.0.1:6379 exec
1) OK
127.0.0.1:6379 get a
a1watch部分key其余key的反应
终端1
127.0.0.1:6379 set a a
OK
127.0.0.1:6379 set b b
OK
127.0.0.1:6379 watch a
OK
127.0.0.1:6379 multi
OK
127.0.0.1:6379 set a a1
QUEUED
127.0.0.1:6379 set b b1
QUEUED终端2
127.0.0.1:6379 set a a2
OK
127.0.0.1:6379 set b b2
OK终端1watch a,没有watch b事务提交时被watch的key可以影响没有被watch的key。
127.0.0.1:6379 exec
(nil)
127.0.0.1:6379 get a
a2
127.0.0.1:6379 get b
b2
管道
官方文档https://redis.io/docs/latest/develop/use/pipelining/极简概括将多个指令的操作一次性发送给Redis进行批量处理。解决问题减少网络开销减少频繁接收命令的开销10轮request-exec-response精简为1次request-10次exec-1次response避免多条Redis指令通信往返时间。避免Redis服务器频繁的从用户态到内核态的调用减少上下文通信时间。与事务对比批量处理指令的行为类似事务。注意redis-cli会话内部并未提供管道命令但是使用Linux Shell端支持STDIN标准输入到redis-cli实现管道例如echo -e set a aa \n set b bb | redis-cli --pipe但redis-server提供了这个机制管道机制最好用编程语言的客户端演示。
若在redis-cli会话内部实现管道会有如下提示
127.0.0.1:6379 pipe
(error) ERR unknown command pipe, with args beginning with:
127.0.0.1:6379 pipeline
(error) ERR unknown command pipeline, with args beginning with:PHP实现
?php
$redis new Redis();
$redis-connect(127.0.0.1, 6379);$pipe $redis-pipeline();$pipe-set(key1, value1);
$pipe-set(key2, value2);
$pipe-get(key1);
$pipe-get(key2);$responses $pipe-exec();var_dump($responses);$redis-close();返回执行的结果
array(4) {[0]bool(true)[1]bool(true)[2]string(6) value1[3]string(6) value2
}管道异常情况Redis语法错误
以PHP为例经实际测试set函数缺少参数2Redis调用语法错误非PHP语法错误会升级为PHP出现致命错误管道流程走不下去。
Fatal error: Uncaught ArgumentCountError: Redis::set() expects at least 2 arguments, 1 given in E:\Host\test\t1.php:7
Stack trace:
#0 E:\Host\test\t1.php(7): Redis-set(a)
#1 {main}thrown in E:\Host\test\t1.php on line 7管道异常情况Redis执行异常
经过实测对字符串进行递增操作除了incr返回false外其余上下文代码执行不受影响。
?php
$redis new Redis();
$redis-connect(127.0.0.1, 6379);$pipe $redis-pipeline();$pipe-set(a, a);
$pipe-incr(a);
$pipe-set(b, b);
$pipe-get(a);
$pipe-get(b);$responses $pipe-exec();var_dump($responses);$redis-close();array(5) { [0] bool(true)[1]bool(false)[2]bool(true)[3]string(1) a[4]string(1) b
}