添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
相关文章推荐
睿智的打火机  ·  如何为jsessionid ...·  1 年前    · 
温柔的开水瓶  ·  Android ...·  1 年前    · 
温柔的豆浆  ·  javascript - ...·  1 年前    · 

分布式并发计数,以视频站点播放数统计为例(本质是{vid->count}映射关系),内容提要:

  • Upsert+$INC解决并发计数
  • findAndModify解决写时返回结果
  • JAVA实现:findAndModify+upsert+$INC三剑客
  • 谢绝ObjectId,用vid直接做_id

(1)Upsert+$INC解决并发计数

第一点:第一次update的时候,提示“ok”;但是查询的时候,发现提示“ok”其实依然失败了。这点表现了MongoDB的fire-and-forget特性,默认情况下MongoDB不执行getLastError(),只管发送写请求,不等待写请求的响应,也就是不管能否写成功。听说MongoDB并发控制策略上用了个“DB级别的全局锁(不是表级,关系型一般了不起是全表锁定,MongoDB却是DB级的)”,难道为了弥补这个影响写性能的缺陷,来了个“fire-and-forget”,这样全局锁对客户来说就的确没有等待的影响了,这是不是太投机了呀?!

第二点:第二次更新操作,多了第三个参数“true”,表示upsert标记(upsert=update+insert),语义是:如果第一个参数Query文档匹配到了,则执行update;如果没匹配到,则insert。同时第二个参数Modifier文档使用了$inc,表示原子累加操作。这两个特性的完美结合,非常适用于“互联网的PV实时统计或视频网站的播放数实时统计”。如果这个逻辑,用关系型实现,那代码要复杂许多。

(2)findAndModify解决写时返回结果


上面说了,“upsert标记+$inc修饰器”完美组合就能轻松搞定“分布式并发计数器”的应用场景(比如:PV统计,视频站点的VV统计)。但是我们以VV统计为例,一个真正实时计数器在执行写操作的时候,往往还需要同时返回更新后的VV数,也就是说写操作的同时应该伴随读操作。显然“upsert标记+$inc修饰器”组合还无法满足需求?那么MongoDB 还会不会有新特性呢?很遗憾,MongoDB的确可以有这种场景的操作“findAndModify”,但是“findAndModify”有两个遗憾(据《MongoDB权威指南》介绍):
(1)    丢失upsert特性: 可以返回更新后,或者更新前的文档的状态,但是不具备upsert标记。也就是Query必须匹配上,否则无法更新,也无法插入。
(2)    性能慢:findAndModify据《MongoDB权威指南》介绍,它的时间开销=find一次+update一次+getLastError一次,三者顺序执行所需要的时间。之前我们说,MongoDB很可能是因为“fire-and-forget”,不等待响应(响应需要通过getLastError指令获得),来规避全局锁的开销,现在findAndModify的的确确需要返回结果,所以这个开销是免不了的。
这么看来,findAndModify有点鸡肋?!《MongoDB权威指南》中介绍findAndModify的时候,举了一个例子,大概可以理解为“分布式任务队列”(分布式任务队列也是架构中常用的模型,理念上是MQ,或者AOP,适合“异步事件”的场景),用findAndModify比用乐观锁解决race condition的问题上要方便些。先不管findAndModify是否鸡肋,先看看findAndModify的API吧: http://www.mongodb.org/display/DOCS/findAndModify+Command  。

MongoDB 1.3+ supports a "find, modify, and return" command.  This command can be used to atomically modify a document (at most one) and return it. Note that, by default, the document returned will not include the modifications made on the update.
If you don't need to return the document, you can use Update (which can affect multiple documents, as well).

官方建议:如果你打算用findAndModify,而不用update,那么一个前提条件是:你想知道更新后的结果(文档状态,不仅仅是getLastError信息)。如果你并不想知道更新后的结果,那么你更应该选择update操作。

Oh, my god,看到上面这幅图,我只能说MongoDB很年轻,因为年轻有很多鸡肋式的不足,因为年轻更有日新月异的成长。

(3)JAVA实现:findAndModify+upsert+$INC三剑客
我们简单来段JAVA代码,开发包是:
https://github.com/mongodb/mongo-java-driver/blame/master/src/main/com/mongodb/DBCollection.java

