一.多数据库
一个redis实例可以包含多个数据库,客户端可以指定连接redis实例的哪个数据库。就好比我们在mysql中可以创建多个数据库一样,客户端可以指定连接哪个数据库。一个redis实例最多可以提供16个数据库,下标分别是从0-15,客户端默认连接第0号数据库。客户端可以通过select命令来选择连接哪个数据库:
select 1
#选择连接1号数据库
客户端可以通过move命令来将一个数据库中的key移动到另一个数据库中
select 0
move myset 1
#将0号数据库中的myset移动到1号数据库中
二.redis事务
在redis中可以使用multi exec discard 这三个命令来实现事务。在事务中,所有命令会被串行化顺序执行,事务执行期间redis不会为其他客户端提供任何服务,从而保证事务中的命令都被原子化执行
- multi 开启事务,这后边执行的命令都会被存到命令的队列当中
- exec 相当于关系型数据库事务中的commit,提交事务
- discard 相当于关系型数据库事务中的rollback,回滚操作
示例:
三.加锁
这里用一个信用卡刷卡的例子来讲redis加锁的问题:
假设balance为可用余额,debt为欠款,amtToSubtract为实刷额度,刷卡操作涉及到balance(可用余额)的减少和debt的增加,因此这里用事务进行管理,
package jedis;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Transaction;
public class TestTransaction {
public boolean transMethod() throws InterruptedException {
Jedis jedis = new Jedis("10.2.1.121", 6379);
jedis.auth("123456");
int balance;// 可用余额
int debt;// 欠额
int amtToSubtract = 10;// 实刷额度
jedis.watch("balance");//标记balabce余额
balance = Integer.parseInt(jedis.get("balance"));
if (balance < amtToSubtract) {
jedis.unwatch();
System.out.println("modify");
return false;
} else {
System.out.println("***********transaction");
Transaction transaction = jedis.multi();//开启事务
System.out.println("线程进入休眠......");
Thread.sleep(7000);
//jedis.set("balance","5");//此处不该出现,这是模拟多线程下其他程序已经修改了该条目,
//测试时我是在线程休眠时,手动在redis客户端中更改了balance的值
transaction.decrBy("balance", amtToSubtract);
transaction.incrBy("debt", amtToSubtract);
transaction.exec();//提交事务
balance = Integer.parseInt(jedis.get("balance"));
debt = Integer.parseInt(jedis.get("debt"));
System.out.println("*******" + balance);
System.out.println("*******" + debt);
return true;
}
}
public static void main(String[] args) throws InterruptedException {
TestTransaction test = new TestTransaction();
boolean retValue = test.transMethod();
System.out.println("main retValue-------: " + retValue);
}
}
通俗点讲,watch命令就是标记一个键,如果标记了一个键, 在提交事务前如果该键被别人修改过,那事务就会失败,这种情况通常可以在程序中重新再尝试一次。
在本例中,就首先标记了键balance,然后检查余额是否足够,不足就取消标记,并不做扣减; 足够的话,就启动事务进行更新操作,如果在此期间键balance被其它人修改, 那在提交事务(执行exec)时就会报错, 程序中通常可以捕获这类错误再重新执行一次,直到成功。