memcached的cas就是数据库的原子性操作,就是一个乐观锁
举例子,用户A和用户B同时对一张银行卡取下钱,银行卡有1000元,用户A取了300,用户B也取了200,同时操作,那么可能会出现用户A扣钱后变700,而用户B也是同时操作的,扣完变800,那么实际上取了500,但是账号余额还有800。
这个时候就是同时操作造成的问题,那么cas就是在用户A取钱的时候,跟他一个cas_token,扣钱的时候检查当前的memcached的token和用户A的cas_token是否一样,一样就是更新数据。但是用户B取得的cas_token,明显跟memcached当前的token不一样,所以更新不会成功,那么就要重新操作一次,保证用户B取钱的时候,余额是700,而不是1000。
这里顺便整理的数据库事务操作的四个特性ACID,原子性(Atomicity),一致性(Consistency),隔离性(Isolation),持久性(Durability)。
所谓的事务,它就是一个操作序列,这些操作要么全部执行,要么全部不执行,它是一个不可分割的工作单位。
原子性
原子性是指事务是一个不可再分割的工作单元,事务中的操作要么都发生,要么都不发生。
可采用“A向B转账”这个例子来说明解释
在DBMS中,默认情况下一条SQL就是一个单独事务,事务是自动提交的。只有显式的使用start transaction开启一个事务,才能将一个代码块放在事务中执行。
一致性
一致性是指在事务开始之前和事务结束以后,数据库的完整性约束没有被破坏。这是说数据库事务不能破坏关系数据的完整性以及业务逻辑上的一致性。
如A给B转账,不论转账的事务操作是否成功,其两者的存款总额不变(这是业务逻辑的一致性,至于数据库关系约束的完整性就更好理解了)。
保障机制(也从两方面着手):数据库层面会在一个事务执行之前和之后,数据会符合你设置的约束(唯一约束,外键约束,check约束等)和触发器设置;此外,数据库的内部数据结构(如 B 树索引或双向链表)都必须是正确的。业务的一致性一般由开发人员进行保证,亦可转移至数据库层面。
隔离性
多个事务并发访问时,事务之间是隔离的,一个事务不应该影响其它事务运行效果。
在并发环境中,当不同的事务同时操纵相同的数据时,每个事务都有各自的完整数据空间。由并发事务所做的修改必须与任何其他并发事务所做的修改隔离。事务查看数据更新时,数据所处的状态要么是另一事务修改它之前的状态,要么是另一事务修改它之后的状态,事务不会查看到中间状态的数据。
事务最复杂问题都是由事务隔离性引起的。完全的隔离性是不现实的,完全的隔离性要求数据库同一时间只执行一条事务,这样会严重影响性能。
关于隔离性中的事务隔离等级(事务之间影响),参见相应博文
持久性
这是最好理解的一个特性:持久性,意味着在事务完成以后,该事务所对数据库所作的更改便持久的保存在数据库之中,并不会被回滚。(完成的事务是系统永久的部分,对系统的影响是永久性的,该修改即使出现致命的系统故障也将一直保持)
write ahead logging:SQL Server中使用了WAL(Write-Ahead Logging)技术来保证事务日志的ACID特性,在数据写入到数据库之前,先写入到日志,再将日志记录变更到存储器中。
Memcached-CAS-JAVA操作
public static void main(String[] args) throws Exception{
MemcachedClient client = new MemcachedClient(new InetSocketAddress("127.0.0.1", 11211));
client.set("x", 9000, "1");
String x1 = (String) client.get("x");
String x2 = (String) client.get("x");
x2 = "2";
client.set("x", 900, x2);
System.out.println("x1="+x1);
System.out.println("x2="+x2);
//以上那样操作肯定会引起不一致行,违法数据库Consistency一致性
//接下来,我们采用CASValue
client.set("y", 900, "3");
//这里要使用gets,gets和get的区别在于gets可以获得的信息稍微多一些,64位的整型值非常像名称/值对的版本标识符,也就是casId,通过cas.getCas()获得
CASValue cas1 = client.gets("y");
CASValue cas2 = client.gets("y");
client.cas("y", cas2.getCas(), "6");
System.out.println("cas1-value:"+cas1.getValue()+",cas1-token:"+cas1.getCas());
System.out.println("cas2-value:"+cas2.getValue()+",cas2-token:"+cas2.getCas());
client.shutdown();
}
运行结果
==============
2018-10-30 14:17:59.402 INFO net.spy.memcached.MemcachedConnection: Connection state changed for sun.nio.ch.SelectionKeyImpl@6eb96738
x1=1
x2=2
cas1-value:3,cas1-token:38
cas2-value:3,cas2-token:38
2018-10-30 14:17:59.418 INFO net.spy.memcached.MemcachedConnection: Shut down memcached client