import com.mongodb.BasicDBObject;
import com.mongodb.DB;
import com.mongodb.DBCollection;
import com.mongodb.DBObject;
import com.mongodb.Mongo;
public static void main(String[] args) throws Exception {
		String host = "XXX";
		int port = XXX;
		String dbname = "XXX";
		String username = "XXX";
		String passwd = "XXX";
		Mongo mongo = new Mongo(host, port);
		DB db = mongo.getDB(dbname);
		boolean authed = db.authenticate(username, passwd.toCharArray());
		System.out.println("认证:"+authed);
		System.out.println("DB下所有集合:"+db.getCollectionNames());
		//CH3: 获取DB
		String collectionName = "vv_stat";
		DBCollection collection = db.getCollection(collectionName);
		//public DBObject findAndModify(DBObject query, DBObject fields, DBObject sort, boolean remove, DBObject update, boolean returnNew, boolean upsert)
		//REFER:  https://github.com/mongodb/mongo-java-driver/blame/master/src/main/com/mongodb/DBCollection.java
		String vid = "10086";
		int count = 38;
		BasicDBObject query = new BasicDBObject().append("vid", vid);
		BasicDBObject fields = new BasicDBObject().append("vid", 1).append("count", 1);
		BasicDBObject sort = new BasicDBObject().append("vid", 1);
		boolean remove = false;
		BasicDBObject update = new BasicDBObject().append("$inc", new BasicDBObject("count",count));
		boolean returnNew = true;
		boolean upsert = true;
		DBObject r = collection.findAndModify(query,fields,sort,remove,update,returnNew,upsert);
		System.out.println("统计结果:"+r);

输出:
统计结果:{ "_id" : { "$oid" : "5029dc79bc7ee3893a74cffe"} , "count" : 38 , "vid" : "10086"}

很完美了!至少功能上是完全符合我们的需求了,性能的问题暂不理会。

(4)谢绝ObjectId,用vid直接作为_id
上面的输出{ "_id" : { "$oid" : "5029dc79bc7ee3893a74cffe"} , "count" : 38 , "vid" : "10086"},我们看到插入的文档除了vid和count,还有_id,因为每个Doc都必须有_id,而且它是唯一索引。播放数统计就是从vid到count映射关系,数据量大了,为了提高查询效率,我们要对vid做索引,而且是唯一索引,那为什么要浪费原有的_id呢?其实_id天然就是NoSQL特性之Key/Value的友好支持。于是,我们应该把vid存储在_id上。

BasicDBObject query = new BasicDBObject().append("_id", vid);
BasicDBObject fields = new BasicDBObject().append("_id", 1).append("count", 1);
BasicDBObject sort = new BasicDBObject().append("_id", 1);
boolean remove = false;
BasicDBObject update = new BasicDBObject().append("$inc", new BasicDBObject("count",count));
boolean returnNew = true;
boolean upsert = true;
DBObject r = collection.findAndModify(query,fields,sort,remove,update,returnNew,upsert);
System.out.println("统计结果:"+r);

输出:“统计结果:{ "_id" : "10086" , "count" : 38}”

 分布式并发计数,以视频站点播放数统计为例(本质是{vid-&amp;gt;count}映射关系),内容提要:Upsert+$INC解决并发计数findAndModify解决写时返回结果JAVA实现:findAndModify+upsert+$INC三剑客谢绝ObjectId,用vid直接做_id(1)Upsert+$INC解决并发计数  第一点:第一次up... LuMongo不再被积极开发。 现在我们的工作集中在 使用Lucene进行分布式实时搜索 LuMongo是基于Lucene的实时分布式搜索和存储系统。 LuMongo从头开始设计,可以在服务器之间垂直和水平扩展。 LuMongo将Lucene索引直接存储到MongoDB中。 文档可以本地存储在MongoDB中。 当以本地方式存储文档时,可以从MongoDB中正常查询文档,并且可以使用和 。 LuMongo是: 纯Java LuMongo支持: 使用单个查询搜索多个索引 将相关文档与文档一起存储 要了解更多信息,请参见 : <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-mongodb</artifactId> </dependency> 2.application.properties配置mon.. 实时性有要求,单并非是严格要求,根据据量的大小可适当延迟 严格要求据一致性,即在据正确的情况下计的结果应该和总记录是一致的 服务是可扩展的,尽量避免受限于单一资源的限制 尽可能的考虑容错 实时据流接入后由Spark... npm install -g loadbalancer 启动负载均衡器 loadbalancer start --config load-balancer-config/config.json ./gradlew clean build 运行第一个实例 java -jar build/libs/distributed-site-hit-counter-0.0.1-SNAPSHOT.jar --server.port=9091 运行第二个实例 java -jar build/libs/distributed-site-hit-counter-0.0.1-SNAPSHOT.jar --server.port=9092 curl -X GET \ http:
LED即发光二极管,是一种有方向性的半导体固体发光器件,在单片机上是贴片形式 LED的 阳极 串联一个电阻,然后连接到电源VCC,而LED的 阴极 连接到单片机的P2口,如果想把LED灯点亮,就把单片机相关的 I/O 口赋为低电平 单片机中,用 0 表示低电频,用 1表示高电频
根据提供的引用内容,如果在Ubuntu中使用命令"sudo apt-get install mongodb"安装MongoDB时出现"E: 无法定位软件包 mongodb"的错误,可能是因为默认的软件源中没有包含MongoDB的安装包。为了解决这个问题,可以尝试添加MongoDB的软件源并重新安装。 以下是解决该问题的步骤: 1. 打开终端。 2. 运行以下命令以添加MongoDB的软件源: ```shell sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 9DA31620334BD75D9DCB49F368818C72E52529D4 echo "deb [ arch=amd64 ] https://repo.mongodb.org/apt/ubuntu bionic/mongodb-org/4.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-4.0.list 3. 运行以下命令以更新软件包列表: ```shell sudo apt-get update 4. 现在可以再次运行以下命令以安装MongoDB: ```shell sudo apt-get install mongodb-org 这样就可以成功安装MongoDB了。