Redis(1.3)Redis的基本特性(事务、多数据库)
【1】两大特性
(1)多数据库
1个redis实例 可以有16个数据库,默认下标为0~15,默认连接到的是 0 下标的数据库。
(2)事务
【2】多数据库
【2.1】概念
1个redis实例 可以有16个数据库,默认下标为0~15,默认连接到的是 0 下标的数据库。
【2.2】基本操作
(1)select (切换数据库):select 1 把当前数据库切换到下标为 1 的数据库
(2)move(移动当前db下的 anykey 到其他 db):move nowdb_anykey new_db =》move a 1
(3)flushdb //清除当前数据库的所有 key
(4)flushall //清除所有数据库中的所有 key
【3】事务
【3.1】概念
所谓事务应具有以下特效:原子性(Atomicity), 一致性(Consistency),隔离性(Isolation),持久性(Durability),简称ACID,但redis所提供的事务比较简单,它通过MULTI、EXEC、DISCARD和WATCH等命令实现事务。
而Redis只支持简单的事务,将执行命令放入队列缓存,当程序中有异常或命令出错,执行DISCARD清空缓存队列不执行队列中命令,其事务过程有以下特点:
-
事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
-
事务是一个泛原子操作(这里我以泛原子称呼,在某些情况redis的事务不是原子性的,后续会说明):事务中的命令要么全部被执行,要么全部都不执行。
EXEC 命令负责触发并执行事务中的所有命令:
- 如果客户端在使用 MULTI 开启了一个事务之后,却因为断线而没有成功执行 EXEC ,那么事务中的所有命令都不会被执行。
- 另一方面,如果客户端成功在开启事务之后执行 EXEC ,那么事务中的所有命令都会被执行。
特别说明文中的泛原子操作:
- redis在开启事务以后,若执行命令具有显示的错误或者客户端中断则此次事务在执行EXEC命令时会调用DISCARD清空缓存队列不执行队列中的所有任务,此时是原子性的。
- (意思就是在multi 执行后,后面代码块输入出现语法错误,比如输入了一个不存在的命令等)
- 当执行命令过程中,命令没有显示的报错(例如LSET操作设置一个不存在的list),而是在EXEC调用时候某个命令出错,那么在这之前已经执行的命令将不会回滚,所以严格说来,redis并不支持原子性。
- (意思是在exec后,事务某一行有报错(比如操作的key不存在,比如给字符串做算术运算等),那么忽略改行,其他的继续运行)
与关系型数据库事务相比,
(1)multi:可以理解成关系型事务中的 begin
(2)exec :可以理解成关系型事务中的 commit
(3)discard :可以理解成关系型事务中的 rollback
【3.2】设计命令
MULTI #用于标记事务块的开始。Redis会将后续的命令逐个放入队列中,然后才能使用EXEC命令执行缓存队列中的命令。
EXEC #执行缓存队列中的命令
DISCARD #清除所有先前在一个事务中放入队列的命令,然后恢复正常的连接状态,如果使用了WATCH命令,那么DISCARD命令就会将当前连接监控的所有键取消监控。
WATCH key [key ...] #当某个事务需要按条件执行时,就要使用这个命令将给定的键设置为受监控的
UNWATCH #清除所有先前为一个事务监控的键,如果你调用了EXEC或DISCARD命令,那么就不需要手动调用UNWATCH命令
(1)multi:开启事务,把执行的命令添加串行化的命令队列中,直到执行exec。这些命令就会被原子化的执行了。
乐观锁机制
乐观锁:总是认为不会产生并发问题,每次去取数据的时候总认为不会有其他线程对数据进行修改,因此不会上锁,但是在更新时会判断其他线程在这之前有没有对数据进行修改,一般会使用版本号机制或检查再设置(CAS)操作实现。
redis通过WATCH命令实现乐观锁,作为WATCH命令的参数的键会受到Redis的监控,Redis能够检测到它们的变化。在执行EXEC命令之前,如果Redis检测到至少有一个键被修改了,那么整个事务便会中止运行,然后EXEC命令会返回一个nil值,提醒用户事务运行失败。
注意:WATCH命令需要在MULTI之前执行,不然redis会将其一个命令放入缓存队列中。
示例:在以下示例中通过一个客户端开启事务监听name键,另一个客户端在执行EXEC之前修改name键,此次事务将不会执行,并返回nil,如下。
原子性实践
为演示redis严格意义上将不支持原子性,做了一些简单实践。
从上面的结果可以看出,在开启事务前name 值为Rose,在开启事务先后执行了SET命令和LSET命令,但是LSET命令是错误的,当我们调用EXEC执行事务完事务以后,在回头看事务中的SET命令已经生效,并未回滚,因为在次过程中该命令没有显示的报错,所以可以说redis的事务不支持原子性。