众所周知,Redis采用的是异步I/O非阻塞的单进程模型,每一条Redis命令都是原子性的。那么mongoDB呢? mongo有哪些原子操作呢?有哪些实现事务性操作的技巧呢?

1.对单个文档的原子性修改

mongoDB保证了对单个document的多个filed的原子性修改。如果需要对单个文档进行原子性的CAS操作(check and set),可以使用findAndModify操作。

比如下面就是一条原子性的CAS操作,首先选择_id为123的文档(注意这里只选择了一个文档),然后对计数器count加1,将status字段变为true,并返回修改后的结果。

db.colleciton.findAndModify({query:{_id:'123'},$inc:{count:1},$update:{status:true}},new:true);

2.对多个文档使用$isolate操作符

$isolate操作符可以对多个文档的修改提供隔离性。针对其他线程的并发写操作,$isolate保证了提交前其他线程无法修改对应的文档。针对其他线程的读操作,$isolate保证了其他线程读取不到未提交的数据。

但是$isolate有验证的性能问题,因为这种情况下线程持有锁的时间较长,严重的影响mongo的并发性。另外,$isolate也无法保证多个文档修改的一致性(all-or-nothing),$isolate失败是可能只修改了部分文档。

3.从语意层面实现事务性操作

mongoDB官方提供了一种做法,即两阶段提交(two-phase commit),基本的原理就是利用了写操作的幂等性。具体实现可以看官网的详细讲解。但是利用幂等性来实现事务性有一个重要的前置条件:业务不在乎中间态的不一致。幂等性可以保证最终的一致性,但是会出现中间的不一致状态。


参考资料: (1) mongoDB原子性与实务

Hive可以将类sql查询语句转换成Hadoop的map reduce任务,让熟悉关系型数据库的人也可以利用hadoop的强大并行计算能力。Hive提供了强大的内置函数支持,但是总有一些特殊情况,内置函数无法覆盖,这就要求我们对定义自己的函数。接下来我们通过一个例子看一下如何自定义hive函数。

1. 自定义函数的实现

假设我们的关系型数据库中user表有一个status字段,代表着用户的活跃等级,取值为1~10,活跃度一次递增。现在我们要根据status字段将用户分为3个活跃度等级。Hive显然没有这种与业务逻辑强耦合的内置函数,但这不应该成为阻碍我们使用Hive的理由。下面的扩展函数就可以满足需求。

package com.test.example;
import org.apache.hadoop.hive.ql.exec.Description;
import org.apache.hadoop.hive.ql.exec.UDF;
import org.apache.hadoop.io.Text;
public class UserStatus extends UDF {
   public Text evaluate(Text input) {
     if(input == null) return null;
     int status= Integer.parseInt(input.toString());
     if(status>= 1 && status<= 3){
         return new Text(String.valueOf(1));
     }else if(status>=4 && status<=7){
         return new Text(String.valueOf(2));
     }else if(status>=7 && status<=10){
         return new Text(String.valueOf(3));
     }
     return null;
   }
}

从上面的例子可以看出实现自定义的hive函数还是相当简单的。就是继承org.apache.hadoop.hive.ql.exec.UDF 并实现execute函数。

2. 自定义函数的使用

定义为自定义函数后该如何使用呢?其实也是相关简单的。假设包含自定义函数的jar包为mydf.jar。

(1).在hive shell中加载

首先加载jar包,并创建临时函数\

%> hive
hive> ADD JAR /path/to/mydf.jar;
hive> create temporary function userStatus as 'com.test.example.UserStatus';

然后就可以直接使用了

hive> select userStatus(4);

但是每次使用都要加载一次,太费劲了。有没有别的方法呢。

(2).在.hiverc中加载

编辑home目录下的.hiverc文件,如果没有这个文件就新建一个。将加载jar包的命令写入.hiverc文件,启动hive shell时会自动执行.hiverc文件,不需要每个shell都load一遍。


参考资料: hive内置函数, hive自定义函数demo