windows下安装mongoDB

1)去官网下载2)安装,列出几个需要注意的地方3)添加目录。创建几个文件夹具体如下:数据库路径(data目录)、日志路径(logs目录)和日志文件(mongo.log文件) mongo.conf #数据库路径dbpath=D:\Mongo\data#日志输出文件路径logpath=D:\Mongo\logs\mongo.log#错误日志采用追加模式logappend=true#启用日志文件,默认启用journal=true#这个选项可以过滤掉一些无用的日志信息,若需要调试使用请设置为falsequiet=true#端口号 默认为27017port=27017 4)启动,在安装位置的bin目录下打开cmd窗口,执行命令 mongod --config "D:\Mongo\mongo.conf" 这个是使用自己刚刚在上面配置的mongo.conf文件来启动服务的,注意修改成自己的目录位置。 允许程序通过防火墙5)验证。浏览器输入网址http://127.0.0.1:27017/ 说明启动成功,安装位置下刚创建的data和logs目录都有文件生成。 6)通过windows服务来管理MongoDB的启动和关闭了。依次执行如下命令 mongod --config "D:\Mongo\mongo.conf" --install --serviceName "MongoDB"下面这行命令需要使用管理员权限打开的CMD窗口下执行 net start MongoDB7)配置环境变量。这样可以在任意目录下启动服务。复制路径添加到path里 8)开始使用。不需要登录,任何用户都可以访问操作数据库。看需要自行配置权限。 参考 个人网站:www.panbingwen.cn

October 7, 2019 · 1 min · jiezi

使用Vue技术栈手把手从零开发开发旅游网站一

vue-travel 旅游网站 github:writing_hand: 项目介绍该项目是一款实现在线预订旅游度假产品,休闲旅游预订平台,提供国内游、国外游、周边旅游以及旅游攻略等功能的web应用。 项目演示 演示地址 项目功能: 1:用户模块 用户注册(包含ajax方式校验邮箱格式与是否重复) 用户激活(邮件激活) 用户登录 用户退出2:旅游景点模块 景点列表(带分页) 景点详情3:订单模块 确认订单(填写出发日期,人数,出发地等信息) 生成订单(将订单信息保存到数据库) 订单详情展示 订单支付(易宝支付) 扩展功能: 1:用户7天免登录功能(自动登录功能) 2:我的订单列表展示(带分页) 项目初始化在任一个文件里,打开git bash,或者cmd来打开命令行窗口,输入命令vue create vue-travel接下来,进行选择,上下箭头切换选项,空格选中选项。看到一下的最终结果:Vue CLI v3.9.3┌────────────────────────────┐│ Update available: 3.11.0 │└────────────────────────────┘? Please pick a preset: Manually select features? Check the features needed for your project: Babel, Router, Vuex, CSS Pre-processors, Linter ? Use history mode for router? (Requires proper server setup for index fallback in production) Yes? Pick a CSS pre-processor (PostCSS, Autoprefixer and CSS Modules are supported by default): Stylus? Pick a linter / formatter config: Standard? Pick additional lint features: (Press <space> to select, <a> to toggle all, <i> to invert selection)Lint on save? Where do you prefer placing config for Babel, PostCSS, ESLint, etc.? In package.json? Save this as a preset for future projects? No我这里选择了Babel, Router, Vuex, CSS Pre-processors(Stylus), Linter,根据自己的需求进行选择安装相应的插件。 ...

October 6, 2019 · 3 min · jiezi

MongoDB-自动删除集合中过期的数据TTL索引

简介 TTL (Time To Live, 有生命周期的) 索引是特殊单字段索引,MongoDB可以用来在一定时间后自动从集合中删除文档的特殊索引。 这对于某些类型的数据非常好,例如机器生成的事件数据,日志和会话信息,这些信息只需要在数据库中保留一段时间。 创建 TTL 索引,只需要在使用 db.collection.createIndex() 方法,对字段值为日期或者包含日期的数组设置 expireAfterSeconds 选项即可。 1、如果字段是一个数组,并有多个日期值时,MongoDB使用最低(即最早)日期值来计算失效阈值。2、如果字段不是日期类型也不是一个包含日期的数组类型那么文档就永远不会过期。 3、如果一个文档不包含索引字段,该文档也不会到期。 示例// 创建一个 TTL 索引db.eventlog.createIndex( { "lastModifiedDate": 1 }, { expireAfterSeconds: 600 } )// 修改TTL索引的过期时间db.runCommand( { collMod: "eventlog", index: { keyPattern: { lastModifiedDate: 1 }, expireAfterSeconds: 3600 }})删除操作过期数据的删除工作是由 mongod 后台线程来执行,每60秒进行一次。 对于复制集情况, TTL 后台线程只会删除主节点上过期的数据,从节点过期文档删除则依赖主节点(从节点的 TTL 后台线程是停止状态)。 索引区别对于查询而言,TTL 索引和其他索引没有区别。 TTL索引有哪些限制单字段索引,复合索引时 expireAfterSeconds 会别忽略掉。_id 字段不支持减 TTL 索引。不能在固定集合上创建 TTL 索引,因为固定集合不支持删除操作。不能使用 createIndex() 去修改一个已存在索引的 expireAfterSeconds 。相反,可以使用collMod 命令来修改。否则,改变现有索引的选项的值,你必须删除索引,重新创建。如果一个非TTL单字段索引字段已经存在,您不能再创建一个同字段的TTL索引,因为不能创建相同键不同选项的多个索引。改变非TTL单字段索引成TTL索引,首先你必须先删除索引再现expireAfterSeconds选项创建索引。官网文档: ...

September 30, 2019 · 1 min · jiezi

陪你秋招系列-MongoDB入门

前言: MongoDB最流行的现代数据库,内存级的读写.MongoDB这个来源英文单词“humongous”,homongous这个单词的意思是“巨大的”、“奇大无比的”,从MongoDB单词本身可以看出它的目标是提供海量数据的存储以及管理能力。 什么是MongoDBMongoDB是由MongoDB,Inc。开发的非关系数据库.MongoDB将数据作为文档存储在名为BSON(二进制JSON)的二进制表示中。相关信息存储在一起,以便通过MongoDB查询语言进行快速查询访问。字段因文档而异; 没有必要向系统声明文档结构 - 文档是自我描述的。如果需要将新字段添加到文档中,则可以在不影响集合中的所有其他文档的情况下创建该字段,而无需更新中央系统目录,也不会使系统脱机。(可选)模式验证可用于对每个集合强制执行数据治理控制。 MongoDB的文档数据模型自然地映射到应用程序代码中的对象,使开发人员可以轻松学习和使用。文档使您能够轻松地表示层次关系以存储数组和其他更复杂的结构。 我们为什么要使用mongoDB因为它使他们能够更快地构建应用程序,处理高度多样化的数据类型,并大规模地管理应用程序。 由于MongoDB文档自然地映射到现代的面向对象编程语言,因此简化了开发。使用MongoDB删除了复杂的对象关系映射(ORM)层,该层将代码中的对象转换为关系表。MongoDB灵活的数据模型还意味着数据库模型可以随业务需求而发展。 MongoDB还可以在多个分布式数据中心内和跨多个分布式数据中心进行扩展,从而提供以前无法通过MySQL等关系数据库实现的新级别的可用性和可伸缩性。随着您的部署在数据量和吞吐量方面的增长,MongoDB可以轻松扩展,无需停机,也无需更改应用程序。相比之下,使用MySQL实现扩展通常需要大量的定制工程工作。 1. 存储方式MongoDB它在数据存储的形态上和MySQL之类关系数据库有本质区别,MongoDB存储的基本对象是Document,所以我们把它称为一种文档数据库.而文档的集合是Collection.与SQL的概念类比,Collection对应于Table而Document对应于Row.Document使用一种BSON(Binary JSON)结构来表达,类似JSON的结构. Document 在内部是如何存储的?每个Document被保存在一个Record 中。 Record 相当于 MongoDB 内部分配的一块空间,除了保存 Document 的内容可能还会预留一些填充的额外空间。对于写入后的 Document 如果还会更新,可能导致Document长度增加,就可以利用上额外的填充空间来。若业务对于写入后的Document不会再更新或删除(像监控日志、流水记录等),可以指定无填充的 Record 分配策略,更节省空间。 2. 效率Mongo的存储方式为虚拟内存+持久化存储,Mongo将数据写入内存中,再由虚拟内存管理器将其持久化到硬盘中,因此写操作会比关系型数据库快很多.而且MongoDB 允许在服务端执行脚本,可以用 Javascript 编写某个函数,直接在服务端执行,也可以把函数的定义存储在服务端,下次直接调用即可。 3. 高扩展mongoDB存储的数据不需要具体的格式,它非常容易扩展,在使用Mysql开发时,最初设计table是让人头疼的,为了考虑到以后的扩展,不得不在table的后面预留一些Row,让人恶心是吧.但是mongoDB文档数据库的特点就是想存什么存什么,后期想添加数据也就是一行代码的事,给了我们极大的自由,当然自由肯定也是有后果的,后面会说. 4. 内置 Sharding分片(Sharding) 帮助扩展,加速查询响应的时间,减少宕机的影响 缺点1. 占空间MongoDB 有一个最大的缺点,就是它占用的空间很大,因为它属于典型空间换时间原则的类型。那么它的磁盘空间比普通数据库会浪费一些,而且到目前为止它还没有实现在线压缩功能,在 MongoDB 中频繁的进行数据增删改时,如果记录变了,例如数据大小发生了变化,这时候容易产生一些数据碎片,出现碎片引发的结果,一个是索引会出现性能问题。 2. MongoDB 对数据间的事务关系支持比较弱mongoDB在4.0版本之后也支持ACID,MongoDB将在4.2里推出分片集群的多文档事务支持。随着事务支持的增加,MongoDB功能上更接近于关系型数据库,但是和关系型还是有本质上的区别:关系数据库是基于关系模型的,其固定化的数据模型严格死板,对新一代应用迭代式开发支持不好,对各种数据多变的场景如物联网或社交化都无法支持的很好。MongoDB的JSON模型则具有动态灵活,数据库无须下线就可以进行模式变迁升级,特别适用于敏捷式的开发环境。 MongoDB的应用场景数据不是特别重要(例如通知,推送这些),数据表结构变化较为频繁,数据量特别大,数据的并发性特别高,数据结构比较特别(例如地图的位置坐标),这些情况下用MongoDB,其他情况就还是用 MySQL,这样组合使用就可以达到最大的效率。 从目前阿里云 MongoDB 云数据库上的用户看,MongoDB 的应用已经渗透到各个领域,比如游戏、物流、电商、内容管理、社交、物联网、视频直播等,以下是几个实际的应用案例。 游戏场景,使用MongoDB存储游戏用户信息,用户的装备、积分等直接以内嵌文档的形式存储,方便查询、更新物流场景,使用MongoDB存储订单信息,订单状态在运送过程中会不断更新,以 MongoDB内嵌数组的形式来存储,一次查询就能将订单所有的变更读取出来。社交场景,使用MongoDB存储存储用户信息,以及用户发表的朋友圈信息,通过地理位置索引实现附近的人、地点等功能物联网场景,使用MongoDB存储所有接入的智能设备信息,以及设备汇报的日志信息,并对这些信息进行多维度的分析视频直播,使用 MongoDB 存储用户信息、礼物信息等......

August 20, 2019 · 1 min · jiezi

mongodb用户创建mongoose数据库连接

一、用户创建1、创建超级管理员 a.首先开启Mongo服务,然后切换admin数据库 use admin;b.创建 db.createUser({user:"root",pwd:"ceshi123",roles:[{role:"root",db:"admin"}]});c.修改mongodb.conf配置文件添加代码: auth=true如果有以下代码,可修改 noauth=true // 修改为auth=true保存mongodb.conf文件 d.创建超级管理员之后,重新启动mongodb服务 use admin;db.shutdownServer(); //关闭服务exit;下一步要进入到mongodb安装目录下的bin如果是windows系统,可以直接去mongodb/bin目录下打开命令shell,具体操作方法:按住shift+鼠标左键可快速打开shell,输入启动命令: ./mongod -f /usr/local/mongodb/conf/mongodb.conf./mongod如果是linux服务器,可以直接到mongodb/bin目录下 cd /usr/local/mongodb/bin // 小编mongodb的安装目录是/usr/local./mongod -f /usr/local/mongodb/conf/mongodb.conf./mongod经过以上操作,现在创建了操作mongodb的超级管理员;下边我们针对我们要使用的数据库进行创建管理员 2、创建指定数据库管理员a.进入到指定数据库,这里采用testDB mongo //启动mongodbuse admin;db.auth("root","ceshi123");如果shell出现1则为进入mongodb成功,为0进入失败。进入成功后,执行以下: use testDB;db.createUser({user:"admin",pwd:"test123",roles:[{role:"root",db:"admin"}]}); // 这里role的权限一定是root,否则node-express-mongoose会读不到数据创建后可以查看: show users;如果输出以上输入信息,则添加成功。下面我们来看下 mongodb 一共有哪些权限: 1. 数据库用户角色:read、readWrite; 2. 数据库管理角色:dbAdmin、dbOwner、userAdmin; 3. 集群管理角色:clusterAdmin、clusterManager、clusterMonitor、hostManager;4. 备份恢复角色:backup、restore;5. 所有数据库角色:readAnyDatabase、readWriteAnyDatabase、userAdminAnyDatabase、dbAdminAnyDatabase6. 超级用户角色:root // 这里还有几个角色间接或直接提供了系统超级用户的访问(dbOwner 、userAdmin、userAdminAnyDatabase)7. 内部角色:__systemread:允许用户读取指定数据库readWrite:允许用户读写指定数据库dbAdmin:允许用户在指定数据库中执行管理函数,如索引创建、删除,查看统计或访问system.profileuserAdmin:允许用户向system.users集合写入,可以找指定数据库里创建、删除和管理用户clusterAdmin:只在admin数据库中可用,赋予用户所有分片和复制集相关函数的管理权限。readAnyDatabase:只在admin数据库中可用,赋予用户所有数据库的读权限readWriteAnyDatabase:只在admin数据库中可用,赋予用户所有数据库的读写权限userAdminAnyDatabase:只在admin数据库中可用,赋予用户所有数据库的userAdmin权限dbAdminAnyDatabase:只在admin数据库中可用,赋予用户所有数据库的dbAdmin权限。root:只在admin数据库中可用。超级账号,超级权限 二、mongoose连接const config = { DB_URL: 'mongodb://admin:test123@127.0.0.1:27017/testDB'};mongoose.connect(config.DB_URL);解释说明:'mongodb://admin:test123@127.0.0.1:27017/testDB'// admin 管理员用户名// test123 管理员密码// testDB 要连接的数据库 参考链接:1、https://blog.csdn.net/zgrbsbf...2、https://blog.csdn.net/szu_lzz...3、https://blog.csdn.net/leihui_...4、https://www.cnblogs.com/leino...5、http://yijiebuyi.com/blog/f9e...

August 17, 2019 · 1 min · jiezi

MongoDB用户和角色解释系列下

MongoDB用户和角色解析系列(上)是否已经阅读呢?如果没阅读可以参考这篇文章《MongoDB用户和角色解析系列(上)》。 在前半部分我已经讲了很多理论。但是,我知道,如果这是您第一次处理MongoDB角色和用户问题,可能非常令人困惑。因此,让我们一步一步地通过一个示例来了解这个过程,比如当您新增一个新的3个节点副本集,并将安全选项标志设置为true,之后运行该副本集时,这个过程您将怎样操作。同时,我们还将看到使用诸如Studio 3T(用于MongoDB的IDE)这样的可视化界面管理用户和角色是多么容易。 修改mongod.conf配置文件 security: authorization: enabled keyFile: /var/lib/rs.key replication: replSetName: 'studio3trs' 在三台服务器上面运行mongod服务(Ubuntu 16.04) $ systemctl start mongod.service 连接其中一台服务器 vagrant@primary:~$ mongoMongoDB shell version v3.4.16connecting to: mongodb://127.0.0.1:27017MongoDB server version: 3.4.16Welcome to the MongoDB shell.For interactive help, type "help". 更全面的文档,请参阅: http://docs.mongodb.org/ 有疑问? 尝试互助—— http://groups.google.com/grou... 首先初始化副本集: rs.initiate(){"info2" : "no configuration specified. Using a default configuration for the set","me" : "192.168.60.10:27017","ok" : 1}studio3trs:SECONDARY>studio3trs:PRIMARY>创建第一个用户 use adminswitched to db admindb.createUser({ user : 'juan',pwd : 'juanpwd',roles : [ { role : 'userAdminAnyDatabase', db : 'admin' } ]})Successfully added user: {"user" : "juan","roles" : [{"role" : "userAdminAnyDatabase","db" : "admin"}]}通过Studio 3T中的point and click向导,添加第一个用户很容易完成。如前所述,第一步是选择要创建用户的数据库,然后单击“Users”按钮,填写所需的数据,并将角色授予它。延伸阅读:阅读我们《在Studio 3T中创建新用户管理》的深入指南。参考网址:https://studio3t.com/knowledg... ...

August 7, 2019 · 3 min · jiezi

基于koa2的前后端管理系统

koa2koa2框架,mongodb作为数据库,Es6/7语法编写,babel编译ES语法。 增加ts语法支持,进行ing 前后端分离,后台管理系统, Koa后端 系统目前包含 文章发布管理系统、标签系统、评论系统、用户系统,四大模块 技术栈使用koa+mongoose 开发; 使用koa2.0作为开发框架mongoose作为数据库,保存数据使用jwt进行token的生成和校验通过Es6语法进行项目编写文件结构采用MC拆分babel-register编译Es6/7/8esLint语法规则server下为目录结构:.|——server|  |—— config 全局配置| |—— constant 常量|  |   |—— index.js             暴露全部常量| | └── user.js 用户常量|  |—— controller 对应路由的逻辑处理|  |   |—— article.js             文章 控制器 接口| | └── comment.js 评论 控制器 接口| | └── tag.js 标签 控制器 接口| | └── user.js 用户 控制器 接口| |—— middleware 路由中间件|  |—— model mongoose数据库模型| |   |—— ArticleModel.js        文章模型| |   |—— TagModel.js            标签模型| |   └── UserModel.js           用户模型| | └── CommentModel.js 评论模型| |—— mongoose 数据库方法暴露| |—— public 静态资源目录| |—— router 路由文件| |   |—— index.js               路由| |   |—— api.js                 api路由| |   └── user.js                user路由| |—— utils                     公共方法| |—— app.js app入口文件调试运行$ yarn install <!-- 需要开启管理权限设置 -->$ mongod //开启mongoDB$ npm run dev //本地测试服务API接口后端 接口文档 ...

July 26, 2019 · 1 min · jiezi

如何搭建-MongoDB-分片无主从

首先,在运行分片的服务器上将 MongoDB 准备好。我们需要规划: 一台配置服务器,用来保存分片信息以及协调数据存储;若干台分片服务器,用来保存数据本身;一个 broker 服务器,用来执行分片操作,同时也是客户端的访问入口。1. 启动分片服务器此时我们先将每个分片服务器独立运行起来,同时加上 --shardsvr 参数表名它是一台用来存储分片数据的实例。 ./mongod --shardsvr --port 10001 --dbpath /data/shard/s1 --fork --logpath /data/shard/log/s1.log --directoryperdb./mongod --shardsvr --port 10002 --dbpath /data/shard/s2 --fork --logpath /data/shard/log/s2.log --directoryperdb./mongod --shardsvr --port 10003 --dbpath /data/shard/s3 --fork --logpath /data/shard/log/s3.log --directoryperdb./mongod --shardsvr --port 10004 --dbpath /data/shard/s4 --fork --logpath /data/shard/log/s4.log --directoryperdb2. 启动配置服务器然后我们将配置服务器开启。 ./mongod --configsvr --port 20000 --dbpath /data/shard/conf --fork --logpath /data/shard/log/conf.log --directoryperdb3. 启动 broker 服务器继续将 broker 开启,此时需要在 --configdb 参数中将其指向配置服务器。 ./mongos --port 30000 --configdb 192.168.39.241:20000 --fork --logpath /data/shard/log/route.log至此,所有的服务器都已经开启,但是它们之间还是处于相互独立的状态。我们需要用客户端登入管理服务,将分片服务器加入进来。 ...

July 26, 2019 · 1 min · jiezi

手撕面试官系列四-MongoDBRedis-面试专题

MongoDB(面试题+答案领取方式见个人主页) 你说的 NoSQL 数据库是什么意思?NoSQL 与 RDBMS 直接有什么区别?为什么要使用和不使用NoSQL 数据库?说一说 NoSQL 数据库的几个优点?NoSQL 数据库有哪些类型?MySQL 与 MongoDB 之间最基本的差别是什么?你怎么比较 MongoDB、CouchDB 及 CouchBase?MongoDB 成为最好 NoSQL 数据库的原因是什么?32 位系统上有什么细微差别?journal 回放在条目(entry)不完整时(比如恰巧有一个中途故障了)会遇到问题吗?分析器在 MongoDB 中的作用是什么?名字空间(namespace)是什么?如果用户移除对象的属性,该属性是否从存储层中删除?能否使用日志特征进行安全备份?允许空值 null 吗?更新操作立刻 fsync 到磁盘?如何执行事务/加锁?为什么我的数据文件如此庞大?启用备份故障恢复需要多久?什么是 master 或 primary?什么是 secondary 或 slave?我必须调用 getLastError 来确保写操作生效了么?我应该启动一个集群分片(sharded)还是一个非集群分片的 MongoDB 环境?分片(sharding)和复制(replication)是怎样工作的?数据在什么时候才会扩展到多个分片(shard)里?当我试图更新一个正在被迁移的块(chunk)上的文档时会发生什么?如果在一个分片(shard)停止或者很慢的时候,我发起一个查询会怎样?我可以把 moveChunk 目录里的旧文件删除吗?我怎么查看 Mongo 正在使用的链接?如果块移动操作(moveChunk)失败了,我需要手动清除部分转移的文档吗?如果我在使用复制技术(replication),可以一部分使用日志(journaling)而其他部分则不使用吗?当更新一个正在被迁移的块(Chunk)上的文档时会发生什么?MongoDB 在 A:{B,C}上建立索引,查询 A:{B,C}和 A:{C,B}都会使用索引吗?如果一个分片(Shard)停止或很慢的时候,发起一个查询会怎样?MongoDB 支持存储过程吗?如果支持的话,怎么用?如何理解 MongoDB 中的 GridFS 机制,MongoDB 为何使用 GridFS 来存储文件?Redis (一) redis 和 和 memcached 什么区别?为什么高并发下有时单线程的 redis 比多线程的memcached 效率要高?redis 主从复制如何实现的?redis 的集群模式如何实现?redis 的 的 key 是如何寻址的?使用 redis 如何设计分布式锁?说一下实现思路?使用 zk 可以吗?如何实现?这两种有什么区别?知道 redis 的持久化吗?底层如何实现的?有什么优点缺点?redis 过期策略都有哪些?LRU 算法知道吗?写一下 java 代码实现?缓存穿透、缓存击穿、缓存雪崩解决方案?在选择缓存时,什么时候选择 redis ,什么时候选择缓存与数据库不一致怎么办主从数据库不一致如何解决Redis 常见的性能问题和解决方案Redis 的数据淘汰策略有哪些Redis 当中有哪些数据结构假如 Redis 里面有 1 亿个 key ,其中有 10w 个 个 key 是以某个固定的已知的前缀开头的,如果将它们全部找出来?使用 Redis 做过异步队列吗,是如何实现的Redis 如何实现延时队列Redis (二) ...

July 16, 2019 · 1 min · jiezi

mongoDB安装环境配置打开

环境系统: windows 101.mongoDB下载地址https://www.mongodb.com/downl...选择Server+Community版本,下载/安装/完成 2.打开方式复制bin文件路径,win+R打开cmd,

July 14, 2019 · 1 min · jiezi

mongodb-和-golang-搭伙采坑

最近在边学边录一些视频教程,放在B站。( https://www.bilibili.com/vide... ) 背景起初是打算用 golang + mongodb 来做。网上也查了写资料,说 mongodb 的 model 和 golang 的 struct 是很般配的。然后作为前端出生的我,本来使用过 nodejs 和 mongodb ,所以也就想着用 golang + mongodb 这样自己会更熟悉。 问题前期的准备什么都没有问题,到后边发现:怎么关联查询啊?然后网上各种关键词搜索,各种看别人代码社区提问什么的。最后,耐着性子,找到了官方文档: https://docs.mongodb.com/manu... 可以看到,golang 的 driver 并不支持 DBRefs,扎心哦。 结论看样子,强类型的语言还是适合 mysql 这种关系型数据库。哎,不说了,换 mysql 继续码了~.~ (个人理解,才疏学浅,有误忘指导...

July 14, 2019 · 1 min · jiezi

MongoDB副本集搭建

MongoDBMongoDB是现在最为流行的NoSQL数据库之一。在大数据时代,传统的关系型数据库遇到了高并发读写、海量数据高效存储、高可扩展性和高可用性这些难题。以MySQL为例,在数据量很大需要分表分库的时候,它本身不提供分片能力,需要自己另建服务。 而NoSQL就是为了解决这些问题而诞生了的。注: NoSQL(NoSQL = Not Only SQL ),意即"不仅仅是SQL"。NoSQL有如下优势: 大数据量,可以通过廉价服务器存储大量的数据,轻松摆脱传统mysql单表存储量级限制。高性能,NoSQL通过简单的key-value方式获取数据,非常快速。还有NoSQL的Cache是记录级的,是一种细粒度的Cache,所以NoSQL在这个层面上来说就要性能高很多。灵活的数据模型,NoSQL无需事先为要存储的数据建立字段,随时可以存储自定义的数据格式。而在关系数据库里,增删字段是一件非常麻烦的事情。如果是非常大数据量的表,增加字段简直就是一个噩梦。高可用,NoSQL在不太影响性能的情况,就可以方便的实现高可用的架构。比如mongodb通过mongos、mongo分片就可以快速配置出高可用配置。单机版MongoDB这种配置只适合简易开发时使用,生产使用不行,因为不是高可用的。这里我使用docker快速启动MongoDB服务,MongoDB版本为3.6。 docker-compose.ymlversion: '3'services: mongo1: image: mongo:3.6 environment: - MONGO_INITDB_ROOT_USERNAME=test - MONGO_INITDB_ROOT_PASSWORD=IIm7A5C5GqRWqnLg network_mode: "host" volumes: - ./mongo_data:/data/db - ./mongod.conf:/etc/mongo/mongod.conf - ./log:/var/log/mongodb command: ["--config", "/etc/mongo/mongod.conf"]mongod.confsystemLog: destination: file path: /var/log/mongodb/mongo.log logAppend: falsestorage: dbPath: /data/db indexBuildRetry: true journal: enabled: truenet: port: 40031 bindIp: 0.0.0.0 maxIncomingConnections: 65536docker-compose.yml中network_mode: "host"是让容器直接使用宿主机网络,配置文件中net下port是指定MongoDB服务监听的端口,storage下dbPath指定数据存储目录,开启journal是因为journal文件用于数据库异常退出时恢复数据(默认开启)。windows上会遇到MongoDB无法启动的问题(WiredTiger提示Operation not permitted),解决方法是自己创建顶级的数据卷 version: '3'services: mongo1: image: mongo:3.6 environment: - MONGO_INITDB_ROOT_USERNAME=test - MONGO_INITDB_ROOT_PASSWORD=IIm7A5C5GqRWqnLg network_mode: "host" volumes: - mongo_data:/data/db - ./mongod.conf:/etc/mongo/mongod.conf - ./log:/var/log/mongodb command: ["--config", "/etc/mongo/mongod.conf"]volumes: mongo_data:副本集高可用的一个做法就是做主从,MongoDB给的方案就是副本集(对于分布式存储有分片集合,之后会写)。MongoDB副本集中主服务器负责整个副本集的读写,副本集定期同步数据备份,一但主节点挂掉,副本节点就会选举一个新的主服务器,这一切对于应用服务器不需要关心。这样的机制提高了数据的可用性,并可以保证数据的安全性。 ...

July 13, 2019 · 1 min · jiezi

mongodb-索引详解二

1. 单字段索引MongoDB为文档集合中的任何字段提供完整的索引支持 。默认情况下,所有集合在_id字段上都有索引,应用程序和用户可以添加其他索引以支持重要的查询和操作。 本文档描述了单个字段的升序/降序索引。 1.1 在单个字段上创建升序索引如:records集合,它包含文档如下: { "_id": ObjectId("570c04a4ad233577f97dc459"), "score": 1034, "location": { state: "NY", city: "New York" }}在records集合的score字段上创建升序索引: db.records.createIndex( { score: 1 } )索引规范中字段的值描述了该字段的索引类型。例如,值为1为按对items升序排序的索引。值为-1指定对item降序排序的索引。有关其他索引类型,请参阅 index types。 如上索引支持在score字段上选择查询,例如: db.records.find( { score: 2 } )db.records.find( { score: { $gt: 10 } } )1.2 嵌入式字段上创建索引在嵌入文档中的字段上创建索引,就像文档中的索引顶级字段一样。嵌入字段上的索引与 i索引在嵌入式文档上不同,嵌入文档中的索引包括索引中嵌入文档的最大内容,直到最大的index size。相反,索引在嵌入式字段上允许使用“点符号”来表示嵌入式文档。 如:records集合,它包含文档如下: { "_id": ObjectId("570c04a4ad233577f97dc459"), "score": 1034, "location": { state: "NY", city: "New York" }}以下操作在location.state 字段上创建索引: db.records.createIndex( { "location.state": 1 } ) 创建的索引将支持在字段上选择的查询 location.state,例如: ...

July 11, 2019 · 2 min · jiezi

MongoDB-索引详解一

默认_id 索引创建索引索引类型索引属性索引使用索引及排序规则覆盖查询交叉索引限制其他注意事项索引支持MongoDB中查询的高效执行。如果没有索引,MongoDB必须执行集合扫描,即扫描集合中的每个文档,以选择与查询语句匹配的文档。如果查询存在适当的索引,MongoDB可以使用索引来限制它必须检查的文档数。 索引是特殊的数据结构[1],它以易于遍历的形式存储部分集合数据集。索引存储特定字段或字段集的值,按字段值排序。索引条目的排序支持高效的等值匹配和基于范围的查询操作。此外,MongoDB可以使用索引中的顺序返回排序后结果。 下图说明了查询如何使用索引选择和排序相匹配文档: 从根本上说,MongoDB中的索引与其他数据库系统中的索引类似。MongoDB在集合级别定义索引,并支持MongoDB集合中文档的任何字段或子字段定义索引。 默认_id 索引MongoDB 在创建集合期间在_id字段上创建了唯一索引。该索引可防止客户端插入两个_id字段值相同的文档。_id字段的索引不能删除。 注意在分片群集中,如果不将该_id字段用作分片键,则应用程序必须确保_id字段值的唯一性以防止出错。通常使用标准的自动生成的ObjectId来完成。 创建索引===Mongo shell 使用db.collection.createIndex() db.collection.createIndex( <key and index type specification>, <options> ) 例如创建单个键上的降序索引 db.collection.createIndex( { name: -1 } ) db.collection.createIndex方法仅用于创建索引,且相同定义的索引不存在 [1] MongoDB 索引使用B-tree数据结构 索引类型MongoDB提供了许多不同的索引类型来支持特定类型的数据和查询。单字段索引:除MongoDB定义的_id索引外,MongoDB还支持在文档的单个字段上创建用户定义的升序/降序索引。 对于单字段索引和排序操作,索引键的排序顺序(即升序或降序)无关紧要,因为MongoDB可以在任一方向上遍历索引。 有关单字段索引的详细信息,请参阅单字段索引和单字段索引排序。 ==复合索引 MongoDB支持用户在多个字段上定义索引,即 复合索引。 复合索引中字段的顺序很重要。例如,如果复合索引为{ userid: 1, score: -1 },则索引首先以userid字段进行排序,然后在每个userid 值以score字段进行排序。 对于复合索引和排序操作,索引键的排序顺序(即升序或降序)可以确定索引是否可以支持排序操作。有关索引顺序对复合索引中结果的影响的详细信息,请参阅 排序顺序。 有关复合索引的详细信息,请参阅复合索引和多个字段排序。 ==多键索引 MongoDB使用多键索引来索引存储在数组中的内容。如果索引字段包含数组值,MongoDB会为数组的每个元素创建单独的索引条目。这些多键索引允许查询通过匹配数组中的元素来获取包含数组的文档。如果索引字段包含数组值,MongoDB会自动决定是否需要创建多键索引; 不需要显式指定多键类型。 多键索引和多键索引边界 获取有关多键索引的详细信息。 ==地理空间索引 为了支持对地理空间坐标数据的高效查询,MongoDB提供了两个特殊索引:返回结果时使用平面几何的2d索引和使用球面几何的2dphere索引。 有关地理空间索引的更多介绍,请参阅2d Index Internals。 == 文本索引 MongoDB提供了一种text索引类型,支持在集合中搜索字符串内容。这些文本索引不存储特定于语言的停用词(例如“the”,“a”,“or”),并且集合中的词干均仅存储词根。有关文本索引和搜索的详细信息,请参阅文本索引。 == 哈希索引 为了支持基于哈希的分片,MongoDB提供了哈希索引类型,索引字段值的哈希值。这些索引在其范围内具有更随机的值分布,但仅 支持等值匹配且不支持范围查询。 ...

July 11, 2019 · 1 min · jiezi

MongoDB-sharding-集合不分片性能更高

最近云上用户用户遇到一个 sharding 集群性能问题的疑惑,比较有代表性,简单分享一下 测试配置mongos x 2、shard x 3测试1:集合不开启分片,批量 insert 导入数据,每个 batch 100 个文档测试2:集合开启分片,随机生成 shardKey,chunk 已提前 split 好,能确保写入均分到3个shard测试结果测试1:单个 shard cpu 跑满,insert qps 在 6w 左右测试2:3个 shard cpu 跑满,insert qps 在 7w 左右(平均每个分片2.4w左右)注:两个测试里,mongos 都不是瓶颈,能力足够 从测试结果看,每个shard都承担 1/3 的负载,的确达到横向扩张的目的,但为啥分片之后,单个shard的能力就下降了呢?如果是这样,sharding的扩展能力如何体现? 结果分析这里核心的问题在于 batch insert 在 mongos 和 mongod 上处理行为的差别 导入数据时,一次 insert 一条数据,和一次 insert 100 条数据,性能差距是很大的;首先减少了client、server 端之间的网络交互;同时 server 可以将 batch insert 放到一个事务里,降低开销;mongos 在收到 batch insert 时,因为一个 batch 里的数据需要根据 shardKey 分布到不同的shard,所以一个 batch 实际上需要被拆开的;这里 mongos 也做了优化,会尽量将连续的分布在一个shard上的文档做 batch 发到后端 shard。在集合不开启分片的情况,mongos 收到的 batch 肯定是转发给 primary shard,所以转发过去还是一整个 batch 操作; 而在集合开启分片的情况下,因为用户测试时,shardKey 是随机生成的,基本上整个 batch 被打散成单条操作,逐个往后端 shard 上发送,请求到后端 shard 基本已经完全没有合并了。所以在上述测试中,不分片的单个 shard 6w qps、与分片后每个 shard 2.4w qps,实际上就是请求是否 batch 执行的差别。 ...

July 11, 2019 · 1 min · jiezi

pymongo-正则匹配

啥都不说了直接上代码:mongo_sq=[{'$match': {'$or': [{'company_name':{'$regex':".*科技.*"}}, {'company_name': {'$regex':".*医疗美容.*"}},{'company_name':{'$regex':".*整形.*"}}, ]}}]cursor.aggregate(mongo_sql).batch_size(1)也可以用re模块正则匹配

July 10, 2019 · 1 min · jiezi

MongoDB学习记录

MongoDB是分布式数据库。和mysql概念区别: 基本操作:(1)显示数据库: show dbs(2) 连接数据库: use local(3)显示当前数据库 db(4)创建数据库(当没有该数据库名时,自动创建) use stray(5)插入数据 db.stray.insert({"name": "stray"})(6) 删除数据库 use straydb.dropDatabase()(7) 创建集合 db.createCollection("stu")创建固定集合 mycol,整个集合空间大小 6142800 KB, 文档最大个数为 10000 个。db.createCollection("mycol", { capped : true, autoIndexId : true, size : 6142800, max : 10000 } ) (8)查看集合 show collections(9)删除集合 db.stu.drop()(10)插入文档 db.COLLECTION_NAME.insert(document)db.stu.insert({title: 'lala', name: 'zhangsan',})(11)更新文档 db.collection.update( <query>, <update>, { upsert: <boolean>, multi: <boolean>, writeConcern: <document> })db.col.update({'title':'MongoDB 教程'},{$set:{'title':'MongoDB'}},{multi:true})(12)删除文档 db.collection.remove( <query>, { justOne: <boolean>, writeConcern: <document> })(13)查询文档 db.collection.find(query, projection)OR条件db.col.find({$or:[{"by":"菜鸟教程"},{"title": "MongoDB 教程"}]}).pretty()db.col.find({"likes": {$gt:50}, $or: [{"by": "菜鸟教程"},{"title": "MongoDB 教程"}]}).pretty()(14) 条件操作符 ...

July 9, 2019 · 1 min · jiezi

SpringBoot-MongoDB-分片集群的集成

简介本文主要讲在 mongodb 在分片集群的情况下,springboot如何进行集成。 默认读者熟悉 maven、springboot、mongodb Springboot+Mongodb集成引入依赖 <!-- mongodb --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-mongodb</artifactId> </dependency>定义配置文件自定义配置文件,多数据源的 topinfo: mongodb: base: # mongos的地址和端口 uri: mongodb://192.168.90.225:20000,192.168.90.226:20000,192.168.90.227:20000 database: topinfo_base warn: # mongos的地址和端口 uri: mongodb://192.168.90.225:20000,192.168.90.226:20000,192.168.90.227:20000 database: topinfo_warn 配置父类package com.topinfo.ci.dataex.config;import java.util.List;import org.springframework.boot.context.properties.ConfigurationProperties;import org.springframework.stereotype.Component;/** * @Description: mongo配置类 - 父类 * @Author:杨攀 * @Since:2019年7月8日上午11:19:39 */public class MongoConfiguration { private String uri; private String database; public String getUri() { return uri; } public void setUri(String uri) { this.uri = uri; } public String getDatabase() { return database; } public void setDatabase(String database) { this.database = database; }}基础配置类package com.topinfo.ci.dataex.config;import java.util.List;import org.springframework.boot.context.properties.ConfigurationProperties;import org.springframework.stereotype.Component;/** * @Description: mongo配置类 * @Author:杨攀 * @Since:2019年7月8日上午11:19:39 */@ConfigurationProperties(prefix = "topinfo.mongodb.base")@Componentpublic class MongoBaseConfiguration extends MongoConfiguration { }预警配置类package com.topinfo.ci.dataex.config;import org.springframework.boot.context.properties.ConfigurationProperties;import org.springframework.stereotype.Component;/** * @Description: mongo配置类 * @Author:杨攀 * @Since:2019年7月8日上午11:19:39 */@ConfigurationProperties(prefix = "topinfo.mongodb.warn")@Componentpublic class MongoWarnConfiguration extends MongoConfiguration { }mongo配置类package com.topinfo.ci.dataex.config;import java.util.ArrayList;import java.util.List;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.data.mongodb.MongoDbFactory;import org.springframework.data.mongodb.core.MongoTemplate;import org.springframework.data.mongodb.core.SimpleMongoDbFactory;import com.mongodb.MongoClient;import com.mongodb.MongoClientOptions;import com.mongodb.MongoClientURI;import com.mongodb.MongoCredential;import com.mongodb.ServerAddress;/** * @Description: mongoConfig * @Author:杨攀 * @Since:2019年7月8日上午11:22:57 */@Configurationpublic class MongoConfig { @Autowired private MongoBaseConfiguration baseConfiguration; @Autowired private MongoWarnConfiguration warnConfiguration; /** *@Description: MongoDbFactory *@Author:杨攀 *@Since: 2019年7月8日下午4:02:33 *@param config *@return *@throws Exception */ @Bean public MongoDbFactory mongoDbFactory(MongoConfiguration config) throws Exception { MongoClientURI uri = new MongoClientURI(config.getUri()); MongoClient mongoClient = new MongoClient(uri); MongoDbFactory dbFactory = new SimpleMongoDbFactory(mongoClient, config.getDatabase()); return dbFactory; } @Bean(name="baseMongoTemplate") public MongoTemplate baseMongoTemplate() throws Exception { return new MongoTemplate(mongoDbFactory(baseConfiguration)); } @Bean(name="warnMongoTemplate") public MongoTemplate warnMongoTemplate() throws Exception { return new MongoTemplate(mongoDbFactory(warnConfiguration)); }}dao层的使用package com.topinfo.ci.dataex.dao.impl;import java.util.List;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.beans.factory.annotation.Qualifier;import org.springframework.data.mongodb.core.MongoTemplate;import org.springframework.stereotype.Component;import com.alibaba.fastjson.JSON;import com.topinfo.ci.dataex.bean.UserBean;import com.topinfo.ci.dataex.dao.TestDao;@Componentpublic class TestDaoImpl implements TestDao { @Autowired @Qualifier("baseMongoTemplate") private MongoTemplate mongoTemplate; @Override public void saveUser(UserBean user) { String userJson = JSON.toJSONString(user); mongoTemplate.save(userJson, "tx"); } @Override public void saveUserList(List<UserBean> userList) { // TODO Auto-generated method stub } @Override public void updateUser(UserBean user) { // TODO Auto-generated method stub } @Override public void deleteUserById(Long id) { // TODO Auto-generated method stub }}以上是 集成的主要配置类, server层直接调用到即可实现。 千万别谢我!!!O(∩_∩)O哈哈~ ...

July 8, 2019 · 2 min · jiezi

MongoDB集群分片安装部署手册

1 简介1.1 简介MongoDB 是由C++语言编写的,是一个基于分布式文件存储的开源数据库系统。在高负载的情况下,添加更多的节点,可以保证服务器性能。MongoDB 旨在为WEB应用提供可扩展的高性能数据存储解决方案。MongoDB 将数据存储为一个文档,数据结构由键值(key=>value)对组成。MongoDB 文档类似于 JSON 对象。字段值可以包含其他文档,数组及文档数组。 官网: https://www.mongodb.com 1.2 参考资料https://www.jianshu.com/p/e18...https://www.cnblogs.com/tians...https://blog.csdn.net/weixin_...https://blog.csdn.net/eagle89... 2 单机部署2.1 单机部署环境操作系统:CentOS7 +三台服务器:192.168.90.225 安装包: mongodb-linux-x86_64-4.0.10.tgz 2.2 安装MongoDB单机测试2.2.1 目录规划mkdir /usr/local/tmp #临时目录,用于上传压缩包mkdir /topinfo #mongodb的安装目录2.2.2 上传MongoDB安装包上传安装包: cd /usr/local/tmp/ # 跳转到临时目录rz #通过rz 上传mongodb-linux-x86_64-4.0.10.tgz2.2.3 安装1、解压 tar -zxvf mongodb-linux-x86_64-4.0.10.tgz # 解压mv mongodb-linux-x86_64-4.0.10 mongodb #重命名mv /usr/local/tmp/mongodb /topinfo # 将解压包拷贝到指定目录2、MongoDB 的可执行文件位于 bin 目录下,所以可以将其添加到 PATH 路径中: vim /etc/profile #加入以下内容export MONGODB_HOME=/topinfo/mongodbexport PATH=$MONGODB_HOME/bin:$PATH #保持退出后,执行以下命令,立即生效source /etc/profile3、环境变量验证使用命令mongod -v输出信息版本信息验证环境变量是否配置成功 mongod -v2.2.4 创建数据库目录1、创建数据库的目录MongoDB的默认数据存储在跟目录data/db目录下,但是这个目录在安装过程不会自动创建,所以你需要手动创建data目录,并在data目录。注意: data/db 是 MongoDB 默认的启动的数据库路径。如果你的数据库目录不是/data/db,可以通过 --dbpath 来指定。(mongod --dbpath /usr/local/mongo/data) ...

July 8, 2019 · 4 min · jiezi

Mongodb的实践二初识

Mongodb 系列教程Mongodb的实践一:安装Mongodb的实践二:初识Mongodb的实践三:Mongodb的实践四:Mongodb的实践五:Mongodb的实践六:Mongodb的实践七:Mongodb的实践八:术语db->数据库collection->集合document->文档index->索引cluster->集群shard->分片 数据类型MongoDB文档存储是使用BSON类型类型的官方文档:https://docs.mongodb.com/manu...BSON官网:http://bsonspec.org/ TypeNumberAliasNotesDouble1“double” String2“string” Object3“object” Array4“array” Binary data5“binData”Undefined6“undefined”Deprecated.ObjectId7“objectId” Boolean8“bool” Date9“date” Null10“null” Regular Expression11“regex” DBPointer12“dbPointer”Deprecated.JavaScript13“javascript” Symbol14“symbol”Deprecated.JavaScript (with scope)15“javascriptWithScope” 32-bit integer16“int” Timestamp17“timestamp” 64-bit integer18“long” Decimal12819“decimal”New in version 3.4.Min key-1“minKey” Max key127“maxKey” 常用数据类型后续实例说明 内建的角色组https://docs.mongodb.com/manu... Database User Roles 这个是针对非系统数据库和部分系统表的角色组Database Administration Roles 可以操作所有数据库Cluster Administration Roles 管理员族 针对整个系统进行管理Backup and Restoration Roles 备份还原角色组All-Database Roles 角色里面有一些跟超管差不多了级别了,针对所有数据库的Superuser Roles 超级管理员 不用多说了Internal Role 内部系统角色MongoDB 通过角色基本权限控制授予(用户)数据和命令的使用权,并且提供给内置角色数据系统一般需要的不同层次的权限。另外,你也可以创建用户定义角色。 角色授予对定义的资源执行一组操作的权限。给定的角色应用于定义它的数据库,并且可以授予对粒度集合级别的访问权限 MongoDB的每个内置角色在数据库级别为角色数据库中的所有非系统集合定义访问权限,在收集级别为所有系统集合定义访问权限MongoDB在每个数据库上提供内置的数据库用户和数据库管理角色。MongoDB仅在管理数据库上提供所有其他内置角色。 本节描述每个内置角色的权限。您还可以随时查看内置角色的权限,方法是发出rolesinfo命令,并将showprivileges和showbuiltinroles字段都设置为true。 Database User Roles (数据库用户角色)每个数据库都包含以下客户角色: read(只读)提供读取所有非系统集合和以下系统集合上的数据的能力:system.indexes, system.js, and system.namespaces集合 ...

July 8, 2019 · 4 min · jiezi

Mongodb的实践一初始包括安装

Mongodb 系列教程Mongodb的实践一:初始(包括安装)Mongodb的实践二:Mongodb的实践三Mongodb的实践四Mongodb的实践五Mongodb的实践六Mongodb的实践七Mongodb的实践八序言Mongodb 教程其实早在一年前就一直想写一写,因为工作比较忙,自己也没有真的大量用于真实项目里的实践,一直耽搁了 简介MongoDB是一个基于分布式文件存储的数据库. 官网https://www.mongodb.com/downl... 单机安装三种安装支持 1. window安装可参考mongodb单机 在window下安装测试 2. 类unix系下安装wget https://fastdl.mongodb.org/osx/mongodb-osx-ssl-x86_64-4.0.6.tgztar zxvf mongodb-osx-ssl-x86_64-4.0.6.tgzcd mongodb-osx-ssl-x86_64-4.0.6/binmongod --config /xx/xx.config --fork3. docker安装# -p local_port:docker_portdocker pull mongo:4.1.5docker run -d --name mongodb-4.1.5 -e MONGO_INITDB_ROOT_USERNAME=root -e MONGO_INITDB_ROOT_PASSWORD=123456 -v /data/mongodb/docker/4.1.5:/mongodb -v /data/mongodb/docker/4.1.5/conf.d:/etc/mongo -p 27077:27017 mongo:4.1.5 --config /etc/mongo/mongod.conf --authdocker 目录结构├── conf.d│   └── mongod.conf├── data└── logsmongod.conf/data/mongodb/docker/4.1.5/conf.d下的mongo配置文件 # mongod.conf# for documentation of all options, see:# http://docs.mongodb.org/manual/reference/configuration-options/# Where and how to store data.storage: dbPath: /mongodb/data journal: enabled: true# engine:# mmapv1:# wiredTiger:# where to write logging data.systemLog: destination: file logAppend: true path: /mongodb/logs/mongod.log# network interfacesnet: port: 27017# bindIp: 127.0.0.1# bindIp: 0.0.0.0# how the process runsprocessManagement: timeZoneInfo: /usr/share/zoneinfosecurity: authorization: enabled#operationProfiling:#replication:#sharding:## Enterprise-Only Options:#auditLog:#snmp:连接测试mongo --host 127.0.0.1 --port 27077> db.auth("root", "123456")1> show dbs;admin 0.000GBconfig 0.000GBlocal 0.000GB创建新数据和账号> use demo;switched to db demo> db.createUser({... user : "demo",... pwd: "123456",... roles : [... {... "role" : "dbAdmin",... "db" : "demo"... },... {... "role" : "dbOwner",... "db" : "demo"... }... ],... "mechanisms" : [... "SCRAM-SHA-1",... "SCRAM-SHA-256"... ]... });Successfully added user: { "user" : "demo", "roles" : [ { "role" : "dbAdmin", "db" : "demo" }, { "role" : "dbOwner", "db" : "demo" } ], "mechanisms" : [ "SCRAM-SHA-1", "SCRAM-SHA-256" ]}> exit;测试插入和查询# 需要重连mongo --host 127.0.0.1 --port 27077>db.auth("demo","123456");1> dbdemo> db.foo.insert({"name":"qkl",age:"18",sex:1});WriteResult({ "nInserted" : 1 })> show dbs;demo 0.000GB> show collections;foo> db.foo.find({}){ "_id" : ObjectId("5d22db4bbcbb35d902a31a92"), "name" : "qkl", "age" : "18", "sex" : 1 }> db.foo.insert({"name":"hax",age:"16",sex:0});WriteResult({ "nInserted" : 1 })> db.foo.find({}){ "_id" : ObjectId("5d22db4bbcbb35d902a31a92"), "name" : "qkl", "age" : "18", "sex" : 1 }{ "_id" : ObjectId("5d22dbafbcbb35d902a31a93"), "name" : "hax", "age" : "16", "sex" : 0 }

July 8, 2019 · 2 min · jiezi

MongoDB-42-新特性解读

MongoDB World 2019 上发布新版本 MongoDB 4.2 Beta,包含多项数据库新特性,本文尝试从技术角度解读。 Full Text SearchMongoDB 4.2 之前,全文搜索(Full Text Search)的能力是靠 Text Index 来支持的,在 MongoDB-4.2 里,MongoDB 直接与 Lucene 等引擎整合,在 Atlas 服务里提供全文建索的能力。 MongoDB FTS 原理1.用户可以在 Atlas 上,对集合开启全文索引,后台会开起 Lucene 索引引擎(索引引擎、查询引擎均可配置),对存量数据建立索引。2.对于开启全文建索的集合,新写入到 MongoDB 的数据, 后台的服务会通过 Change Stream 的方式订阅,并更新到 Lucene 索引引擎里。3.索引的查询直接以 MongoDB Query 的方式提供,Mongod 收到请求会把请求转发到 Lucene 引擎,收到建索结果后回复给客户端。 Full Text Search 示例下面是一个 Full Text Search 使用的简单示例,整个使用体验非常简单,除了需要在 Atlas 控制台上建索引,其他跟正常使用 MongoDB 毫无差别,随着这块能力的完善,能覆盖很多 Elastic Search 的场景。 Step1: 准备数据 MongoDB Enterprise > db.fruit.find(){ "_id" : 1, "type" : "apple", "description" : "Apples come in several varieties, including Fuji, Granny Smith, and Honeycrisp." }{ "_id" : 2, "type" : "banana", "description" : "Bananas are usually sold in bunches of five or six." } ...

July 3, 2019 · 3 min · jiezi

副本集是如何实现自动Failover的

简介最近一个 MongoDB 集群环境中的某节点异常下电了,导致业务出现了中断,随即又恢复了正常。通过ELK 告警也监测到了业务报错日志。 运维部对于节点下电的原因进行了排查,发现仅仅是资源分配上的一个失误导致。 在解决了问题之后,大家也对这次中断的也提出了一些问题: ”当前的 MongoDB集群 采用了分片副本集的架构,其中主节点发生故障会产生多大的影响?””MongoDB 副本集不是能自动倒换吗,这个是不是秒级的?”带着这些问题,下面针对副本集的自动Failover机制做一些分析。 日志分析首先可以确认的是,这次掉电的是一个副本集上的主节点,在掉电的时候,主备关系发生了切换。从另外的两个备节点找到了对应的日志: 备节点1的日志 2019-05-06T16:51:11.766+0800 I REPL [ReplicationExecutor] Starting an election, since we've seen no PRIMARY in the past 10000ms2019-05-06T16:51:11.766+0800 I REPL [ReplicationExecutor] conducting a dry run election to see if we could be elected2019-05-06T16:51:11.766+0800 I ASIO [NetworkInterfaceASIO-Replication-0] Connecting to 172.30.129.78:300712019-05-06T16:51:11.767+0800 I REPL [ReplicationExecutor] VoteRequester(term 3 dry run) received a yes vote from 172.30.129.7:30071; response message: { term: 3, voteGranted: true, reason: "", ok: 1.0 }2019-05-06T16:51:11.767+0800 I REPL [ReplicationExecutor] dry election run succeeded, running for election2019-05-06T16:51:11.768+0800 I ASIO [NetworkInterfaceASIO-Replication-0] Connecting to 172.30.129.78:300712019-05-06T16:51:11.771+0800 I REPL [ReplicationExecutor] VoteRequester(term 4) received a yes vote from 172.30.129.7:30071; response message: { term: 4, voteGranted: true, reason: "", ok: 1.0 }2019-05-06T16:51:11.771+0800 I REPL [ReplicationExecutor] election succeeded, assuming primary role in term 42019-05-06T16:51:11.771+0800 I REPL [ReplicationExecutor] transition to PRIMARY2019-05-06T16:51:11.771+0800 I REPL [ReplicationExecutor] Entering primary catch-up mode.2019-05-06T16:51:11.771+0800 I ASIO [NetworkInterfaceASIO-Replication-0] Ending connection to host 172.30.129.78:30071 due to bad connection status; 2 connections to that host remain open2019-05-06T16:51:11.771+0800 I ASIO [NetworkInterfaceASIO-Replication-0] Connecting to 172.30.129.78:300712019-05-06T16:51:13.350+0800 I REPL [ReplicationExecutor] Error in heartbeat request to 172.30.129.78:30071; ExceededTimeLimit: Couldn't get a connection within the time limit备节点2的日志 ...

July 3, 2019 · 3 min · jiezi

SpringBoot整合MongoDB多数据源

<h1 align="center">SpringBoot整合MongoDB多数据源</h1> 依赖// build.gradleimplementation 'org.springframework.boot:spring-boot-starter-data-mongodb'配置文件# application.ymlspring: data: mongodb: primary: uri: mongodb://localhost:27017/db1 secondary: uri: mongodb://localhost:27017/db2主数据库配置// PrimaryMongoConfig.javapackage com.fengwenyi.springboot_mongo_multi_source.config;import com.mongodb.MongoClientURI;import org.springframework.boot.autoconfigure.mongo.MongoProperties;import org.springframework.boot.context.properties.ConfigurationProperties;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.Primary;import org.springframework.data.mongodb.MongoDbFactory;import org.springframework.data.mongodb.core.MongoTemplate;import org.springframework.data.mongodb.core.SimpleMongoDbFactory;import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;/** * MongoDB Primary Config * @author Erwin Feng * @since 2019-07-01 17:12 */@Configuration@EnableMongoRepositories(basePackages = "com.fengwenyi.springboot_mongo_multi_source.primary", mongoTemplateRef = "primaryMongoTemplate")public class PrimaryMongoConfig { @Bean @Primary @ConfigurationProperties(prefix="spring.data.mongodb.primary") public MongoProperties primaryMongoProperties() { return new MongoProperties(); } @Primary @Bean(name = "primaryMongoTemplate") public MongoTemplate primaryMongoTemplate() throws Exception { return new MongoTemplate(primaryFactory(primaryMongoProperties())); } @Bean @Primary public MongoDbFactory primaryFactory(MongoProperties mongoProperties) throws Exception { return new SimpleMongoDbFactory(new MongoClientURI(primaryMongoProperties().getUri())); }}副数据库配置// SecondaryMongoConfig.javapackage com.fengwenyi.springboot_mongo_multi_source.config;import com.mongodb.MongoClientURI;import org.springframework.boot.autoconfigure.mongo.MongoProperties;import org.springframework.boot.context.properties.ConfigurationProperties;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.Primary;import org.springframework.data.mongodb.MongoDbFactory;import org.springframework.data.mongodb.core.MongoTemplate;import org.springframework.data.mongodb.core.SimpleMongoDbFactory;import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;/** * MongoDB Secondary Config * @author Erwin Feng * @since 2019-07-01 17:12 */@Configuration@EnableMongoRepositories(basePackages = "com.fengwenyi.springboot_mongo_multi_source.secondary", mongoTemplateRef = "secondaryMongoTemplate")public class SecondaryMongoConfig { @Bean @ConfigurationProperties(prefix="spring.data.mongodb.secondary") public MongoProperties secondaryMongoProperties() { return new MongoProperties(); } @Bean(name = "secondaryMongoTemplate") public MongoTemplate secondaryMongoTemplate() throws Exception { return new MongoTemplate(secondaryFactory(secondaryMongoProperties())); } @Bean public MongoDbFactory secondaryFactory(MongoProperties mongoProperties) throws Exception { return new SimpleMongoDbFactory(new MongoClientURI(secondaryMongoProperties().getUri())); }}用户实体,副// User.javapackage com.fengwenyi.springboot_mongo_multi_source.secondary.entity;import lombok.Data;import lombok.experimental.Accessors;import org.springframework.data.annotation.Id;import org.springframework.data.mongodb.core.mapping.Document;import java.io.Serializable;import java.time.Instant;/** * 用户 * @author Erwin Feng * @since 2019-07-01 17:15 */@Data@Accessors(chain = true)@Document(collection = "t_user")public class User implements Serializable { private static final long serialVersionUID = -7229906944062898852L; /** ID */ @Id private String id; /** 用户名 */ private String username; /** 年龄 */ private Integer age; /** 注册时间 */ private Instant registerTime;}用户查询仓库,副// UserRepository.javapackage com.fengwenyi.springboot_mongo_multi_source.secondary.repository;import com.fengwenyi.springboot_mongo_multi_source.secondary.entity.User;import org.springframework.data.mongodb.repository.MongoRepository;import java.util.List;/** * 用户 * @author Erwin Feng * @since 2019-07-01 17:18 */public interface UserRepository extends MongoRepository<User, String> { /** * 通过用户名查询 * @param username 用户名 * @return */ List<User> findAllByUsername(String username);}登录日志实体,主// LoginLog.javapackage com.fengwenyi.springboot_mongo_multi_source.primary.entity;import lombok.Data;import lombok.experimental.Accessors;import org.springframework.data.annotation.Id;import java.io.Serializable;import java.time.Instant;/** * 登录日志 * @author Erwin Feng * @since 2019-07-01 17:18 */@Data@Accessors(chain = true)public class LoginLog implements Serializable { private static final long serialVersionUID = -6694661682102504919L; /** ID */ @Id private String id; /** 用户ID */ private String uid; /** 用户名 */ private String username; /** 登录时间 */ private Instant loginTime;}登录日志查询仓库,主// LoginLogRepository.javapackage com.fengwenyi.springboot_mongo_multi_source.primary.repository;import com.fengwenyi.springboot_mongo_multi_source.primary.entity.LoginLog;import org.springframework.data.mongodb.repository.MongoRepository;/** * 登录日志 * @author Erwin Feng * @since 2019-07-01 17:21 */public interface LoginLogRepository extends MongoRepository<LoginLog, String> {}初始化// InitController.javapackage com.fengwenyi.springboot_mongo_multi_source.controller;import com.fengwenyi.springboot_mongo_multi_source.secondary.entity.User;import com.fengwenyi.springboot_mongo_multi_source.secondary.repository.UserRepository;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.RestController;import javax.annotation.PostConstruct;import java.time.Instant;import java.util.ArrayList;import java.util.List;/** * 初始化工具类 * @author Erwin Feng * @since 2019-07-01 17:24 */@RestControllerpublic class InitController { /** [mongo] 用户 */ @Autowired private UserRepository userRepository; @PostConstruct public void init() { List<User> all = userRepository.findAll(); if (all.size() > 0) return; userRepository.save(new User().setUsername("Zhangsan").setAge(20).setRegisterTime(Instant.now())); List<User> users = new ArrayList<>(); User u1 = new User().setUsername("u1").setAge(19).setRegisterTime(Instant.now()); User u2 = new User().setUsername("u2").setAge(20).setRegisterTime(Instant.now()); User u3 = new User().setUsername("u3").setAge(10).setRegisterTime(Instant.now()); users.add(u1); users.add(u2); users.add(u3); userRepository.saveAll(users); }}测试代码// TestController.javapackage com.fengwenyi.springboot_mongo_multi_source.controller;import com.fengwenyi.springboot_mongo_multi_source.primary.entity.LoginLog;import com.fengwenyi.springboot_mongo_multi_source.primary.repository.LoginLogRepository;import com.fengwenyi.springboot_mongo_multi_source.secondary.entity.User;import com.fengwenyi.springboot_mongo_multi_source.secondary.repository.UserRepository;import net.iutil.ApiResult;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.data.mongodb.core.MongoTemplate;import org.springframework.data.mongodb.core.query.Query;import org.springframework.util.StringUtils;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import java.time.Instant;import java.util.List;/** * 测试 * @author Erwin Feng * @since 2019-07-01 17:28 */@RestController@RequestMapping("/test")public class TestController { /** [mongo] 用户 */ @Autowired private UserRepository userRepository; /** [mongo] 登录日志 */ @Autowired private LoginLogRepository loginLogRepository; /** [mongo] */ @Autowired private MongoTemplate mongoTemplate; /** * 登录 * @param username * @return */ @GetMapping("/login") public ApiResult login(String username) { if (StringUtils.isEmpty(username)) return ApiResult.error().setMsg("用户名不能为空"); List<User> users = userRepository.findAllByUsername(username); if (users.size() == 1) { // 记录日志 loginLogRepository.save(new LoginLog().setUid(users.get(0).getId()).setUsername(username).setLoginTime(Instant.now())); return ApiResult.success(); } if (users.size() == 0) return ApiResult.error().setMsg("用户名查询失败"); return ApiResult.error().setMsg("用户异常"); } /** * 登录日志 * @return */ @GetMapping("/login-log") public ApiResult loginLog() { Query query = new Query(); List<LoginLog> loginLogs = mongoTemplate.find(query, LoginLog.class); return ApiResult.success(loginLogs); }}测试用户登录GET http://localhost:8080/test/login?username=Zhangsan ...

July 1, 2019 · 3 min · jiezi

学习到实践docker下的MongoDB运行

目标需求:密码登录+容器数据共享,主从复制 1.配置启动添加配置文件夹,组织容器命令。 a.官方“未提供”相关信息从测试容器中匹配搜索得到 mongod.conf.orig,设置可以启动,网上查找配置项反不能启动,原因是配置是yaml格式!好像听说过。百度查询得到:官方配置说明,网站卡得出奇。 b.启动配置[]:~/tmp/dk/mongodb# docker run --rm --name mg1 -d mongo#默认数据目录root@199665dc0d08:/# ls /data/db -lhtotal 268K-rw------- 1 mongodb mongodb 45 Jun 29 23:39 WiredTiger-rw------- 1 mongodb mongodb 21 Jun 29 23:39 WiredTiger.lock... ...# mongod.conf 删除了一些注释选项# Where and how to store data.storage: #数据库数据存放目录 dbPath: /data/db #启用日志文件,默认启用 journal: enabled: true# where to write logging data.systemLog: destination: file logAppend: true #数据库日志存放目录? path: /var/log/mongodb/mongod.log #静默模式,过滤掉一些无用的日志信息 quiet: true# network interfacesnet: port: 27017 bindIp: 192.168.0.4security: 登录认证 authorization: "enable"测试过程中主要错误有: ...

June 30, 2019 · 3 min · jiezi

mongoDB-40-事务

官网:mongoDB中,对单文档的操作是原子性的。例如insertOne,updateOne等操作。因此建议使用嵌入式文档来实现事务需求,而不是规范化的跨文档设计。但是业务上例如三方数据依赖的需求往往使用嵌入式文档不是理想中的那么方便。所以4.0开始提供了对副本集多文档事务的支持,注意是副本集,也就是说单server是不生效的。接下来的测试需要集群环境,赖得搭建,所以使用mongoDB Cloud提供的Altas的免费集群。 创建测试数据user info 创建springboot项目添加依赖<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.5.RELEASE</version></parent><dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.58</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-mongodb</artifactId> </dependency></dependencies>连接mongoDB这里涉及到了Write Concern,推荐阅读MongoDB writeConcern原理解析w=majority:数据写入到副本集大多数成员后向客户端发送确认,适用于对数据安全性要求比较高的场景,该选项会降低写入性能w=1:默认的writeConcern,数据写入到Primary就向客户端发送确认Read Concern推荐阅读MongoDB readConcern 原理解析spring.data.mongodb.uri=mongodb+srv://vulgar:761341@cluster0-t16it.mongodb.net/vulgar_test?retryWrites=true&w=majority配置mongoDB事务管理@Configurationpublic class MongoTransactionConfiguration { @Bean MongoTransactionManager mongoTransactionManager(MongoDbFactory factory) { return new MongoTransactionManager(factory); }}创建对应实体类User.class@Data@Document(collection = "user")public class User implements Serializable { private static final long serialVersionUID = -7257487638617643262L; private String username; private String password; private String sex; private Integer age; private String email;}Info.class@Data@Document(collection = "info")public class Info implements Serializable { private static final long serialVersionUID = 4494527542566322152L; private String username; private String description;}创建测试SERVICE@Slf4j@Service("mongoService")public class MongoService { @Autowired private MongoTemplate mongoTemplate; @Transactional(rollbackFor = ArithmeticException.class) public void updateWithTransaction() { Query query = new Query(Criteria.where("username").is("vulgar-cd")); Update update = new Update(); update.set("age", 10); mongoTemplate.updateFirst(query, update, User.class); User user = mongoTemplate.findOne(query, User.class); log.info("user is {}", JSON.toJSON(user)); update = new Update(); update.set("description", "hahahaha"); mongoTemplate.updateFirst(query, update, Info.class); Info info = mongoTemplate.findOne(query, Info.class); log.info("info is {}", JSON.toJSON(info)); //测试事务回滚 int i = 1/0; }}创建测试CONTROLLER@Slf4j@RestControllerpublic class MongoController { @Resource(name = "mongoService") private MongoService mongoService; @GetMapping("/transaction") public void updateWithTransaction() { mongoService.updateWithTransaction(); }}启动引用程序 ...

June 30, 2019 · 1 min · jiezi

python使用Scrapy框架抓取起点中文网免费小说案例

使用工具,ubuntu,python,pycharm一、使用pycharm创建项目:过程略二、安装scrapy框架 pip install Scrapy三、创建scrapy项目: 1.创建爬虫项目 scrapy startproject qidian2.创建爬虫,先进入爬虫项目目录cd qidian/scrapy genspider book book.qidian.com创建完成后项目目录如下 目录下的的book.py就是我们的爬虫文件 四、打开book.py编写爬虫的代码 1.进入需要爬去的书的目录,找到开始url设置start_url:#鬼吹灯图书目录start_urls = ['https://book.qidian.com/info/53269#Catalog']2、在创建项目的时候,筛选的url地址为:allowed_domains = ['book.qidian.com'] 打开图书章节后发现章节的url如下: # https://read.qidian.com/chapter/PNjTiyCikMo1/FzxWdm35gIE1 所以需要将read.qidian.com 加入allowed_domains 中,allowed_domains = ['book.qidian.com', 'read.qidian.com']剩下的就是通过xpath 获取抓取到的内容,提取我们需要的内容完整代码如下# -*- coding: utf-8 -*-import scrapyimport logginglogger = logging.getLogger(__name__)class BookSpider(scrapy.Spider): name = 'book' allowed_domains = ['book.qidian.com', 'read.qidian.com'] start_urls = ['https://book.qidian.com/info/53269#Catalog'] def parse(self, response): # 获取章节列表 li_list = response.xpath('//div[@class="volume"][2]/ul/li') # 列表循环取出章节名称和章节对应的url for li in li_list: item = {} # 章节名称 item['chapter_name'] = li.xpath('./a/text()').extract_first() # 章节url item['chapter_url'] = li.xpath('./a/@href').extract_first() # 获取到的url //read.qidian.com/chapter/PNjTiyCikMo1/TpiSLsyH5Hc1 # 需要重新构造 item['chapter_url'] = 'https:' + item['chapter_url'] # 循环抓取每个章节的内容 if item['chapter_url'] is not None: # meta:传递item数据 yield scrapy.Request(item['chapter_url'], callback=self.parse_chapter, meta={'item': item}) def parse_chapter(self, response): item = response.meta['item'] # 获取文章内容 item['chapter_content'] = response.xpath('//div[@class="read-content j_readContent"]/p/text()').extract() yield item五、将爬去数据保存到mongodb中 ...

June 28, 2019 · 1 min · jiezi

liunx服务器永久启动mongoDB和express-generator

踩在巨人的肩膀上,总结如何在关掉shell之后让服务依然保持运行1、永久启动mongod:第一步:找到 mongodb.conf ,用vim编辑,如果为空的话,加入代码: dbpath=/root/mongoDB/data #数据库路径logpath=/root/mongoDB/log/mongodb.log #日志传输路径port=27017 #端口号 fork=true #以守护进程的方式运行,创建服务器进程journal=falsestorageEngine=mmapv1第二步:运行命令 mongod -f ××/××/mongodb.conf(mongodb.conf所在的路径),成功之后就永久启动了2、以 forever 永久启动express generator:第一步:下载forever,npm i forever -g第二步:找到forever安装路径,(默认一般安装在 usr/local/bin/)配置全局环境变量(修改根目录 etc/profile 文件)第三步:找到你express generator生成的目录文件夹,修改package.json文件,找到 "start": "node ./bin/www" ## 将node 改为 forever start 即可第四步:启动forever forever start app.js然后,就算你关闭shell,也可以访问你的项目了最后,附上forever的一些常用命令:(不定时更新)//1. 简单的启动 forever start ×××//2. 停止启动 forever stop ××× //3. 停止所有运行的nodeforever stopall //4. 监听当前文件夹下的所有文件改动 forever start -w ××× //5.显示所有运行的服务 forever list

June 28, 2019 · 1 min · jiezi

MongoDB-–-使用模式构建之属性模式

简单描述:直接进入了解属性模式。它特别适合以下的情况: 有一个大的文档,但是它其实拥有一些相似的字段,而且这些字段的一个子集具有相同的特征,最后其实需要对这些子集字段进行排序或者查询;实际上也不是所有文档都会出现需要的排序字段;或者上述两个条件均满足事实上考虑到性能方面的原因,为了优化搜索可能需要许多索引才能照顾到这些子集。但是创建越多的索引也只会导致性能的下降。属性模式为这种情况提供了一个很好的解决方案。 实例:一个订单数据文档,其实是有很多需要记录的时间,比如创建时间,支付时间,发货时间等等。在设计数据结构的时候当然第一时间就会想到如图: 实际上这种设计在时间类型比较少的情况下是没有太大问题,但是结合了实际业务场景,一张订单的时间当然不会太少,有时候为了优化排序,不得不建立相应的所以,现在问题就来的,根据这么多字段逐个建立索引那可能建立很多,这样反而会降低整体查询的性能。那么这时候使用属性模式就很合适了。如下图: 如果订单数据结构考虑使用了这种模式后,就不需要反复为相似的字段子集建立索引,大大提高查询效率。 结论:属性模式针对每个文档中许多类似字段提供了更简单的文档索引。通过将这个数据子集移动到一个键值子文档中,我们可以使用不确定的字段名,为信息添加额外的限定符,并更清楚地说明原始字段和值的关系。当我们使用属性模式时,由于需要的索引更少,查询变得更简单更快。

June 28, 2019 · 1 min · jiezi

MongoDB-–-使用模式构建之多态模式

简述:当集合中的所有文档都具有相似但不相同的结构时,我们将其称为多态模式 实例:实际业务中需要用到是用户帖子,帖子的大致结构是相同的,但是其具体内容是根据不同帖子类型需要显示内容是不同的。所以这个时候使用多态模式比较适合,单个查询可以便可以得到全部帖子以及其内容,提高了性能。如下图: 总结:当文档具有更多的相似性而不是差异性时,就会使用多态模式。这种模式设计的典型用例是: 单一视图应用程序内容管理移动应用程序产品目录多态模式提供了一个易于实现的设计,允许在单个集合中进行查询。这也是 mongodb 的无 schema 设计提供一个便利数据构建功能。

June 28, 2019 · 1 min · jiezi

MongoDB-42-新特性解读

云数据库 MongoDB 版 基于飞天分布式系统和高性能存储,提供三节点副本集的高可用架构,容灾切换,故障迁移完全透明化。并提供专业的数据库在线扩容、备份回滚、性能优化等解决方案。 了解更多 MongoDB World 2019 上发布新版本 MongoDB 4.2 Beta,包含多项数据库新特性,本文尝试从技术角度解读。 Full Text SearchMongoDB 4.2 之前,全文搜索(Full Text Search)的能力是靠 Text Index 来支持的,在 MongoDB-4.2 里,MongoDB 直接与 Lucene 等引擎整合,在 Atlas 服务里提供全文建索的能力。 MongoDB FTS 原理用户可以在 Atlas 上,对集合开启全文索引,后台会开起 Lucene 索引引擎(索引引擎、查询引擎均可配置),对存量数据建立索引。对于开启全文建索的集合,新写入到 MongoDB 的数据, 后台的服务会通过 Change Stream 的方式订阅,并更新到 Lucene 索引引擎里。索引的查询直接以 MongoDB Query 的方式提供,Mongod 收到请求会把请求转发到 Lucene 引擎,收到建索结果后回复给客户端。Full Text Search 示例下面是一个 Full Text Search 使用的简单示例,整个使用体验非常简单,除了需要在 Atlas 控制台上建索引,其他跟正常使用 MongoDB 毫无差别,随着这块能力的完善,能覆盖很多 Elastic Search 的场景。 Step1: 准备数据 ...

June 24, 2019 · 3 min · jiezi

MongoDB指南17MapReduce

上一篇文章:MongoDB指南---16、聚合下一篇文章:MongoDB指南---18、聚合命令MapReduce是聚合工具中的明星,它非常强大、非常灵活。有些问题过于复杂,无法使用聚合框架的查询语言来表达,这时可以使用MapReduce。MapReduce使用JavaScript作为“查询语言”,因此它能够表达任意复杂的逻辑。然而,这种强大是有代价的:MapReduce非常慢,不应该用在实时的数据分析中。MapReduce能够在多台服务器之间并行执行。它会将一个大问题分割为多个小问题,将各个小问题发送到不同的机器上,每台机器只负责完成一部分工作。所有机器都完成时,再将这些零碎的解决方案合并为一个完整的解决方案。MapReduce需要几个步骤。最开始是映射(map),将操作映射到集合中的每个文档。这个操作要么“无作为”,要么“产生一些键和X个值”。然后就是中间环节,称作洗牌(shuffle),按照键分组,并将产生的键值组成列表放到对应的键中。化简(reduce)则把列表中的值化简成一个单值。这个值被返回,然后接着进行洗牌,直到每个键的列表只有一个值为止,这个值也就是最终结果。下面会多举几个MapReduce的例子,这个工具非常强大,但也有点复杂。 示例1:找出集合中的所有键用MapReduce来解决这个问题有点大材小用,不过还是一种了解其机制的不错的方式。要是已经知道MapReduce的原理,则直接跳到本节最后,看看MongoDB中MapReduce的使用注意事项。MongoDB会假设你的模式是动态的,所以并不跟踪记录每个文档中的键。通常找到集合中所有文档所有键的最好方式就是用MapReduce。在本例中,会记录每个键出现了多少次。内嵌文档中的键就不计算了,但给map函数做个简单修改就能实现这个功能了。在映射环节,我们希望得到集合中每个文档的所有键。map函数使用特别的emit函数“返回”要处理的值。emit会给MapReduce一个键(类似于前面$group所使用的键)和一个值。这里用emit将文档某个键的计数(count)返回({count : 1})。我们想为每个键单独计数,所以为文档中的每个键调用一次emit。this就是当前映射文档的引用: > map = function() {... for (var key in this) {... emit(key, {count : 1});... }};这样就有了许许多多{count : 1}文档,每一个都与集合中的一个键相关。这种由一个或多个{count : 1}文档组成的数组,会传递给reduce函数。reduce函数有两个参数,一个是key,也就是emit返回的第一个值,还有另外一个数组,由一个或者多个与键对应的{count : 1}文档组成。 > reduce = function(key, emits) {... total = 0;... for (var i in emits) {... total += emits[i].count;... }... return {"count" : total};... }reduce一定要能够在之前的map阶段或者前一个reduce阶段的结果上反复执行。所以reduce返回的文档必须能作为reduce的第二个参数的一个元素。例如,x键映射到了3个文档{count : 1,id : 1}、{count : 1,id : 2}和{count : 1,id : 3},其中id键只用于区分不同的文档。MongoDB可能会这样调用reduce: > r1 = reduce("x", [{count : 1, id : 1}, {count : 1, id : 2}]){count : 2}> r2 = reduce("x", [{count : 1, id : 3}]){count : 1}> reduce("x", [r1, r2]){count : 3}不能认为第二个参数总是初始文档之一(比如{count:1})或者长度固定。reduce应该能处理emit文档和其他reduce返回结果的各种组合。总之,MapReduce函数可能会是下面这样: ...

June 21, 2019 · 2 min · jiezi

MongoDB指南18聚合命令

上一篇文章:MongoDB指南---17、MapReduce下一篇文章:MongoDB为在集合上执行基本的聚合任务提供了一些命令。这些命令在聚合框架出现之前就已经存在了,现在(大多数情况下)已经被聚合框架取代。然而,复杂的group操作可能仍然需要使用JavaScript,count和distinct操作可以被简化为普通命令,不需要使用聚合框架。 countcount是最简单的聚合工具,用于返回集合中的文档数量: > db.foo.count()0> db.foo.insert({"x" : 1})> db.foo.count()1不论集合有多大,count都会很快返回总的文档数量。也可以给count传递一个查询文档,Mongo会计算查询结果的数量: > db.foo.insert({"x" : 2})> db.foo.count()2> db.foo.count({"x" : 1})1对分页显示来说总数非常必要:“共439个,目前显示0~10个”。但是,增加查询条件会使count变慢。count可以使用索引,但是索引并没有足够的元数据供count使用,所以不如直接使用查询来得快。 distinctdistinct用来找出给定键的所有不同值。使用时必须指定集合和键。 > db.runCommand({"distinct" : "people", "key" : "age"})假设集合中有如下文档: {"name" : "Ada", "age" : 20}{"name" : "Fred", "age" : 35}{"name" : "Susan", "age" : 60}{"name" : "Andy", "age" : 35}如果对"age"键使用distinct,会得到所有不同的年龄: > db.runCommand({"distinct" : "people", "key" : "age"}){"values" : [20, 35, 60], "ok" : 1}这里还有一个常见问题:有没有办法获得集合里面所有不同的键呢?MongoDB并没有直接提供这样的功能,但是可以用MapReduce(详见7.3节)自己写一个。 group使用group可以执行更复杂的聚合。先选定分组所依据的键,而后MongoDB就会将集合依据选定键的不同值分成若干组。然后可以对每一个分组内的文档进行聚合,得到一个结果文档。如果你熟悉SQL,那么这个group和SQL中的GROUP BY差不多。假设现在有个跟踪股票价格的站点。从上午10点到下午4点每隔几分钟就会更新某只股票的价格,并保存在MongoDB中。现在报表程序要获得近30天的收盘价。用group就可以轻松办到。股价集合中包含数以千计如下形式的文档: {"day" : "2010/10/03", "time" : "10/3/2010 03:57:01 GMT-400", "price" : 4.23}{"day" : "2010/10/04", "time" : "10/4/2010 11:28:39 GMT-400", "price" : 4.27}{"day" : "2010/10/03", "time" : "10/3/2010 05:00:23 GMT-400", "price" : 4.10}{"day" : "2010/10/06", "time" : "10/6/2010 05:27:58 GMT-400", "price" : 4.30}{"day" : "2010/10/04", "time" : "10/4/2010 08:34:50 GMT-400", "price" : 4.01}注意,由于精度的问题,实际使用中不要将金额以浮点数的方式存储,这个例子只是为了简便才这么做。我们需要的结果列表中应该包含每天的最后交易时间和价格,就像下面这样: ...

June 21, 2019 · 3 min · jiezi

Mongodb初识一

Mongodb初识(一)MongoDB启动服务器第一步:下载安装包第二步:解压下载到的安装包,找到bin目录下面全部.exe文件第三步:在该目录下新建“data”文件夹,它将会作为数据存放的根文件夹。第四步:打开CMD窗口,切换到该目录下,按照如下方式输入命令:mongod --dbpath G:MongoDBdata第四步:测试是否启动,http://localhost:27017/MongoDB 插入文档db.collection_name.insert(document) , 使用 insert() 或 save() 方法向集合中插入文档,如果该集合不在该数据库中, MongoDB 会自动创建该集合并插入文档。MongoDB 可以将数据定义为一个变量,执行插入操作:db.col.insert(document),如果不指定 _id 字段 save() 方法类似于 insert() 方法。如果指定 _id 字段,则会更新该 _id 的数据。MongoDB 语法use database_name命令:如果数据库不存在,则创建数据库,否则切换到指定数据库(默认为 test)db.dropDatabase()命令: 删除当前数据库,默认为 testshow dbs 命令:查看所有数据库db 命令: 查看当前数据库名。db.createCollection(name, options)命令:创建集合 MongoDB中并不需要创建集合。 当插入一些文档 MongoDB 会自动创建集合。db.yiibai.insert({"name" : "yiibai"})show collections 命令:检查创建的集合db.collection_name.drop() 命令:从数据库中删除集合。db.collection_name.find()命令:从集合查询MongoDB数据 find() 将在非结构化的方式显示所有的文件。 如果显示结果是格式化的,那么可以用db.collection_name.find().pretty()除了find()方法还有findOne()方法,仅返回一个文档。MongoDB投影,只选择需要的数据,而不是选择整个一个文档的数据。db.collection_name.find({},{KEY:1}) db.mycol.update({key1:value1},{$set:{key2:value2}})命令:将现有的文档中的值更新 默认情况下,MongoDB将只更新单一文件,更新多,需要一个参数 'multi' 设置为 true。db.mycol.update({'title':'MongoDB Overview'},{$set:{'title':'New MongoDB Tutorial'}},{multi:true})db.mycol.remove(DELLETION_CRITTERIA)命令:用于从集合中删除文档 db.mycol.remove()命令:删除所有文件RDBMS Where子句等效于MongoDB AND条件:使用db.mycol.find({key1:value1, key2:value2}).pretty()命令OR条件:使用db.mycol.find({$or: [{key1: value1}, {key2:value2}]}).pretty(),需要使用$or关键字使用 AND 和 OR条件:使用db.mycol.find({"likes": {$gt:10}, $or: [{key1:value1}, { key2:value2}] }).pretty()命令记录:Limit() 方法,db.collection_name.find().limit(NUMBER),skip()也接受数字类型参数并用于跳过文件数。 ...

June 21, 2019 · 1 min · jiezi

MongoDB指南16聚合

上一篇文章:MongoDB指南---15、特殊的索引和集合:地理空间索引、使用GridFS存储文件下一篇文章:如果你有数据存储在MongoDB中,你想做的可能就不仅仅是将数据提取出来那么简单了;你可能希望对数据进行分析并加以利用。本章介绍MongoDB提供的聚合工具: 聚合框架;MapReduce;几个简单聚合命令:count、distinct和group。 聚合框架使用聚合框架可以对集合中的文档进行变换和组合。基本上,可以用多个构件创建一个管道(pipeline),用于对一连串的文档进行处理。这些构件包括筛选(filtering)、投射(projecting)、分组(grouping)、排序(sorting)、限制(limiting)和跳过(skipping)。例如,有一个保存着杂志文章的集合,你可能希望找出发表文章最多的那个作者。假设每篇文章被保存为MongoDB中的一个文档,可以按照如下步骤创建管道。 将每个文章文档中的作者投射出来。将作者按照名字排序,统计每个名字出现的次数。将作者按照名字出现次数降序排列。将返回结果限制为前5个。这里面的每一步都对应聚合框架中的一个操作符: {"$project" : {"author" : 1}}这样可以将"author"从每个文档中投射出来。这个语法与查询中的字段选择器比较像:可以通过指定"fieldname" : 1选择需要投射的字段,或者通过指定"fieldname":0排除不需要的字段。执行完这个"$project"操作之后,结果集中的每个文档都会以{"_id" : id, "author" : "authorName"}这样的形式表示。这些结果只会在内存中存在,不会被写入磁盘。 {"$group" : {"_id" : "$author", "count" : {"$sum" : 1}}}这样就会将作者按照名字排序,某个作者的名字每出现一次,就会对这个作者的"count"加1。这里首先指定了需要进行分组的字段"author"。这是由"_id" : "$author"指定的。可以将这个操作想象为:这个操作执行完后,每个作者只对应一个结果文档,所以"author"就成了文档的唯一标识符("_id")。第二个字段的意思是为分组内每个文档的"count"字段加1。注意,新加入的文档中并不会有"count"字段;这"$group"创建的一个新字段。执行完这一步之后,结果集中的每个文档会是这样的结构: {"_id" : "authorName", "count" : articleCount}。{"$sort" : {"count" : -1}}这个操作会对结果集中的文档根据"count"字段进行降序排列。 {"$limit" : 5}这个操作将最终的返回结果限制为当前结果中的前5个文档。在MongoDB中实际运行时,要将这些操作分别传给aggregate()函数: > db.articles.aggregate({"$project" : {"author" : 1}},... {"$group" : {"_id" : "$author", "count" : {"$sum" : 1}}},... {"$sort" : {"count" : -1}},... {"$limit" : 5}){ "result" : [ { "_id" : "R. L. Stine", "count" : 430 }, { "_id" : "Edgar Wallace", "count" : 175 }, { "_id" : "Nora Roberts", "count" : 145 }, { "_id" : "Erle Stanley Gardner", "count" : 140 }, { "_id" : "Agatha Christie", "count" : 85 } ], "ok" : 1}aggregate()会返回一个文档数组,其中的内容是发表文章最多的5个作者。 ...

June 20, 2019 · 4 min · jiezi

MongoDB指南15特殊的索引和集合地理空间索引使用GridFS存储文件

上一篇文章:MongoDB指南---14、特殊的索引和集合:固定集合、TTL索引、全文本索引下一篇文章:地理空间索引MongoDB支持几种类型的地理空间索引。其中最常用的是2dsphere索引(用于地球表面类型的地图)和2d索引(用于平面地图和时间连续的数据)。2dsphere允许使用GeoJSON格式(http://www.geojson.org)指定点、线和多边形。点可以用形如[longitude, latitude]([经度,纬度])的两个元素的数组表示: { "name" : "New York City", "loc" : { "type" : "Point", "coordinates" : [50, 2] }}线可以用一个由点组成的数组来表示: { "name" : "Hudson River", "loc" : { "type" : "Line", "coordinates" : [[0,1], [0,2], [1,2]] }}多边形的表示方式与线一样(都是一个由点组成的数组),但是"type"不同: { "name" : "New England", "loc" : { "type" : "Polygon", "coordinates" : [[0,1], [0,2], [1,2]] }}"loc"字段的名字可以是任意的,但是其中的子对象是由GeoJSON指定的,不能改变。在ensureIndex中使用"2dsphere"选项就可以创建一个地理空间索引: > db.world.ensureIndex({"loc" : "2dsphere"}) 地理空间查询的类型可以使用多种不同类型的地理空间查询:交集(intersection)、包含(within)以及接近(nearness)。查询时,需要将希望查找的内容指定为形如{"$geometry" : geoJsonDesc}的GeoJSON对象。例如,可以使用"$geoIntersects"操作符找出与查询位置相交的文档: > var eastVillage = {... "type" : "Polygon",... "coordinates" : [... [-73.9917900, 40.7264100],... [-73.9917900, 40.7321400],... [-73.9829300, 40.7321400],... [-73.9829300, 40.7264100]... ]}> db.open.street.map.find(... {"loc" : {"$geoIntersects" : {"$geometry" : eastVillage}}})这样就会找到所有与East Village区域有交集的文档。可以使用"$within"查询完全包含在某个区域的文档,例如:“East Village有哪些餐馆?” ...

June 20, 2019 · 2 min · jiezi

MongoDB指南14特殊的索引和集合固定集合TTL索引全文本索引

上一篇文章:MongoDB指南---13、索引类型、索引管理下一篇文章:本章介绍MongoDB中一些特殊的集合和索引类型,包括: 用于类队列数据的固定集合(capped collection);用于缓存的TTL索引;用于简单字符串搜索的全文本索引;用于二维平面和球体空间的地理空间索引;用于存储大文件的GridFS。 固定集合MongoDB中的“普通”集合是动态创建的,而且可以自动增长以容纳更多的数据。MongoDB中还有另一种不同类型的集合,叫做固定集合,固定集合需要事先创建好,而且它的大小是固定的(如图6-1所示)。说到固定大小的集合,有一个很有趣的问题:向一个已经满了的固定集合中插入数据会怎么样?答案是,固定集合的行为类似于循环队列。如果已经没有空间了,最老的文档会被删除以释放空间,新插入的文档会占据这块空间(如图6-2所示)。也就是说,当固定集合被占满时,如果再插入新文档,固定集合会自动将最老的文档从集合中删除。 图6-1 新文档被插入到队列末尾 图6-2 如果队列已经被占满,那么最老的文档会被之后插入的新文档覆盖固定集合的访问模式与MongoDB中的大部分集合不同:数据被顺序写入磁盘上的固定空间。因此它们在碟式磁盘(spinning disk)上的写入速度非常快,尤其是集合拥有专用磁盘时(这样就不会因为其他集合的一些随机性的写操作而“中断”)。 固定集合不能被分片。固定集合可以用于记录日志,尽管它们不够灵活。虽然可以在创建时指定集合大小,但无法控制什么时候数据会被覆盖。 创建固定集合不同于普通集合,固定集合必须在使用之前先显式创建。可以使用create命令创建固定集合。在shell中,可以使用createCollection函数: > db.createCollection("my_collection", {"capped" : true, "size" : 100000});{ "ok" : true }上面的命令创建了一个名为my_collection大小为100 000字节的固定集合。除了大小,createCollection还能够指定固定集合中文档的数量: > db.createCollection("my_collection2",... {"capped" : true, "size" : 100000, "max" : 100});{ "ok" : true }可以使用这种方式来保存最新的10则新闻,或者是将每个用户的文档数量限制为1000。固定集合创建之后,就不能改变了(如果需要修改固定集合的属性,只能将它删除之后再重建)。因此,在创建大的固定集合之前应该仔细想清楚它的大小。 为固定集合指定文档数量限制时,必须同时指定固定集合的大小。不管先达到哪一个限制,之后插入的新文档就会把最老的文档挤出集合:固定集合的文档数量不能超过文档数量限制,固定集合的大小也不能超过大小限制。创建固定集合时还有另一个选项,可以将已有的某个常规集合转换为固定集合,可以使用convertToCapped命令实现。下面的例子将test集合转换为一个大小为10 000字节的固定集合: > db.runCommand({"convertToCapped" : "test", "size" : 10000});{ "ok" : true }无法将固定集合转换为非固定集合(只能将其删除)。 自然排序对固定集合可以进行一种特殊的排序,称为自然排序(natural sort)。自然排序返回结果集中文档的顺序就是文档在磁盘上的顺序(如图6-3所示)。 图6-3 使用{"$natural" : 1}进行排序对大多数集合来说,自然排序的意义不大,因为文档的位置经常变动。但是,固定集合中的文档是按照文档被插入的顺序保存的,自然顺序就是文档的插入顺序。因此,自然排序得到的文档是从旧到新排列的。当然也可以按照从新到旧的顺序排列(如图6-4所示)。 > db.my_collection.find().sort({"$natural" : -1}) ...

June 20, 2019 · 3 min · jiezi

MongoDB指南13索引类型索引管理

上一篇文章:MongoDB指南---12、使用explain()和hint()、何时不应该使用索引下一篇文章:MongoDB指南---14、特殊的索引和集合:固定集合、TTL索引、全文本索引创建索引时可以指定一些选项,使用不同选项建立的索引会有不同的行为。接下来的小节会介绍常见的索引变种,更高级的索引类型和特殊选项会在下一章介绍。 唯一索引唯一索引可以确保集合的每一个文档的指定键都有唯一值。例如,如果想保证同不文档的"username"键拥有不同的值,创建一个唯一索引就好了: > db.users.ensureIndex({"username" : 1}, {"unique" : true})如果试图向上面的集合中插入如下文档: > db.users.insert({username: "bob"})> db.users.insert({username: "bob"})E11000 duplicate key error index: test.users.$username_1 dup key: { : "bob" }如果检查这个集合,会发现只有第一个"bob"被保存进来了。发现有重复的键时抛出异常会影响效率,所以可以使用唯一索引来应对偶尔可能会出现的键重复问题,而不是在运行时对重复的键进行过滤。有一个唯一索引可能你已经比较熟悉了,就是"_id"索引,这个索引会在创建集合时自动创建。这就是一个正常的唯一索引(但它不能被删除,而其他唯一索引是可以删除的)。 如果一个文档没有对应的键,索引会将其作为null存储。所以,如果对某个键建立了唯一索引,但插入了多个缺少该索引键的文档,由于集合已经存在一个该索引键的值为null的文档而导致插入失败。5.4.2节会详细介绍相关内容。有些情况下,一个值可能无法被索引。索引储桶(index bucket)的大小是有限制的,如果某个索引条目超出了它的限制,那么这个条目就不会包含在索引里。这样会造成一些困惑,因为使用这个索引进行查询时会有一个文档凭空消失不见了。所有的字段都必须小于1024字节,才能包含到索引里。如果一个文档的字段由于太大不能包含在索引里,MongoDB不会返回任何错误或者警告。也就是说,超出8 KB大小的键不会受到唯一索引的约束:可以插入多个同样的8 KB长的字符串。 1. 复合唯一索引也可以创建复合的唯一索引。创建复合唯一索引时,单个键的值可以相同,但所有键的组合值必须是唯一的。例如,如果有一个{"username" : 1, "age" : 1}上的唯一索引,下面的插入是合法的: db.users.insert({"username" : "bob"})db.users.insert({"username" : "bob", "age" : 23})db.users.insert({"username" : "fred", "age" : 23})然而,如果试图再次插入这三个文档中的任意一个,都会导致键重复异常。GirdFS是MongoDB中存储大文件的标准方式(详见6.5节),其中就用到了复合唯一索引。存储文件内容的集合有一个{"files_id" : 1, "n" : 1}上的复合唯一索引,因此文档的某一部分看起来可能会是下面这个样子:{"files_id" : ObjectId("4b23c3ca7525f35f94b60a2d"), "n" : 1}{"files_id" : ObjectId("4b23c3ca7525f35f94b60a2d"), "n" : 2}{"files_id" : ObjectId("4b23c3ca7525f35f94b60a2d"), "n" : 3}{"files_id" : ObjectId("4b23c3ca7525f35f94b60a2d"), "n" : 4}注意,所有"files_id"的值都相同,但是"n"的值不同。2. 去除重复在已有的集合上创建唯一索引时可能会失败,因为集合中可能已经存在重复值了: ...

June 20, 2019 · 2 min · jiezi

MongoDB指南12使用explain和hint何时不应该使用索引

上一篇文章:MongoDB指南---11、使用复合索引、$操作符如何使用索引、索引对象和数组、索引基数下一篇文章:MongoDB指南---13、索引类型使用explain()和hint()从上面的内容可以看出,explain()能够提供大量与查询相关的信息。对于速度比较慢的查询来说,这是最重要的诊断工具之一。通过查看一个查询的explain()输出信息,可以知道查询使用了哪个索引,以及是如何使用的。对于任意查询,都可以在最后添加一个explain()调用(与调用sort()或者limit()一样,不过explain()必须放在最后)。最常见的explain()输出有两种类型:使用索引的查询和没有使用索引的查询。对于特殊类型的索引,生成的查询计划可能会有些许不同,但是大部分字段都是相似的。另外,分片返回的是多个explain()的聚合(第13章会介绍),因为查询会在多个服务器上执行。不使用索引的查询的exlpain()是最基本的explain()类型。如果一个查询不使用索引,是因为它使用了"BasicCursor"(基本游标)。反过来说,大部分使用索引的查询使用的是BtreeCursor(某些特殊类型的索引,比如地理空间索引,使用的是它们自己类型的游标)。对于使用了复合索引的查询,最简单情况下的explain()输出如下所示: > db.users.find({"age" : 42}).explain(){ "cursor" : "BtreeCursor age_1_username_1", "isMultiKey" : false, "n" : 8332, "nscannedObjects" : 8332, "nscanned" : 8332, "nscannedObjectsAllPlans" : 8332, "nscannedAllPlans" : 8332, "scanAndOrder" : false, "indexOnly" : false, "nYields" : 0, "nChunkSkips" : 0, "millis" : 91, "indexBounds" : { "age" : [ [ 42, 42 ] ], "username" : [ [ { "$minElement" : 1 }, { "$maxElement" : 1 } ] ] }, "server" : "ubuntu:27017"}从输出信息中可以看到它使用的索引是age_1_username_1。"millis"表明了这个查询的执行速度,时间是从服务器收到请求开始一直到发出响应为止。然而,这个数值不一定真的是你希望看到的值。如果MongoDB尝试了多个查询计划,那么"millis"显示的是这些查询计划花费的总时间,而不是最优查询计划所花的时间。接下来是实际返回的文档数量:"n"。它无法反映出MongoDB在执行这个查询的过程中所做的工作:搜索了多少索引条目和文档。索引条目是使用"nscanned"描述的。"nscannedObjects"字段的值就是所扫描的文档数量。最后,如果要对结果集进行排序,而MongoDB无法对排序使用索引,那么"scanAndOrder"的值就会是true。也就是说,MongoDB不得不在内存中对结果进行排序,这是非常慢的,而且结果集的数量要比较小。现在你已经知道这些基础知识了,接下来依次详细介绍这些字段。 ...

June 20, 2019 · 2 min · jiezi

flutter的入门实践到可开发

flutter的入门记录前言:flutter 的入门demo 已经写好一个星期了,只不过一直都没有整理出博客来。收拾好心情,来整理一下。继上一篇关于react-native-wx的介绍,是仿照微信界面ui,因为作为前端开发,有一定的js 基础,所有写起RN来,也不是很吃力。但是这个flutter 用的是一个新的语言 dart作为开发语言,没有学习这个语言,但是不要怕,有开发文档,一切都没有那么难。这个flutter demo,虽然已经写了一个初步的demo,但是你要是问我,这个dart里面的具体的东西,我直言说,‘不会’(因为没有具体去学习这个语言,回过头来说,若深入flutter,必学dart),我一路就是对着开发文档和其他参考资料一路"CV"过来。不禁想起了这个图片~ 话不多说,也将继续参考微信界面ui开发,项目名称为 flutter-wx,为了不增加这个入门体验demo的复杂度,只做了几个基本功能页面。项目地址: flutter-wx,欢迎查看!下面截图如下:文章内,图片很多,占据了一定的篇幅。班门弄斧之作,若有大神见到,敬请指教,有不对不合理之处,敬请指出!我是迩伶贰! 1. 环境准备以ios 系统为例。安装xcode,这里主要使用的是xcode里面的ios模拟器,笔者不太喜欢用xcode,如何安装,这里不做赘述;使用flutter镜像编辑 .bash_profile, 往后追加以下,export PUB_HOSTED_URL=https://pub.flutter-io.cnexport FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn获取Flutter SDKhttps://flutter.dev/docs/deve...解压SDK 压缩包,将路径写入 .bash_profilesource ~/.bash_profile检查 flutter 环境flutter doctor上图看出,flutter 需要的环境有的具有,有的不具有,不同的操作系统需要配备不同的软件环境,我们这里是mac,用的是Xcode,因此,我们不需要安装Android Studio,忽略第一条,按照第二条提示的安装环境; 2. 软件安装安装编辑器vscode,这里使用vscode,安装vscode 插件flutter3. 初始化项目打开vscode, command+shift+p; 初始化之后,如下 debugger 模式下,跑起来: 4. 修改项目 新建文件夹 page, utils, page 下新建多个文件,一个文件表示一个页面,utils 为工具函数, main.dart 为项目的主入口。 5. 添砖加瓦-- 组件封装网络请求封装: 在utils 文件夹新建文件 HttpRequest.dart, 代码写入:import 'package:dio/dio.dart';import 'dart:async';/* * 封装 restful 请求 * * GET、POST、DELETE、PATCH * 主要作用为统一处理相关事务: * - 统一处理请求前缀; * - 统一打印请求信息; * - 统一打印响应信息; * - 统一打印报错信息; */class HttpUtils { /// global dio object static Dio dio; /// default options static const String API_PREFIX = 'http://18.10.1.115:4000'; // static const String API_PREFIX = 'http://api.wtodd.wang:4000'; static const int CONNECT_TIMEOUT = 10000; static const int RECEIVE_TIMEOUT = 3000; /// http request methods static const String GET = 'get'; static const String POST = 'post'; static const String PUT = 'put'; static const String PATCH = 'patch'; static const String DELETE = 'delete'; /// request method static Future<Map> request ( String url, { data, method }) async { data = data ?? {}; method = method ?? 'GET'; /// restful 请求处理 data.forEach((key, value) { if (url.indexOf(key) != -1) { url = url.replaceAll(':$key', value.toString()); } }); /// 打印请求相关信息:请求地址、请求方式、请求参数 print('请求地址:【' + method + ' ' + url + '】'); print('请求参数:' + data.toString()); Dio dio = createInstance(); var result; try { Response response = await dio.request(url, data: data, options: new Options(method: method)); result = response.data; /// 打印响应相关信息 print('响应数据:' + response.toString()); } on DioError catch (e) { /// 打印请求失败相关信息 print('请求出错:' + e.toString()); } return result; } /// 创建 dio 实例对象 static Dio createInstance () { if (dio == null) { /// 全局属性:请求前缀、连接超时时间、响应超时时间 BaseOptions options = new BaseOptions( baseUrl: API_PREFIX, connectTimeout: CONNECT_TIMEOUT, receiveTimeout: RECEIVE_TIMEOUT, ); dio = new Dio(options); } return dio; } /// 清空 dio 对象 static clear () { dio = null; }}调用:请求类型封装 ...

June 20, 2019 · 2 min · jiezi

MongoDB指南10索引复合索引-简介

上一篇文章:MongoDB指南---9、游标与数据库命令下一篇文章:MongoDB指南---11、使用复合索引、$操作符如何使用索引、索引对象和数组、索引基数本章介绍MongoDB的索引,索引可以用来优化查询,而且在某些特定类型的查询中,索引是必不可少的。 什么是索引?为什么要用索引?如何选择需要建立索引的字段?如何强制使用索引?如何评估索引的效率?创建索引和删除索引。为集合选择合适的索引是提高性能的关键。 1、 索引简介数据库索引与书籍的索引类似。有了索引就不需要翻整本书,数据库可以直接在索引中查找,在索引中找到条目以后,就可以直接跳转到目标文档的位置,这能使查找速度提高几个数量级。不使用索引的查询称为全表扫描(这个术语来自关系型数据库),也就是说,服务器必须查找完一整本书才能找到查询结果。这个处理过程与我们在一本没有索引的书中查找信息很像:从第1页开始一直读完整本书。通常来说,应该尽量避免全表扫描,因为对于大集合来说,全表扫描的效率非常低。来看一个例子,我们创建了一个拥有1 000 000个文档的集合(如果你想要10 000 000或者100 000 000个文档也行,只要你有那个耐心): > for (i=0; i<1000000; i++) {... db.users.insert(... {... "i" : i,... "username" : "user"+i,... "age" : Math.floor(Math.random()*120),... "created" : new Date()... }... );... }如果在这个集合上做查询,可以使用explain()函数查看MongoDB在执行查询的过程中所做的事情。下面试着查询一个随机的用户名: > db.users.find({username: "user101"}).explain(){ "cursor" : "BasicCursor", "nscanned" : 1000000, "nscannedObjects" : 1000000, "n" : 1, "millis" : 721, "nYields" : 0, "nChunkSkips" : 0, "isMultiKey" : false, "indexOnly" : false, "indexBounds" : { }}5.2节会详细介绍输出信息里的这些字段,目前来说可以忽略大多数字段。"nscanned"是MongoDB在完成这个查询的过程中扫描的文档总数。可以看到,这个集合中的每个文档都被扫描过了。也就是说,为了完成这个查询,MongoDB查看了每一个文档中的每一个字段。这个查询耗费了将近1秒的时间才完成:"millis"字段显示的是这个查询耗费的毫秒数。字段"n"显示了查询结果的数量,这里是1,因为这个集合中确实只有一个username为"user101"的文档。注意,由于不知道集合里的username字段是唯一的,MongoDB不得不查看集合中的每一个文档。为了优化查询,将查询结果限制为1,这样MongoDB在找到一个文档之后就会停止了: ...

June 20, 2019 · 4 min · jiezi

MongoDB指南11使用复合索引操作符如何使用索引索引对象和数组索引基数

上一篇文章:MongoDB指南---9、游标与数据库命令下一篇文章:1、使用复合索引在多个键上建立的索引就是复合索引,在上面的小节中,已经使用过复合索引。复合索引比单键索引要复杂一些,但是也更强大。本节会更深入地介绍复合索引。 1. 选择键的方向到目前为止,我们的所有索引都是升序的(或者是从最小到最大)。但是,如果需要在两个(或者更多)查询条件上进行排序,可能需要让索引键的方向不同。例如,假设我们要根据年龄从小到大,用户名从Z到A对上面的集合进行排序。对于这个问题,之前的索引变得不再高效:每一个年龄分组内都是按照"username"升序排列的,是A到Z,不是Z到A。对于按"age"升序排列按"username"降序排列这样的需求来说,用上面的索引得到的数据的顺序没什么用。为了在不同方向上优化这个复合排序,需要使用与方向相匹配的索引。在这个例子中,可以使用{"age" : 1, "username" : -1},它会以下面的方式组织数据: [21, "user999977"] -> 0xe57bf737[21, "user999954"] -> 0x8bffa512[21, "user999902"] -> 0x9e1447d1[21, "user999900"] -> 0x3a6a8426[21, "user999874"] -> 0xc353ee06...[30, "user999936"] -> 0x7f39a81a[30, "user999850"] -> 0xa979e136[30, "user999775"] -> 0x5de6b77a...[30, "user100324"] -> 0xe14f8e4d[30, "user100140"] -> 0x0f34d446[30, "user100050"] -> 0x223c35b1年龄按照从年轻到年长顺序排列,在每一个年龄分组中,用户名是从Z到A排列的(对于我们的用户名来说,也可以说是按照"9"到"0"排列的)。如果应用程序同时需要按照{"age" : 1, "username" : 1}优化排序,我们还需要创建一个这个方向上的索引。至于索引使用的方向,与排序方向相同就可以了。注意,相互反转(在每个方向都乘以-1)的索引是等价的:{"age" : 1, "user name" : -1}适用的查询与{"age" : -1, "username" : 1}是完全一样的。只有基于多个查询条件进行排序时,索引方向才是比较重要的。如果只是基于单一键进行排序,MongoDB可以简单地从相反方向读取索引。例如,如果有一个基于{"age" : -1}的排序和一个基于{"age" : 1}的索引,MongoDB会在使用索引时进行优化,就如同存在一个{"age" : -1}索引一样(所以不要创建两个这样的索引!)。只有在基于多键排序时,方向才变得重要。 2. 使用覆盖索引(covered index)在上面的例子中,查询只是用来查找正确的文档,然后按照指示获取实际的文档。然后,如果你的查询只需要查找索引中包含的字段,那就根本没必要获取实际的文档。当一个索引包含用户请求的所有字段,可以认为这个索引覆盖了本次查询。在实际中,应该优先使用覆盖索引,而不是去获取实际的文档。这样可以保证工作集比较小,尤其与右平衡索引一起使用时。为了确保查询只使用索引就可以完成,应该使用投射(详见4.1.1节)来指定不要返回"_id"字段(除非它是索引的一部分)。可能还需要对不需要查询的字段做索引,因此需要在编写时就在所需的查询速度和这种方式带来的开销之间做好权衡。如果在覆盖索引上执行explain(),"indexOnly"字段的值要为true。如果在一个含有数组的字段上做索引,这个索引永远也无法覆盖查询(因为数组是被保存在索引中的,5.1.4节会深入介绍)。即便将数组字段从需要返回的字段中剔除,这样的索引仍然无法覆盖查询。 ...

June 20, 2019 · 3 min · jiezi

MongoDB用户和角色解释系列上

本文来自MongoDB中文社区:http://www.mongoing.com/ 1、介绍本文讨论保护MongoDB数据库所需的访问控制。具体来说,我们可以使用这些特性来确保只有经过授权的用户才能访问数据库。每个MongoDB用户应该只能访问他们在组织中所扮演的角色所需要的数据,这由组织中负责管理数据安全的人员来决定。这是管理数据和遵守国际要求所必需的良好特质。 1.1 访问控制访问控制确保访问数据库的人员得到明确的标识,并且能够访问、更新或删除他们有权访问的数据。这是我们将在本文中讨论的主题。在数据库中,我们可以处理客户机的身份验证和它们希望执行的操作的授权。 1.2 认证当客户机或用户访问数据库时,第一个任务是检查该用户是否是已知用户,并提供凭证以确保能够令人信服地识别他们,这就是所谓的身份验证。使用MongoDB,我们可以使用以下工具之一来处理认证问题。 1.2.1 内部工具SCRAM:MongoDB默认身份验证机制。它根据用户名、密码来进行数据库身份验证。x.509证书:该机制使用x.509证书代替用户名和密码。基于副本集或分片集群中的服务器或成员对客户机进行身份验证。维基百科上说:“x.509证书包含公钥和标识,由证书颁发机构或自签名,持有证书的人可以依赖证书所包含的公钥来建立安全通信”。 1.2.2 外部工具LDAP:这个协议最常见的用途是提供一个中心服务器来存储用户名和密码,允许不同的应用程序连接到LDAP服务器来验证用户。Kerberos:这是一个基于票据的行业标准认证协议。注解:外部工具只在MongoDB企业版中提供。 作为一种最佳实践,我们将为需要访问数据库的每个实体创建登录凭据,但只针对这些实体。这样做,我们将能够审计所有用户所做的所有活动,并完成GDPR要求。除了用户身份验证之外,还需要对服务器和网络进程进行身份验证。在一套副本或分片集群的所有节点检查彼此不断为了确保都是已知的访问用户(换句话说,确认他们的会员),以及其他任务,比如检查每个成员的健康为了确定副本必须完成一次新的选举。那么什么是选举呢?在MongoDB中,只有一个节点能够执行写操作。当此节点关闭或网络部分开始工作时,其余节点开始进行一次选择,以便选择新的主节点并使服务在不停止的情况下运行。 1.3 授权数据库管理员负责向用户授予或拒绝对数据库资源进行操作的权限。通过使用角色,我们可以指定对资源执行什么操作。因此,角色是授予用户使用特定资源执行特定任务的权限。 资源←动作←角色(权限)→用户 MongoDB提供内置角色,还允许您根据数据库的特定需求定义新的角色。这些角色是根据对资源的操作来定义的。动作是我们可以对数据库进行所有类型的操作,例如查找、删除、插入、更新或创建索引。资源可以是集合、文档、索引、数据库等等。使用只读视图,管理员通过限制对只公开其子集的敏感数据的访问来获得字段级安全性。对视图授予的权限与授予底层集合的权限是分开指定的。每个角色只应该为该角色授予必要的权限,并且只应该为用户分配适合其需求的角色。 1.4 数据库的身份验证MongoDB用户必须使用最初创建它们的数据库来标识自己。这通常是管理数据库,但也可以是其他数据库。无论在哪个数据库上创建了用户,如果将适当的角色授予了用户,他们将能够对其他数据库采取操作。 2、MongoDB用户在启用访问控制之前,应该创建一个用户,该用户可以在启用访问控制后创建用户并为用户分配角色。然后,这个用户管理员将用于创建和维护其他用户和角色,因此需要分配一个合适的角色来支持。如果你不创建此管理用户,则在启用访问控制时将无法登录或创建新用户和角色。 2.1 本地主机异常如果在没有创建至少一个管理用户的情况下启用访问控制,则无法登录。localhost异常允许您在启用访问控制之后创建第一个用户,从而避免了这个问题。要做到这一点,你需要:  启用访问控制 连接到localhost接口 在管理数据库中创建第一个用户,该用户必须具有足够的权限来管理其他用户和角色。 这个localhost异常只适用于仍然没有创建用户的情况。您必须在两个选项中进行选择,在启用访问控制之前创建第一个用户,或者在启用访问控制之后使用localhost异常创建第一个用户。 2.2 如何启用访问控制在启动mongod服务时,可以使用参数指定数据库的特性,或者更好的方法是使用配置文件。无论哪种方式,你都必须使用安全选项:securityauthorization:enabled此设置启用或禁用基于角色的访问控制(译者注:上面的配置是激活状态)。 2.3 如何创建用户在创建MongoDB用户之前,有必要考虑一下用户将要执行的任务。可能会有几个具有相同权限级别的用户,所以最明智的选择是创建一个角色并将其分配给每个用户。通过只更改一个角色,您将更新所有使用它的用户的权限。否则,需要为每个用户对一组或一类用户的访问需求进行更改。操作步骤如下:第一步:将上下文更改为要创建角色的数据库: use admin第二步:执行这个命令:<pre> db.createUser({user : '<userName>',pwd : '<password>', roles : [ { role : '<roleName>', db : '<dbName>' } | '<roleName>', …] })</pre>如果您想创建一个用户而不为该用户分配任何角色,您只需指定一个空的roles字段。注解:如果您还在掌握MongoDB查询语言,像MongoDB IDE Studio 3T这样的工具有一个直观的用户管理器特性,这使得用户管理更加直观和直观。 MongoDB IDE Studio 3T地址:https://studio3t.com/用户管理器地址:https://studio3t.com/knowledg... 2.4 如何删除一个用户假设您有合适的登陆角色并允许删除用户,您将需要将上下文更改到创建数据库的时候,use admin 之后执行如下命令:db.dropUser('<userName>')2.5 用户存储在哪里?要检查用户,必须将上下文更改为创建用户的数据库的情景,例如管理员数据库。<pre> > use '<dbName>'然后您可以使用其中之一> db.system.users.find()或者> db.getUsers()但是,如果您只想询问特定的用户,请使用以下命令:> db.getUser('<userName>')</pre> ...

June 15, 2019 · 2 min · jiezi

聊聊-Python-的内置电池

本文原创并首发于公众号【Python猫】,未经授权,请勿转载。 原文地址:https://mp.weixin.qq.com/s/XzCqoCvcpFJt4A-E4WMqaA (一) 最近,我突然想到一个问题:相比其它语言,有哪些概念或习惯叫法是 Python 特有的? 在朋友圈提出这个问题后,我得到最多的回复是——Pythonic 。这个回复一点都不意外,名字中自带 Python 的,当然是特有的啦,与它相似的,还有 Pythonista 。 这两个词是啥意思呢?Python 圈内流传着一个说法“人生苦短,我用 Python”,人们相信存在着最佳的实践方式,采用这种方式是最美的、最高效的、最优雅的,也即是 Pythonic ,而这样做的人(或以此为追求的人)则自称是 Pythonista。这个称号是有别于 Pythoner 或者 Pythonist 的,简单地说就是,它更有追求、更有逼格。 除了以上两个,Python 还有众多独特的叫法,例如终生仁慈独裁者、装饰器、上下文管理器、推导式与生成式、鸭子类型、猴子补丁、魔术方法、GIL、内置电池,等等。它们有的并不是 Python 所原创或独有,但是却因为它才广为人知,它们在 Python 中是代表性的存在物。 (二) 这些内容都很有意思,本文唯独想聊聊它——内置电池 。 Batteries Included 这个叫法是 Python 特有的,它指的是 Python 拥有“内置电池”,也就是自带丰富多样的标准库,开箱即用,动力十足。 在《PEP 206 -- Python Advanced Library》中,它提出了“内置电池的哲学”(Batteries Included Philosophy):拥有丰富而通用的标准库,无需用户单独下载就能立即使用。还说这使得 Python 领先于很多项目。 根据官方文档显示,Python 内置了 200 多个标准库,类型丰富多样,包括字符处理、数据类型、数值计算、文件处理、并发执行、网络通信、多媒体服务、图形界面、调试与开发、以及操作系统专有服务等等。 内置电池为 Python 提供了一种自给自足的能力(self-sufficient),在大多数情况下,用户不需要再去下载和安装单独的软件包,因此也免去一大堆的依赖问题的折磨。 (三) 某些编程语言中也有内置电池的概念,例如 Perl、Ruby、PHP等等,还有的语言会强调自己内置了强大的功能,例如 Erlang(一切皆进程)、Go(goroutine 机制)。 然而,这个叫法在 Python 中被叫得最响,也被推广到了技术生态中的其它项目里,几乎成了 Python 的专有名词。 在维基百科上搜索“Batteries Included”,该条目有 4 个解释,其中之一表明它是 Python 的 Motto ,这个词的意思是座右铭、格言、箴言,足见分量之重了吧。 ...

June 15, 2019 · 1 min · jiezi

MongoDB指南8特定类型的查询

上一篇文章:MongoDB指南---7、find简介与查询条件下一篇文章:如第2章所述,MongoDB的文档可以使用多种类型的数据。其中有一些在查询时会有特别的表现。 4.3.1 nullnull类型的行为有点奇怪。它确实能匹配自身,所以要是有一个包含如下文档的集合: > db.c.find(){ "_id" : ObjectId("4ba0f0dfd22aa494fd523621"), "y" : null }{ "_id" : ObjectId("4ba0f0dfd22aa494fd523622"), "y" : 1 }{ "_id" : ObjectId("4ba0f148d22aa494fd523623"), "y" : 2 }就可以按照预期的方式查询"y"键为null的文档: > db.c.find({"y" : null}){ "_id" : ObjectId("4ba0f0dfd22aa494fd523621"), "y" : null }但是,null不仅会匹配某个键的值为null的文档,而且还会匹配不包含这个键的文档。所以,这种匹配还会返回缺少这个键的所有文档: > db.c.find({"z" : null}){ "_id" : ObjectId("4ba0f0dfd22aa494fd523621"), "y" : null }{ "_id" : ObjectId("4ba0f0dfd22aa494fd523622"), "y" : 1 }{ "_id" : ObjectId("4ba0f148d22aa494fd523623"), "y" : 2 }如果仅想匹配键值为null的文档,既要检查该键的值是否为null,还要通过"$exists"条件判定键值已存在: > db.c.find({"z" : {"$in" : [null], "$exists" : true}})很遗憾,没有"$eq"操作符,所以这条查询语句看上去有些令人费解,但是使用只有一个元素的"$in"操作符效果是一样的。 ...

June 13, 2019 · 3 min · jiezi

使用模式构建系列总结

在MongoDB University学习更多关于MongoDB的知识和技能 现在到了我们总结使用模式构建系列的时候,这是一个很好的机会回顾一下这个系列涵盖的模式所解决的问题,并着重复习每个模式所具有的一些好处以及做出的权衡。关于模式设计,最常见的问题是“我正在设计一个要做某某事情的应用程序,如何对数据建模?”正如我们希望你在学习本系列过程中可以体会到的那样,要回答这个问题,需要考虑很多事情。不过我们提供了一个应用场景示例图,这至少有助于为通用的数据建模提供一些初级的指导。 应用场景示例 下图是我们在与客户合作多年后发现的用于各种应用程序中设计模式的指导原则。对于哪种设计模式可以用于某类特定的应用程序不是“一成不变”的。你需要仔细查看用例中经常使用的那些,但是不要忽略其它的,它们可能仍然适用。如何设计应用程序的数据模式非常依赖于数据访问的方式。 设计模式总结 近似值 近似值模式适用于当昂贵的计算很频繁,而这些计算的精度要求通常不是首要考虑的时候。 优点• 对数据库更少的写入• 保持在统计学上有效的数字缺点• 无法展示精确的数字• 需要在应用层实现 属性 属性模式适用于解决这样一类问题:我们有一些大文档,它们有很多相似的字段,而这些字段的一个子集具有共同的特征,我们希望对该子集字段进行排序或查询。当需要排序的字段只能在一小部分文档中找到。或者在文档中同时满足这两个条件时。 优点• 需要更少的索引• 查询变得更容易编写,而且通常更快 分桶 当需要管理流式数据,如时间序列、实时分析或物联网(IOT)应用程序时,分桶模式是一个很好的解决方案。 优点• 减少了集合中的文档总数• 提高了索引性能• 可以通过预聚合简化数据的访问 计算 当数据访问模式为读取密集型并且应用程序需要重复计算这些数据时,计算模式是一个很好的选项。 优点• 对于频繁的计算可以减少CPU的工作负载• 查询变得更容易编写,而且通常更快缺点• 识别出需要使用此模式的的场景可能比较困难• 除非必要,请勿过度使用此模式 文档版本控制 当你需要在MongoDB中维护以前版本的文档时,文档版本控制模式是一种可行的解决方案。 优点• 容易实现,即使是在现存的系统中• 在最新版本上进行请求时,没有性能上的影响缺点• 写操作的数量会翻倍• 请求需要被定位到正确的集合 扩展引用 当你的应用程序使用了大量的JOIN操作来将频繁访问的数据集中在一起时,你会发现扩展引用模式非常有用。 优点• 当有大量的JOIN操作时可以提升性能• 读操作会更快,并且可以减少JOIN操作的数量缺点• 会有重复数据 异常值 你是否发现有一些查询或文档和其它典型数据的模式不一样?这些例外情况是否驱动了你应用程序的解决方案?如果是这样,那么异常值模式就是解决这种情况的一个很好的方法。 优点• 防止整个应用的解决方案被某些个别的文档或请求所左右• 请求会针对那些典型的用例进行优化,而异常值仍将得到处理缺点• 通常会为特定的查询而进行定制,因此一些临时产生的查询可能性能不太理想• 此模式的大部分工作是在应用程序代码中完成的 预分配 当你事先知道文档的结构,而应用程序只需要用数据填充它时,预分配模式是正确的选择。 优点• 当预先知道文档结构时,可以简化设计缺点• 简单和性能之间的权衡 多态 当有多种文档它们的相似性比差异更多,并且需要将这些文档保存在同一个集合中时,多态模式是一种解决方案。 优点• 实现简单• 查询可以在单个集合中运行 模式版本控制 几乎每个应用程序都可以从模式版本控制模式中获益,因为数据模式的更改经常发生在应用程序的生命周期中。此模式允许历史版本和当前版本的文档在集合中同时存在。 优点• 不需要停机时间• 模式迁移可控• 减少未来的技术债务缺点• 在迁移过程中,对相同的字段可能需要两个索引 ...

June 13, 2019 · 1 min · jiezi

记一次MongoDB高负载的性能优化

Last-Modified: 2019年6月13日11:08:19 本文是关于记录某次游戏服务端的性能优化, 此处涉及的技术包括: MongoDB(MMAPv1引擎), PHP 随着游戏导入人数逐渐增加, 单个集合的文档数已经超过400W, 经常有玩家反馈说卡, 特别是在服务器迁移后(从8核16G降到4核8G), 卡顿更严重了, 遂开始排查问题. 确认服务器压力首先使用top 命令查看总体情况, 此时cpu占用不高, %wa比例维持在40%左右, 初步判断是磁盘IO过高使用iotop命令以进程粒度来查看io统计, 发现MongoDB进程全速在读操作.使用MongoDB自带的mongostat 命令, 发现 faults字段持续高达200以上, 这意味着每秒访问失败数高达200, 即数据被交换出物理内存, 放到SWAP 由于未设置交换空间, 因此无法通过 vmstat 命令查看是否正在操作SWAP在mongo shell中执行 db.currentOp() 确认当前存在大量执行超久的操作到了此时基本确定问题所在了: 大量的查询(先不管是否合理)导致MongoDB不断进行磁盘IO操作, 由于内存较小(相较之前的16G)导致查询过的缓存数据不断被移出内存. 开始处理减小单个集合大小这一步骤主要是针对库中几个特别大的集合, 且这些集合中的数据不重要且易移除. 此处以Shop表为例(保存每个玩家各种商店的数据), 在移除超过N天未登录玩家数据后, 集合大小从24G降为3G 通过减小集合大小, 不仅可以提高查询效率, 同时可以加快每天的数据库备份速度. 慢日志分析需要打开慢日志 profile=1slowms=300逐条确认所有慢日志, 分析执行语句问题 use xxx;db.system.profile.find({}, {}, 20).sort({millis:-1});此时的重点在于确认执行统计字段(execStats)中 阶段(stage)是全表扫描(COLLSCAN)的, 这是最大的性能杀手. 增加/修改索引通过慢日志分析, 发现大部分全表扫描的原因在于: 排行榜定期统计游戏逻辑需要对某些集合中符合条件的所有文档 update…针对这几种情况, 可以通过增加索引来解决. 举例1: 玩家等级排行榜 // 查询语句db.User.find({gm:0}, {}, 100).sort({Lv:-1, Exp:-1});// 移除旧索引, 增加复合索引db.User.createIndex({Lv:-1, Exp:-1}, {background:true});db.User.dropIndex({Lv:-1})生产环境建索引一定要加 {background: true}, 否则建索引期间会引起大量阻塞. ...

June 13, 2019 · 1 min · jiezi

MongoDB的特性和优势

△事务支持MongoDB 目前只支持单文档事务,需要复杂事务支持的场景暂时不适合 △灵活的文档模型JSON 格式存储最接近真实对象模型,对开发者友好,方便快速开发迭代 △高可用复制集满足数据高可靠、服务高可用的需求,运维简单,故障自动切换 △可扩展分片集群海量数据存储,服务能力水平扩展 △高性能mmapv1、wiredtiger、mongorocks(rocksdb)、in-memory 等多引擎支持满足各种场景需求 △强大的索引支持地理位置索引可用于构建 各种 O2O 应用、文本索引解决搜索的需求、TTL索引解决历史数据自动过期的需求 △Gridfs解决文件存储的需求 △aggregation & mapreduce解决数据分析场景需求,用户可以自己写查询语句或脚本,将请求都分发到 MongoDB 上完成

June 12, 2019 · 1 min · jiezi

MongoDB指南7find简介与查询条件

上一篇文章:MongoDB指南---6、更新文档下一篇文章:本章将详细介绍查询。主要会涵盖以下几个方面: 使用find或者findOne函数和查询文档对数据库执行查询;使用$条件查询实现范围查询、数据集包含查询、不等式查询,以及其他一些查询;查询将会返回一个数据库游标,游标只会在你需要时才将需要的文档批量返回;还有很多针对游标执行的元操作,包括忽略一定数量的结果,或者限定返回结果的数量,以及对结果排序。4.1 find简介MongoDB中使用find来进行查询。查询就是返回一个集合中文档的子集,子集合的范围从0个文档到整个集合。find的第一个参数决定了要返回哪些文档,这个参数是一个文档,用于指定查询条件。空的查询文档(例如{})会匹配集合的全部内容。要是不指定查询文档,默认就是{}。例如: > db.c.find()将批量返回集合c中的所有文档。开始向查询文档中添加键/值对时,就意味着限定了查询条件。对于绝大多数类型来说,这种方式很简单明了。数值匹配数值,布尔类型匹配布尔类型,字符串匹配字符串。查询简单的类型,只要指定想要查找的值就好了,十分简单。例如,想要查找"age"值为27的所有文档,直接将这样的键/值对写进查询文档就好了: > db.users.find({"age" : 27})要是想匹配一个字符串,比如值为"joe"的"username"键,那么直接将键/值对写在查询文档中即可: > db.users.find({"username" : "joe"})可以向查询文档加入多个键/值对,将多个查询条件组合在一起,这样的查询条件会被解释成“条件1AND条件2AND ... AND条件N”。例如,要想查询所有用户名为joe且年龄为27岁的用户,可以像下面这样: > db.users.find({"username" : "joe", "age" : 27}) 4.1.1 指定需要返回的键有时并不需要将文档中所有键/值对都返回。遇到这种情况,可以通过find(或者findOne)的第二个参数来指定想要的键。这样做既会节省传输的数据量,又能节省客户端解码文档的时间和内存消耗。例如,如果只对用户集合的"username"和"email"键感兴趣,可以使用如下查询返回这些键: > db.users.find({}, {"username" : 1, "email" : 1}){ "_id" : ObjectId("4ba0f0dfd22aa494fd523620"), "username" : "joe", "email" : "joe@example.com"}可以看到,默认情况下"_id"这个键总是被返回,即便是没有指定要返回这个键。也可以用第二个参数来剔除查询结果中的某些键/值对。例如,文档中有很多键,但是我们不希望结果中含有"fatal_weakness"键: > db.users.find({}, {"fatal_weakness" : 0})使用这种方式,也可以把"_id"键剔除掉: > db.users.find({}, {"username" : 1, "_id" : 0}){ "username" : "joe",} 4.1.2 限制查询的使用上有些限制。传递给数据库的查询文档的值必须是常量。(在你自己的代码里可以是正常的变量。)也就是不能引用文档中其他键的值。例如,要想保持库存,有"in_stock"(剩余库存)和"num_sold"(已出售)两个键,想通过下列查询来比较两者的值是行不通的: > db.stock.find({"in_stock" : "this.num_sold"}) // 这样是行不通的的确有办法实现类似的操作(详见4.4节),但通常需要略微修改一下文档结构,就能通过普通查询来完成这样的操作了,这种方式性能更好。在这个例子中,可以在文档中使用"initial_stock"(初始库存)和"in_stock"两个键。这样,每当有人购买物品,就将"in_stock"减去1。这样,只需要用一个简单的查询就能知道哪种商品已脱销: ...

June 12, 2019 · 1 min · jiezi

MongoDB指南6更新文档

上一篇文章:MongoDB指南---5、创建、删除文档下一篇文章:文档存入数据库以后,就可以使用update方法来更新它。update有两个参数,一个是查询文档,用于定位需要更新的目标文档;另一个是修改器(modifier)文档,用于说明要对找到的文档进行哪些修改。更新操作是不可分割的:若是两个更新同时发生,先到达服务器的先执行,接着执行另外一个。所以,两个需要同时进行的更新会迅速接连完成,此过程不会破坏文档:最新的更新会取得“胜利”。 3.3.1 文档替换最简单的更新就是用一个新文档完全替换匹配的文档。这适用于进行大规模模式迁移的情况。例如,要对下面的用户文档做一个比较大的调整: { "_id" : ObjectId("4b2b9f67a1f631733d917a7a"), "name" : "joe", "friends" : 32, "enemies" : 2}我们希望将"friends"和"enemies"两个字段移到"relationships"子文档中。可以在shell中改变文档的结构,然后使用update替换数据库中的当前文档: > var joe = db.users.findOne({"name" : "joe"}); > joe.relationships = {"friends" : joe.friends, "enemies" : joe.enemies};{ "friends" : 32, "enemies" : 2}> joe.username = joe.name;"joe"> delete joe.friends;true> delete joe.enemies;true> delete joe.name;true> db.users.update({"name" : "joe"}, joe);现在,用findOne查看更新后的文档结构。 { "_id" : ObjectId("4b2b9f67a1f631733d917a7a"), "username" : "joe", "relationships" : { "friends" : 32, "enemies" : 2 }}一个常见的错误是查询条件匹配到了多个文档,然后更新时由于第二个参数的存在就产生重复的"_id"值。数据库会抛出错误,任何文档都不会更新。例如,有好几个文档都有相同的"name"值,但是我们没有意识到: ...

June 12, 2019 · 6 min · jiezi

MongoDB指南5创建删除文档

上一篇文章:MongoDB指南---4、MongoDB基础知识-使用MongoDB Shell下一篇文章:本章会介绍对数据库移入/移出数据的基本操作,具体包含如下操作: 向集合添加新文档;从集合里删除文档;更新现有文档;为这些操作选择合适的安全级别和速度。 3.1 插入并保存文档插入是向MongoDB中添加数据的基本方法。可以使用insert方法向目标集合插入一个文档: > db.foo.insert({"bar" : "baz"})这个操作会给文档自动增加一个"_id"键(要是原来没有的话),然后将其保存到MongoDB中。 3.1.1 批量插入如果要向集合中插入多个文档,使用批量插入会快一些。使用批量插入,可以将一组文档传递给数据库。在shell中,可以使用batchInsert函数实现批量插入,它与insert函数非常像,只是它接受的是一个文档数组作为参数: > db.foo.batchInsert([{"_id" : 0}, {"_id" : 1}, {"_id" : 2}])> db.foo.find(){ "_id" : 0 }{ "_id" : 1 }{ "_id" : 2 }一次发送数十、数百乃至数千个文档会明显提高插入的速度。只有需要将多个文档插入到一个集合时,这种方式才会有用。不能在单次请求中将多个文档批量插入到多个集合中。要是只导入原始数据(例如,从数据feed或者MySQL中导入),可以使用命令行工具,如mongoimport,而不是批量插入。另一方面,可以使用批量插入在将数据存入MongoDB之前对数据做一些小的修整(将日期转换为日期类型,或添加自定义的"_id"),这样批量插入也可用于导入数据。当前版本的MongoDB能接受的最大消息长度是48 MB,所以在一次批量插入中能插入的文档是有限制的。如果试图插入48 MB以上的数据,多数驱动程序会将这个批量插入请求拆分为多个48 MB的批量插入请求。具体可以查看所使用的驱动程序的相关文档。如果在执行批量插入的过程中有一个文档插入失败,那么在这个文档之前的所有文档都会成功插入到集合中,而这个文档以及之后的所有文档全部插入失败。 > db.foo.batchInsert([{"_id" : 0}, {"_id" : 1}, {"_id" : 1}, {"_id" : 2}])只有前两个文档会被插入,因为插入第三个文档时会发生错误:集合中已经存在一个_id为1的文档,不能重复插入。在批量插入中遇到错误时,如果希望batchInsert忽略错误并且继续执行后续插入,可以使用continueOnError选项。这样就可以将上面例子中的第一个、第二个以及第四个文档都插入到集合中。Shell并不支持这个选项,但是所有驱动程序都支持。 3.1.2 插入校验插入数据时,MongoDB只对数据进行最基本的检查:检查文档的基本结构,如果没有"_id"字段,就自动增加一个。检查大小就是其中一项基本结构检查:所有文档都必须小于16 MB(这个值是MongoDB设计者人为定的,未来有可能会增加)。作这样的限制主要是为了防止不良的模式设计,并且保证性能一致。如果要查看doc文档的BSON大小(单位为字节),可以在shell中执行Object.bsonsize(doc)。16 MB的数据究竟有多大?要知道整部《战争与和平》也才3.14 MB。由于MongoDB只进行最基本的检查,所以插入非法数据很容易(如果你想这么干的话)。因此,应该只允许信任的源(比如你的应用程序服务器)连接数据库。主流语言的所有驱动程序(以及大部分其他语言的驱动程序),都会在将数据插入到数据库之前做大量的数据校验(比如文档是否过大,文档是否包含非UTF-8字符串,是否使用不可识别的类型)。 3.2 删除文档现在数据库中有些数据,要删除它: > db.foo.remove()上述命令会删除foo集合中的所有文档。但是不会删除集合本身,也不会删除集合的元信息。remove函数可以接受一个查询文档作为可选参数。给定这个参数以后,只有符合条件的文档才被删除。例如,假设要删除mailing.list集合中所有"opt-out"为true的人: > db.mailing.list.remove({"opt-out" : true})删除数据是永久性的,不能撤销,也不能恢复。 删除速度删除文档通常很快,但是如果要清空整个集合,那么使用drop直接删除集合会更快(然后在这个空集合上重建各项索引)。例如,使用如下方法插入一百万个测试数据: > for (var i = 0; i < 1000000; i++) { db.tester.insert({"foo": "bar", "baz": i, "z": 10 - i}) }现在把刚插入的文档都删除,并记录花费的时间。首先使用remove进行删除: ...

June 11, 2019 · 1 min · jiezi

MongoDB指南4MongoDB基础知识使用MongoDB-Shell

上一篇文章:MongoDB指南---3、MongoDB基础知识-数据类型下一篇文章:本节将介绍如何将shell作为命令行工具的一部分来使用,如何对shell进行定制,以及shell的一些高级功能。在上面的例子中,我们只是连接到了一个本地的mongod实例。事实上,可以将shell连接到任何MongoDB实例(只要你的计算机与MongoDB实例所在的计算机能够连通)。在启动shell时指定机器名和端口,就可以连接到一台不同的机器(或者端口): $ mongo some-host:30000/myDBMongoDB shell version: 2.4.0connecting to: some-host:30000/myDBdb现在就指向了some-host:30000上的myDB数据库。启动mongo shell时不连接到任何mongod有时很方便。通过--nodb参数启动shell,启动时就不会连接任何数据库: $ mongo --nodbMongoDB shell version: 2.4.0>启动之后,在需要时运行new Mongo(hostname)命令就可以连接到想要的mongod了: > conn = new Mongo("some-host:30000")connection to some-host:30000> db = conn.getDB("myDB")myDB执行完这些命令之后,就可以像平常一样使用db了。任何时候都可以使用这些命令来连接到不同的数据库或者服务器。 2.7.1 shell小贴士由于mongo是一个简化的JavaScript shell,可以通过查看JavaScript的在线文档得到大量帮助。对于MongoDB特有的功能,shell内置了帮助文档,可以使用help命令查看: > help db.help() help on db methods db.mycoll.help() help on collection methods sh.help() sharding helpers ... show dbs show database names show collections show collections in current database show users show users in current database ...可以通过db.help()查看数据库级别的帮助,使用db.foo.help()查看集合级别的帮助。如果想知道一个函数是做什么用的,可以直接在shell输入函数名(函数名后不要输入小括号),这样就可以看到相应函数的JavaScript实现代码。例如,如果想知道update函数的工作机制,或者是记不清参数的顺序,就可以像下面这样做: ...

June 11, 2019 · 2 min · jiezi

MongoDB指南3MongoDB基础知识数据类型

上一篇文章:MongoDB指南---2、MongoDB基础知识-文档、集合、数据库、客户端下一篇文章:MongoDB指南---4、MongoDB基础知识-使用MongoDB Shell本章开始部分介绍了文档的基本概念,现在你已经会启动、运行MongoDB,也会在shell中进行一些操作了。这一节的内容会更加深入。MongoDB支持将多种数据类型作为文档中的值,下面将一一介绍。 2.6.1 基本数据类型在概念上,MongoDB的文档与JavaScript中的对象相近,因而可认为它类似于JSON。JSON(http://www.json.org)是一种简单的数据表示方式:其规范仅用一段文字就能描述清楚(其官网证明了这点),且仅包含6种数据类型。这样有很多好处:易于理解、易于解析、易于记忆。然而,从另一方面来说,因为只有null、布尔、数字、字符串、数组和对象这几种数据类型,所以JSON的表达能力有一定的局限。虽然JSON具备的这些类型已具有很强的表现力,但绝大多数应用(尤其是在与数据库打交道时)都还需要其他一些重要的类型。例如,JSON没有日期类型,这使原本容易的日期处理变得烦人。另外,JSON只有一种数字类型,无法区分浮点数和整数,更别说区分32位和64位数字了。再者,JSON无法表示其他一些通用类型,如正则表达式或函数。MongoDB在保留JSON基本键/值对特性的基础上,添加了其他一些数据类型。 在不同的编程语言下,这些类型的确切表示有些许差异。下面说明MongoDB支持的其他通用类型,以及如何在文档中使用它们。 nullnull用于表示空值或者不存在的字段: {"x" : null}布尔型布尔类型有两个值true和false: {"x" : true}数值shell默认使用64位浮点型数值。因此,以下数值在shell中是很“正常”的: {"x" : 3.14}或: {"x" : 3}对于整型值,可使用NumberInt类(表示4字节带符号整数)或NumberLong类(表示8字符带符号整数),分别举例如下: {"x" : NumberInt("3")}{"x" : NumberLong("3")}字符串UTF-8字符串都可表示为字符串类型的数据: {"x" : "foobar"}日期日期被存储为自新纪元以来经过的毫秒数,不存储时区: {"x" : new Date()} 正则表达式查询时,使用正则表达式作为限定条件,语法也与JavaScript的正则表达式语法相同: {"x" : /foobar/i}数组数据列表或数据集可以表示为数组: {"x" : ["a", "b", "c"]} 内嵌文档文档可嵌套其他文档,被嵌套的文档作为父文档的值: {"x" : {"foo" : "bar"}}对象id对象id是一个12字节的ID,是文档的唯一标识。详见2.6.5节。 {"x" : ObjectId()}还有一些不那么常用,但可能有需要的类型,包括下面这些。 二进制数据二进制数据是一个任意字节的字符串。它不能直接在shell中使用。如果要将非UTF-8字符保存到数据库中,二进制数据是唯一的方式。 代码查询和文档中可以包括任意JavaScript代码: {"x" : function() { /* ... */ }} 另外,有几种大多数情况下仅在内部使用(或被其他类型取代)的类型。在本书中,出现这种情况时会特别说明。关于MongoDB数据格式的更多信息,参考附录B。 2.6.2 日期在JavaScript中,Date类可以用作MongoDB的日期类型。创建日期对象时,应使用new Date(…),而非Date(…)。如将构造函数(constructor)作为函数进行调用(即不包括new的方式),返回的是日期的字符串表示,而非日期(Date)对象。这个结果与MongoDB无关,是JavaScript的工作机制决定的。如果不注意这一点,没有始终使用日期(Date)构造函数,将得到一堆混乱的日期对象和日期的字符串。由于日期和字符串之间无法匹配,所以执行删除、更新及查询等几乎所有操作时会导致很多问题。关于JavaScript日期类的完整解释,以及构造函数的参数格式,参见ECMAScript规范15.9节(http://www.ecmascript.org)。shell根据本地时区设置显示日期对象。然而,数据库中存储的日期仅为新纪元以来的毫秒数,并未存储对应的时区。(当然,可将时区信息存储为另一个键的值)。 2.6.3 数组数组是一组值,它既能作为有序对象(如列表、栈或队列),也能作为无序对象(如数据集)来操作。在下面的文档中,"things"这个键的值是一个数组: {"things" : ["pie", 3.14]}此例表示,数组可包含不同数据类型的元素(在此,是一个字符串和一个浮点数)。实际上,常规的键/值对支持的所有值都可以作为数组的值,数组中甚至可以套嵌数组。文档中的数组有个奇妙的特性,就是MongoDB能“理解”其结构,并知道如何“深入”数组内部对其内容进行操作。这样就能使用数组内容对数组进行查询和构建索引了。例如,之前的例子中,MongoDB可以查询出"things"数组中包含3.14这个元素的所有文档。要是经常使用这个查询,可以对"things"创建索引来提高性能。MongoDB可以使用原子更新对数组内容进行修改,比如深入数组内部将pie改为pi。本书后面还会介绍更多这种操作的例子。 ...

June 11, 2019 · 1 min · jiezi

MongoDB指南1MongoDB简介

下一篇文章:MongoDB指南---2、MongoDB基础知识-文档、集合、数据库、shellMongoDB是一款强大、灵活,且易于扩展的通用型数据库。它能扩展出非常多的功能,如二级索引(secondary index)、范围查询(range query)、排序、聚合(aggregation),以及地理空间索引(geospatial index)。本章涵盖了MongoDB的主要设计特点。 1.1 易于使用MongoDB是一个面向文档(document-oriented)的数据库,而不是关系型数据库。不采用关系模型主要是为了获得更好的扩展性。当然, 还有其他一些好处。与关系型数据库相比,面向文档的数据库不再有“行”(row)的概念,取而代之的是更为灵活的“文档”(document)模型。通过在文档中嵌入文档和数组,面向文档的方法能够仅使用一条记录来表现复杂的层次关系,这与使用现代面向对象语言的开发者对数据的看法一致。另外,不再有预定义模式(predefined schema):文档的键(key)和值(value)不再是固定的类型和大小。由于没有固定的模式,根据需要添加或删除字段变得更容易了。通常,由于开发者能够进行快速迭代,所以开发进程得以加快。而且,实验更容易进行。开发者能尝试大量的数据模型,从中选择一个最好的。 1.2 易于扩展应用程序数据集的大小正在以不可思议的速度增长。随着可用带宽的增长和存储器价格的下降,即使是一个小规模的应用程序,需要存储的数据量也可能大得惊人,甚至超出了很多数据库的处理能力。过去非常罕见的T级别数据,现在已是司空见惯了。由于需要存储的数据量不断增长,开发者面临一个困难:应该如何扩展数据库?实质上,这是纵向扩展(scale up)和横向扩展(scale out)之间的选择。纵向扩展就是使用计算能力更强的机器,而横向扩展就是通过分区将数据分散到更多机器上。通常,纵向扩展是最省力的做法,其缺点是大型机一般都非常昂贵。而且,当数据量达到机器的物理极限时,无论花多少钱也买不到更强的机器了。另一个选择是横向扩展:要增加存储空间或提高性能,只需购买一台普通的服务器并把它添加到集群中就可以了。横向扩展既便宜又易于扩展;不过,管理1000台机器比管理一台机器显然要困难得多。MongoDB的设计采用横向扩展。面向文档的数据模型使它能很容易地在多台服务器之间进行数据分割。MongoDB能自动处理跨集群的数据和负载,自动重新分配文档,以及将用户请求路由到正确的机器上。这样,开发者能够集中精力编写应用程序,而不需要考虑如何扩展的问题。如果一个集群需要更大的容量,只需要向集群添加新服务器,MongoDB就会自动将现有数据向新服务器传送。 1.3 丰富的功能MongoDB作为一款通用型数据库,除了能够创建、读取、更新和删除数据之外,还提供一系列不断扩展的独特功能。 索引(indexing)MongoDB支持通用二级索引,允许多种快速查询,且提供唯一索引、复合索引、地理空间索引,以及全文索引。  聚合(aggregation)MongoDB支持“聚合管道”(aggregation pipeline)。用户能通过简单的片段创建复杂的聚合,并通过数据库自动优化。 特殊的集合类型MongoDB支持存在时间有限的集合,适用于那些将在某个时刻过期的数据,如会话(session)。类似地,MongoDB也支持固定大小的集合,用于保存近期数据,如日志。 文件存储(file storage)MongoDB支持一种非常易用的协议,用于存储大文件和文件元数据。MongoDB并不具备一些在关系型数据库中很普遍的功能,如连接(join)和复杂的多行事务(multirow transaction)。省略这些功能是出于架构上的考虑(为了得到更好的扩展性),因为在分布式系统中这两个功能难以高效地实现。 1.4 卓越的性能MongoDB的一个主要目标是提供卓越的性能,这很大程度上决定了MongoDB的设计。MongoDB能对文档进行动态填充(dynamic padding),也能预分配数据文件以利用额外的空间来换取稳定的性能。MongoDB把尽可能多的内存用作缓存(cache),试图为每次查询自动选择正确的索引。总之,MongoDB在各方面的设计都旨在保持它的高性能。虽然,MongoDB非常强大并试图保留关系型数据库的很多特性,但它并不追求具备关系型数据库的所有功能。只要有可能,数据库服务器就会将处理和逻辑交给客户端(通过驱动程序或用户的应用程序代码来实现)。这种精简方式的设计是MongoDB能够实现如此高性能的原因之一。 1.5 小结本书将详细说明MongoDB开发过程中的一些特定设计背后的原因和动机,借此分享MongoDB背后的哲学。当然,掌握MongoDB最好的方式是创建一个易扩展、灵活、快速的功能完备的数据存储,这也是MongoDB的意义所在。 下一篇文章:MongoDB指南---2、MongoDB基础知识-文档、集合、数据库、shell

June 11, 2019 · 1 min · jiezi

MongoDB指南2MongoDB基础知识文档集合数据库shell

上一篇文章:MongoDB指南---1、MongoDB简介下一篇文章:MongoDB非常强大但很容易上手。本章会介绍一些MongoDB的基本概念。文档是MongoDB中数据的基本单元,非常类似于关系型数据库管理系统中的行,但更具表现力。类似地,集合(collection)可以看作是一个拥有动态模式(dynamic schema)的表。MongoDB的一个实例可以拥有多个相互独立的数据库(database),每一个数据库都拥有自己的集合。每一个文档都有一个特殊的键"_id", 这个键在文档所属的集合中是唯一的。MongoDB自带了一个简单但功能强大的JavaScript shell,可用于管理MongoDB的实例或数据操作。 2.1 文档文档是MongoDB的核心概念。文档就是键值对的一个有序集。每种编程语言表示文档的方法不太一样,但大多数编程语言都有一些相通的数据结构,比如映射(map)、散列(hash)或字典(dictionary)。例如,在JavaScript 里面,文档被表示为对象: {"greeting" : "Hello, world!"}这个文档只有一个键"greeting",其对应的值为"Hello,world!"。大多数文档会比这个简单的例子复杂得多,通常会包含多个键/值对: {"greeting" : "Hello, world!", "foo" : 3}从上面的例子可以看出,文档中的值可以是多种不同的数据类型(甚至可以是一个完整的内嵌文档,详见2.6.4节)。在这个例子中,"greeting"的值是一个字符串,而"foo"的值是一个整数。文档的键是字符串。除了少数例外情况,键可以使用任意UTF-8字符。键不能含有0(空字符)。这个字符用于表示键的结尾。.和$具有特殊意义,只能在特定环境下使用(后面的章节会详细说明)。通常,这两个字符是被保留的;如果使用不当的话,驱动程序会有提示。MongoDB不但区分类型,而且区分大小写。例如,下面的两个文档是不同的: {"foo" : 3}{"foo" : "3"}下面两个文档也是不同的: {"foo" : 3}{"Foo" : 3}还有一个非常重要的事项需要注意,MongoDB的文档不能有重复的键。例如,下面的文档是非法的: {"greeting" : "Hello, world!", "greeting" : "Hello, MongoDB!"}文档中的键/值对是有序的:{"x" : 1, "y":2}与{"y": 2, "x": 1}是不同的。通常,字段顺序并不重要,无须让数据库模式依赖特定的字段顺序(MongoDB会对字段重新排序)。在某些特殊情况下,字段顺序变得非常重要,本书将就此给出提示。一些编程语言对文档的默认表示根本就不包含顺序问题(如:Python中的字典、Perl和Ruby 1.8中的散列)。通常,这些语言的驱动具有某些特殊的机制,可以在必要时指定文档的顺序。 2.2 集合集合就是一组文档。如果将MongoDB中的一个文档比喻为关系型数据库中的一行,那么一个集合就相当于一张表。 2.2.1 动态模式集合是动态模式的。这意味着一个集合里面的文档可以是各式各样的。例如,下面两个文档可以存储在同一个集合里面: {"greeting" : "Hello, world!"}{"foo" : 5}需要注意的是,上面的文档不光值的类型不同(一个是字符串,一个是整数),它们的键也完全不同。因为集合里面可以放置任何文档,随之而来的一个问题是:还有必要使用多个集合吗?这的确值得思考:既然没有必要区分不同类型文档的模式,为什么还要使用多个集合呢?这里有几个重要的原因。如果把各种各样的文档不加区分地放在同一个集合里,无论对开发者还是对管理员来说都将是噩梦。开发者要么确保每次查询只返回特定类型的文档,要么让执行查询的应用程序来处理所有不同类型的文档。如果查询博客文章时还要剔除含有作者数据的文档,这会带来很大困扰。在一个集合里查询特定类型的文档在速度上也很不划算,分开查询多个集合要快得多。例如,假设集合里面一个名为"type"的字段用于指明文档是skim、whole还是chunky monkey。那么,如果从一个集合中查询这三种类型的文档,速度会很慢。但如果将这三种不同类型的文档拆分为三个不同的集合,每次只需要查询相应的集合,速度快得多。把同种类型的文档放在一个集合里,数据会更加集中。从一个只包含博客文章的集合里查询几篇文章,或者从同时包含文章数据和作者数据的集合里查出几篇文章,相比之下,前者需要的磁盘寻道操作更少。创建索引时,需要使用文档的附加结构(特别是创建唯一索引时)。索引是按照集合来定义的。在一个集合中只放入一种类型的文档,可以更有效地对集合进行索引。上面这些重要原因促使我们创建一个模式,把相关类型的文档组织在一起,尽管MongoDB对此并没有强制要求。 2.2.2 命名集合使用名称进行标识。集合名可以是满足下列条件的任意UTF-8字符串。 集合名不能是空字符串("")。集合名不能包含0字符(空字符),这个字符表示集合名的结束。集合名不能以“system.”开头,这是为系统集合保留的前缀。例如,system.users这个集 合保存着数据库的用户信息,而system.namespaces集合保存着所有数据库集合的信息。用户创建的集合不能在集合名中包含保留字符'$'。因为某些系统生成的集合中包含$,很多驱动程序确实支持在集合名里包含该字符。除非你要访问这种系统创建的集合,否则不应该在集合名中包含$。子集合组织集合的一种惯例是使用“.”分隔不同命名空间的子集合。例如,一个具有博客功能的应用可能包含两个集合,分别是blog.posts和blog.authors。这是为了使组织结构更清晰,这里的blog集合(这个集合甚至不需要存在)跟它的子集合没有任何关系。虽然子集合没有任何特别的属性,但它们却非常有用,因而很多MongoDB工具都使用了子集合。 GridFS(一种用于存储大文件的协议)使用子集合来存储文件的元数据,这样就可以与文件内容块很好地隔离开来。(第6章会详细介绍GridFS。)大多数驱动程序都提供了一些语法糖,用于访问指定集合的子集合。例如,在数据库shell中,db.blog代表blog集合,而db.blog.posts代表blog.posts集合。在MongoDB中,使用子集合来组织数据非常高效,值得推荐。 2.3 数据库在MongoDB中,多个文档组成集合,而多个集合可以组成数据库。一个MongoDB实例可以承载多个数据库,每个数据库拥有0个或者多个集合。每个数据库都有独立的权限,即便是在磁盘上,不同的数据库也放置在不同的文件中。按照经验,我们将有关一个应用程序的所有数据都存储在同一个数据库中。要想在同一个MongoDB服务器上存放多个应用程序或者用户的数据,就需要使用不同的数据库。数据库通过名称来标识,这点与集合类似。数据库名可以是满足以下条件的任意UTF-8字符串。 不能是空字符串("")。不得含有/、、.、"、*、<、>、:、|、?、$(一个空格)、0(空字符)。基本上,只能使用ASCII中的字母和数字。数据库名区分大小写,即便是在不区分大小写的文件系统中也是如此。简单起见,数据库名应全部小写。数据库名最多为64字节。要记住一点,数据库最终会变成文件系统里的文件,而数据库名就是相应的文件名,这是数据库名有如此多限制的原因。另外,有一些数据库名是保留的,可以直接访问这些有特殊语义的数据库。这些数据库如下所示。 admin从身份验证的角度来讲,这是“root”数据库。如果将一个用户添加到admin数据库,这个用户将自动获得所有数据库的权限。再者,一些特定的服务器端命令也只能从admin数据库运行,如列出所有数据库或关闭服务器。 local这个数据库永远都不可以复制,且一台服务器上的所有本地集合都可以存储在这个数据库中。(第9章会详细介绍复制及本地数据库。) configMongoDB用于分片设置时(参见第13章),分片信息会存储在config数据库中。 ...

June 11, 2019 · 2 min · jiezi

mgo做分页的几种方法

场景当数据两足够大的时候,一页展示不完的时候,我们经常会需要分页的功能。方案方案一,数据不是很大需要排序 s := globalS.Copy() c := s.DB(db).C(collection) defer s.Close() return c.Find(query).Select(selector).Sort(sort).Skip(100).Limit(20).All(result)这中情况只适用于数据量比较小的时候,当数据量达到一定量,sort会内存益处报错 方案二,数据比较大不需要排序 s := globalS.Copy() c := s.DB(db).C(collection) defer s.Close() return c.Find(query).Select(selector).Skip(100).Limit(20).All(result)当数据量比较大的时候,可以使用这种情况。如果需要排序,那就取得返回值后在排序。 方案三,数据比较大,排序需要排序 var result []interface{} s := globalS.Copy() c := s.DB(db).C(collection) defer s.Close() pipeM := []bson.M{ {"$match": bson.M{"status": "true"}}, {"$skip": start}, {"$limit": end}, {"$sort": bson.M{"height": -1}}, } pipe := c.Pipe(pipeM) err = pipe.All(&result)当数据量比较大的时候,并且需要排序的时候,可以使用这种情况。因为在skip,limit和sort同时出现的时候,由于有优先级,需要无论顺序如何,都是先执行sort,在执行skip,最后执行limit,但是我们使用聚合,也就是mgo里面的pipeline,可以改变执行的先后顺序。 结束语上面的三种情况都不适用大量的数据,首先是skip的限制,有人建议边查询边排序,我没有尝试如果数据量过大,我建议使用分库分表的方式来完成,这样加上GO语言的多线程,可以很快的查询和聚合

June 10, 2019 · 1 min · jiezi

讲讲NoSQL比较火的三个数据库MemcachedRedisMongoDB

前言NoSQL,泛指非关系型的数据库。随着互联网不断的发展,传统的关系数据库在应付新互联网模式的网站,特别是超大规模和高并发的SNS类型的纯动态网站已经显得力不从心,暴露了很多难以克服的问题,而非关系型的数据库则由于其本身的特点得到了非常迅速的发展。NoSQL数据库的产生就是为了解决大规模数据集合多重数据种类带来的挑战,尤其是大数据应用难题。而今天主要讲用得比较多的三个NoSQL:Memcached、Redis、MongoDB。 Memcached优点1.Memcached可以利用多核优势,单实例吞吐量极高,可以达到几十万QPS(取决于key、value的字节大小以及服务器硬件性能,日常环境中QPS高峰大约在4-6w左右)。适用于最大程度扛量。 2.支持直接配置为session handle。 缺点1.只支持简单的key/value数据结构,不像Redis可以支持丰富的数据类型。 2.无法进行持久化,数据不能备份,只能用于缓存使用,且重启后数据全部丢失。 3.无法进行数据同步,不能将MC中的数据迁移到其他MC实例中。 4.Memcached内存分配采用Slab Allocation机制管理内存,value大小分布差异较大时会造成内存利用率降低,并引发低利用率时依然出现踢出等问题。需要用户注重value设计。 Redis优点1.支持多种数据结构,如 string(字符串)、 list(双向链表)、dict(hash表)、set(集合)、zset(排序set)、hyperloglog(基数估算)。 2.支持持久化操作,可以进行aof及rdb数据持久化到磁盘,从而进行数据备份或数据恢复等操作,较好的防止数据丢失的手段。 3.支持通过Replication进行数据复制,通过master-slave机制,可以实时进行数据的同步复制,支持多级复制和增量复制,master-slave机制是Redis进行HA的重要手段。 4.单线程请求,所有命令串行执行,并发情况下不需要考虑数据一致性问题。 5.支持pub/sub消息订阅机制,可以用来进行消息订阅与通知。 6.支持简单的事务需求,但业界使用场景很少,并不成熟。 缺点1.Redis只能使用单线程,性能受限于CPU性能,故单实例CPU最高才可能达到5-6wQPS每秒(取决于数据结构,数据大小以及服务器硬件性能,日常环境中QPS高峰大约在1-2w左右)。 2.支持简单的事务需求,但业界使用场景很少,并不成熟,既是优点也是缺点。 3.Redis在string类型上会消耗较多内存,可以使用dict(hash表)压缩存储以降低内存耗用。 MongoDB优点1.更高的写负载,MongoDB拥有更高的插入速度。 2.处理很大的规模的单表,当数据表太大的时候可以很容易的分割表。 3.高可用性,设置M-S不仅方便而且很快,MongoDB还可以快速、安全及自动化的实现节点(数据中心)故障转移。 4.快速的查询,MongoDB支持二维空间索引,比如管道,因此可以快速及精确的从指定位置获取数据。MongoDB在启动后会将数据库中的数据以文件映射的方式加载到内存中。如果内存资源相当丰富的话,这将极大地提高数据库的查询速度。 5.非结构化数据的爆发增长,增加列在有些情况下可能锁定整个数据库,或者增加负载从而导致性能下降,由于MongoDB的弱数据结构模式,添加1个新字段不会对旧表格有任何影响,整个过程会非常快速。 缺点1.不支持事务。 2.MongoDB占用空间过大 。 3.MongoDB没有成熟的维护工具。 Memcached、Redis、MongoDB的区别1.性能三者的性能都比较高,总的来讲:Memcached和Redis差不多,要高于MongoDB。 2.便利性Memcached数据结构单一。 Redis丰富一些,数据操作方面,Redis更好一些,较少的网络IO次数。 MongoDB支持丰富的数据表达,索引,最类似关系型数据库,支持的查询语言非常丰富。 3.存储空间Memcached可以修改最大可用内存,采用LRU算法。 Redis在2.0版本后增加了自己的VM特性,突破物理内存的限制;可以对key value设置过期时间(类似memcached)。 MongoDB适合大数据量的存储,依赖操作系统VM做内存管理,吃内存也比较厉害,服务不要和别的服务在一起。 4.可用性Memcached本身没有数据冗余机制,也没必要;对于故障预防,采用依赖成熟的hash或者环状的算法,解决单点故障引起的抖动问题。 Redis,依赖客户端来实现分布式读写;主从复制时,每次从节点重新连接主节点都要依赖整个快照,无增量复制,因性能和效率问题,所以单点问题比较复杂;不支持自动sharding,需要依赖程序设定一致hash 机制。一种替代方案是,不用redis本身的复制机制,采用自己做主动复制(多份存储),或者改成增量复制的方式(需要自己实现),一致性问题和性能的权衡。 MongoDB支持master-slave,replicaset(内部采用paxos选举算法,自动故障恢复),auto sharding机制,对客户端屏蔽了故障转移和切分机制。 5.可靠性Memcached不支持,通常用在做缓存,提升性能。 Redis支持(快照、AOF):依赖快照进行持久化,aof增强了可靠性的同时,对性能有所影响。 MongoDB从1.8版本开始采用binlog方式支持持久化的可靠性。 6.一致性Memcached在并发场景下,用cas保证一致性。 Redis事务支持比较弱,只能保证事务中的每个操作连续执行。 MongoDB不支持事务。7.数据分析 MongoDB内置了数据分析的功能(mapreduce),其他两者不支持。 8.应用场景Memcached:用于在动态系统中减少数据库负载,提升性能;做缓存,提高性能(适合读多写少,对于数据量比较大,可以采用sharding)。 Redis:数据量较小的高性能操作和运算上。 MongoDB:主要解决海量数据的访问效率问题。 总结分析1.若是简单的存取key-value(主要是读)这样的数据用Memcached好一些。若是要支持数据持久化,量也不大,操作很频繁,多数据类型(如集合、散列之类的),用列表类型做队列之类的高级应用,就用Redis,但如果是数据量比较大时就采用MongoDB。 2.Memcached的很多客户端更加成熟稳定,Redis协议比Memcached复杂。Redis不可能比Memcached快?但是测试结果基本是Redis占绝对优势。 3.云数据库Memcached版实例中的数据是存储在内存中的,当出现宕机、机房断电等意外,或是云数据库Memcached版实例在正常升级维护时,内存中的数据均会丢失。因此,云数据库Memcached版不能作为持久化的数据存储服务使用。Redis的数据都存放在内存中,如果没有配置持久化,Redis重启后数据就全丢失了,于是需要开启Redis的持久化功能,将数据保存到磁盘上,当Redis重启后,可以从磁盘中恢复数据,实现持久化。 4.对于Redis和MongoDB来说,大家一般称之为Redis缓存、MongoDB数据库。Redis主要把数据存储在内存中,其“缓存”的性质远大于其“数据存储“的性质,其中数据的增删改查也只是像变量操作一样简单;MongoDB却是一个“存储数据”的系统,增删改查可以添加很多条件,就像SQL数据库一样灵活。 5.MongoDB和Redis都是NoSQL,采用结构型数据存储。二者在使用场景中,存在一定的区别,这也主要由于二者在内存映射的处理过程,持久化的处理方法不同。MongoDB建议集群部署,更多的考虑到集群方案,Redis更偏重于进程顺序写入,虽然支持集群,也仅限于主-从模式。 相关资料Nosql简介 Redis,Memchche,MongoDb的区别

June 8, 2019 · 1 min · jiezi

APubPlat-一款Devops自动化部署持续集成堡垒机开源项目友好的Web-Terminal

嗨、很高心你能进入这里,我是zane,  在这里给你介绍一款完整的Devops自动化部署工具 APubPlat - 一款完整的Devops自动化部署、持续集成、堡垒机、并且友好的Web Terminal开源项目。 如果你对它感兴趣,就给一个小小的关注吧,一款好的产品更需要碰撞和火花。: github address : https://github.com/wangweianger/APubPlat document : http://apub-wiki.seosiwei.com 接下来我还会持续的更新和迭代。 功能描述资产管理: 方便快捷的管理资产,可为资产分组,为应用分配不同的资产,快捷控制台管理等。应用管理:可建立各种应用任务,前端,后端发布任务,可同时执行单机和多机任务,并实时显示任务日志。WEB控制台: 一套强大的Web Terminal,可直接替代Xshell等工具,可单个或批量打开窗口或执行命令(已支持linux系统,后期版本支持windows系统)。脚本管理:可为单个或者多个资产预装各种软装或者执行各种命令,可自由自定义各种预装脚本,例如安装nginx单|多机脚本生成:可同时为单机或者多机器同时生成shell脚本到指定的目录,方便统一管理和操作。备份还原:单多机可同时备份,并按详细日期进行备份,可随时随意一键恢复任意历史版本。应用场景各种前端静态发布(例如:vue,react,jquery之类的纯前端持续集成)前端中间层发布(例如:使用node.js开发的前端中间层之类的服务持续集成)后端发布 (不限制后端语言,只依赖于shell脚本)单机 | 多台机器 同时发布、备份、还原web版本的xshell,让你不管何时何地都能方便的管理服务器资源强大的权限管理能力,为不同角色分配不同的管理权限,让我们的持续集成更灵活更方便安装环境APubPlat依赖的环境并不复杂,对软硬件的要求也并不高,一台1G双核的服务器都能搞定。 APubPlat 开发技术基于egg.js、vue.js, 因此只需要安装node环境,node.js版本推荐 8.9.0 ~ 10.15.1 之间 数据库基于mongudb、环境数据库基于redis、web服务器基于nginx,所有的软件和服务你都可以安装在一台机器中。 如果想了解更多你可以选择去查看项目文档: http://apub-wiki.seosiwei.com 项目预览登录界面、第一次使用时请注册admin账号,其他账号在后台中进行新增和编辑管理 你可以自定义任何适合你的项目环境 资产管理是项目的一个核心能力,所有持续集成都依赖于资产,也是Web Terminal的入口之一 你可以新建任何需要发布和管理的应用,分配相应的资产,可以选择单机部署、部分部署或者全量部署 在这里你可以查看任何时候的应用构建状态、备份状态、生成配置状态 一切的部署都依赖于shell脚本,脚本的正确与否,决定了你的应用是否能部署成功 友好的web化界面部署日志,支持多机,你可以随时掌控部署状态,也可随时终端某台机器的发布 强大的Web Terminal能力,跟xshell工具一样的体验,随时随地管理你的资产吧 感兴趣如果你有那么一点感兴趣,别犹豫先star或者watch,我会持续的更新和迭代,让它成为你开发中的神器吧 github address: https://github.com/wangweianger/APubPlat 如果你也认可我,那也可以给我一个following额 你还可以加入QQ群来尽情的交流吧,一款好的产品更需要碰撞和火花。

June 5, 2019 · 1 min · jiezi

Gunicron-gevent-Mongodb数据库连接一直增加不释放

问题描述使用Flask开发的Web服务,部署在服务器上使用的是gunicorn manage:app -k gevent -w 4 某日告警,说浏览器崩了,当时急急忙忙的重启,搞好了,因为所有的服务都正常运行,后面查看日志,也没有发现什么特别的地方,最终感觉因该是MongoDB连接数满了,本地测试发现确实是连接数一直增加,不会释放。解决过程关于Gunicron什么是Gunicron:是一个unix上被广泛使用的高性能的Python WSGI UNIX HTTP Server。和大多数的web框架兼容,并具有实现简单,轻量级,高性能等特点。 深入理解uwsgi和gunicorn网络模型为什么要使用Gunicron:用于接受http请求并转换为WSGI协议,以供实现了WSGI协议的flask使用,并且gunicorn得益于gevent等技术,大幅度提高了性能,在生产环境以替代框架自带的WSGI server。生产环境配置gevent==1.3.6greenlet==0.4.14gunicorn==19.9.0pymongo==3.7.0 mongodb连接代码 def __init__(self): config_name = os.getenv('FLASK_CONFIG') or 'default' base_config = config[config_name] # object self.client = MongoClient(host=base_config.MONGO_HOST, port=base_config.MONGO_PORT) db_name = base_config.MONGO_NAME self.db = self.client[db_name]修改方案参考pymongo: MongoClient opened before fork错误排解fork是启动新进程的方法,由于MongoClient不是进程安全的,所以不可以将该实例从父进程中复制到子进程当中。在这个flask应用中,flask使用gunicorn作为网关接口,在启动的时候会启动一个主进程和多个子进程,也就是master/workers,这个时候就出现了MongoClient实例在进程之间的传递。为了解决这个问题,在实例化MongoClient对象的时候要加上connect=False参数。 def __init__(self): config_name = os.getenv('FLASK_CONFIG') or 'default' base_config = config[config_name] # object self.client = MongoClient(host=base_config.MONGO_HOST, port=base_config.MONGO_PORT, maxIdleTimeMS=300000, connect=False) db_name = base_config.MONGO_NAME self.db = self.client[db_name]参考用gevent来host wsgi server,mysql能否长连接python – Mongo连接从未发布 – Django和Mongoengine在gevent上使用gunicorn

June 4, 2019 · 1 min · jiezi

MongoDB-全文检索

免费MongoDB课程:阿里云大学——开发者课堂全文检索对每一个词建立一个索引,指明该词在文章中出现的次数和位置,当用户查询时,检索程序就根据事先建立的索引进行查找,并将查找的结果反馈给用户的检索方式。这个过程类似于通过字典中的检索字表查字的过程。MongoDB 从 2.4 版本开始支持全文检索,目前支持15种语言(暂时不支持中文)的全文索引。danishdutchenglishfinnishfrenchgermanhungarianitaliannorwegianportugueseromanianrussianspanishswedishturkish 启用全文检索MongoDB 在 2.6 版本以后是默认开启全文检索的,如果你使用之前的版本,你需要使用以下代码来启用全文检索: >db.adminCommand({setParameter:true,textSearchEnabled:true})或者使用命令: mongod --setParameter textSearchEnabled=true创建全文索引考虑以下 posts 集合的文档数据,包含了文章内容(post_text)及标签(tags): { "post_text": "enjoy the mongodb articles on Openketang", "tags": [ "mongodb", "openketang" ]}我们可以对 post_text 字段建立全文索引,这样我们可以搜索文章内的内容: db.posts.ensureIndex({post_text:"text"})使用全文索引现在我们已经对 post_text 建立了全文索引,我们可以搜索文章中的关键词 openketang: >db.posts.find({$text:{$search:"openketang"}})以下命令返回了如下包含 runoob 关键词的文档数据: { "_id" : ObjectId("53493d14d852429c10000002"), "post_text" : "enjoy the mongodb articles on Openketang", "tags" : [ "mongodb", "openketang" ]}如果你使用的是旧版本的 MongoDB,你可以使用以下命令: >db.posts.runCommand("text",{search:"openketang"})使用全文索引可以提高搜索效率。 删除全文索引删除已存在的全文索引,可以使用 find 命令查找索引名: >db.posts.getIndexes()通过以上命令获取索引名,本例的索引名为post_text_text,执行以下命令来删除索引: >db.posts.dropIndex("post_text_text")免费MongoDB课程:阿里云大学——开发者课堂

June 4, 2019 · 1 min · jiezi

mongodb数据库之连表查询

在做自己的项目时,因为刚开始接触mongodb非关系型数据库以及关系型数据库的影响还是留在脑中,总会想着进行一个连表查询,然后看官网和资料学习了下,还有那个查询时使用正则来匹配,在这里做个记录1.mongodb正则匹配/* 使用$regex字段匹配 */name: {$regex: 'aa', $options: 'i'};或者:name: {$regex: /aa/, $options: 'i'};或者:name: {$regex: /aa/i};/* 直接使用表达式 */name: {/aa/i}/* 使用$in来匹配,这个字段必须是一个数组 */name: {$in: [/aa/]}2.连表查询使用$lookup来进行连表查询/* aggregate聚合操作,$unwind将数组拆分成单个元素 * $group 分组依据 * $sum 统计 * $project 将返回值进行筛选,是否返回筛选完后的某个字段 * $match 匹配条件 * */usingRecord.aggregate([ { $lookup: { from: 'resources', /* 要连接的表名 */ localField: 'resource_id', /* 当前表对应的字段 */ foreignField: '_id', /* 要连接的表的字段 */ as: 'resourceInfo' /* 查询结果中显示的字段名 */ }, }, { $match: { 'user_id': user_id } }, { $unwind: '$resourceInfo' }, { $sort: { _id: -1 } } ])3.小结mongodb数据库是非关系型、文档型数据库,将有关联的数据存储在一份文档中就能够减少进行表与表之间的关联查询自己先在网上找资料学习后,然后在学习慕课网上的视频时,就有很大的感触,目前的话还是有一些迷惑,对于数据的插入我会找到更好的方法来实现正在努力学习中,若对你的学习有帮助,留下你的印记呗(点个赞咯^_^)往期好文推荐: ...

June 3, 2019 · 1 min · jiezi

Python爬虫入门教程-9100-河北阳光理政投诉板块

1.河北阳光理政投诉板块-写在前面之前几篇文章都是在写图片相关的爬虫,今天写个留言板爬出,为另一套数据分析案例的教程做做准备,作为一个河北人,遵纪守法,有事投诉是必备的技能,那么咱看看我们大河北人都因为什么投诉过呢? 今天要爬取的网站地址 http://yglz.tousu.hebnews.cn/l-1001-5-,一遍爬取一遍嘀咕,别因为爬这个网站在去喝茶,再次声明,学习目的,切勿把人家网站爬瘫痪了。 2.河北阳光理政投诉板块-开始撸代码今天再次尝试使用一个新的模块 lxml ,它可以配合xpath快速解析HTML文档,官网网站 https://lxml.de/index.html利用pip安装lxml,如果安装失败,可以在搜索引擎多搜搜,内容很多,100%有解决方案。 pip install lxml废话不多说,直接通过requests模块获取百度首页,然后用lxml进行解析 import requestsfrom lxml import etree # 从lxml中导入etreeresponse = requests.get("http://www.baidu.com")html = response.content.decode("utf-8")tree=etree.HTML(html) # 解析htmlprint(tree)当你打印的内容为下图所示,你就接近成功了! 下面就是 配合xpath 语法获取网页元素了,关于xpath 这个你也可以自行去学习,非常简单,搜索一下全都是资料,咱就不讲了。 通过xpath我们进行下一步的操作,代码注释可以多看一下。 tree=etree.HTML(html) # 解析htmlhrefs = tree.xpath('//a') #通过xpath获取所有的a元素# 注意网页中有很多的a标签,所以获取到的是一个数组,那么我们需要用循环进行操作for href in hrefs: print(href)打印结果如下 <Element a at 0x1cf64252408><Element a at 0x1cf642523c8><Element a at 0x1cf64252288><Element a at 0x1cf64252308><Element a at 0x1cf64285708><Element a at 0x1cf642aa108><Element a at 0x1cf642aa0c8><Element a at 0x1cf642aa148><Element a at 0x1cf642aa048><Element a at 0x1cf64285848><Element a at 0x1cf642aa188>在使用xpath配合lxml中,记住只要输出上述内容,就代表获取到东西了,当然这个不一定是你需要的,不过代码至少是没有错误的。 ...

May 29, 2019 · 2 min · jiezi

Python爬虫入门教程-8100-蜂鸟网图片爬取之三

1. 蜂鸟网图片-啰嗦两句前几天的教程内容量都比较大,今天写一个相对简单的,爬取的还是蜂鸟,依旧采用aiohttp 希望你喜欢爬取页面https://tu.fengniao.com/15/ 本篇教程还是基于学习的目的,为啥选择蜂鸟,没办法,我瞎选的。 一顿熟悉的操作之后,我找到了下面的链接https://tu.fengniao.com/ajax/ajaxTuPicList.php?page=2&tagsId=15&action=getPicLists 这个链接返回的是JSON格式的数据 page =2页码,那么从1开始进行循环就好了tags=15 标签名称,15是儿童,13是美女,6391是私房照,只能帮助你到这了,毕竟我这是专业博客 ヾ(◍°∇°◍)ノ゙action=getPicLists接口地址,不变的地方2. 蜂鸟网图片-数据有了,开爬吧import aiohttpimport asyncioheaders = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36", "X-Requested-With": "XMLHttpRequest", "Accept": "*/*"}async def get_source(url): print("正在操作:{}".format(url)) conn = aiohttp.TCPConnector(verify_ssl=False) # 防止ssl报错,其中一种写法 async with aiohttp.ClientSession(connector=conn) as session: # 创建session async with session.get(url, headers=headers, timeout=10) as response: # 获得网络请求 if response.status == 200: # 判断返回的请求码 source = await response.text() # 使用await关键字获取返回结果 print(source) else: print("网页访问失败")if __name__=="__main__": url_format = "https://tu.fengniao.com/ajax/ajaxTuPicList.php?page={}&tagsId=15&action=getPicLists" full_urllist= [url_format.format(i) for i in range(1,21)] event_loop = asyncio.get_event_loop() #创建事件循环 tasks = [get_source(url) for url in full_urllist] results = event_loop.run_until_complete(asyncio.wait(tasks)) #等待任务结束 ...

May 28, 2019 · 2 min · jiezi

Python爬虫入门教程-7100-蜂鸟网图片爬取之二

1. 蜂鸟网图片-简介今天玩点新鲜的,使用一个新库 aiohttp ,利用它提高咱爬虫的爬取速度。 安装模块常规套路 pip install aiohttp 运行之后等待,安装完毕,想要深造,那么官方文档必备 :https://aiohttp.readthedocs.io/en/stable/ 接下来就可以开始写代码了。 我们要爬取的页面,这一次选取的是 http://bbs.fengniao.com/forum/forum_101_1_lastpost.html打开页面,我们很容易就获取到了页码 好久没有这么方便的看到页码了。 尝试用 aiohttp 访问这个页面吧,模块的引入,没有什么特殊的,采用 import 即可如果我们需要 使用Asyncio + Aiohttp异步IO 编写爬虫,那么需要注意,你需要异步的方法前面加上async 接下来,先尝试去获取一下上面那个地址的网页源码。 代码中,先声明一个fetch_img_url的函数,同时携带一个参数,这个参数也可以直接写死。 with 上下文不在提示,自行搜索相关资料即可 (`・・´) aiohttp.ClientSession() as session: 创建一个session对象,然后用该session对象去打开网页。session可以进行多项操作,比如post, get, put等 代码中 await response.text() 等待网页数据返回 asyncio.get_event_loop创建线程,run_until_complete方法负责安排执行 tasks中的任务。tasks可以为单独的函数,也可以是列表。 import aiohttp import asyncio async def fetch_img_url(num): url = f'http://bbs.fengniao.com/forum/forum_101_{num}_lastpost.html' # 字符串拼接 # 或者直接写成 url = 'http://bbs.fengniao.com/forum/forum_101_1_lastpost.html' print(url) headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.26 Safari/537.36 Core/1.63.6726.400 QQBrowser/10.2.2265.400', } async with aiohttp.ClientSession() as session: # 获取轮播图地址 async with session.get(url,headers=headers) as response: try: html = await response.text() # 获取到网页源码 print(html) except Exception as e: print("基本错误") print(e)# 这部分你可以直接临摹loop = asyncio.get_event_loop()tasks = asyncio.ensure_future(fetch_img_url(1))results = loop.run_until_complete(tasks)上面代码最后一部分也可以写成 ...

May 27, 2019 · 2 min · jiezi

MongoDB-Change-Stream初体验

Change Stream是MongoDB从3.6开始支持的新特性。这个新特性有哪些奇妙之处,会给我们带来什么便利?本次的文章将就这个主题进行初步讨论。 Change Stream是什么?顾名思义,Change Stream即变更流,是MongoDB向应用发布数据变更的一种方式。即当数据库中有任何数据发生变化,应用端都可以得到通知。我们可以将其理解为在应用中执行的触发器。至于应用想得到什么数据,以什么形式得到数据,则可以通过聚合框架加以过滤和转换。这点将在后文中讨论。 Change Stream的原理我们先来回顾一下MongoDB复制集大致是如何工作的: 应用通过驱动向数据库发起写入请求;在同一个事务中,MongoDB完成oplog和集合的修改;oplog被其他从节点拉走;从节点应用得到的oplog,同样在一个事务中完成对oplog和集合的修改;至此,复制集同步完成。可以发现,整个同步过程是依赖于oplog来进行的。也就是说oplog实际上已经包含了我们需要的所有变更数据。如果观测oplog的变化,是否就能够得到所有变更的数据了呢?对,change stream正是基于这个原理实现的。但事情并没有这么简单!我们来看一下问题有可能出在什么地方。 如何从断点恢复现实世界中,没有哪个应用是可以不间断运行的。不考虑bug导致的问题,正常的应用升级也会导致应用中断运行。那么在应用恢复的时候,从哪里开始继续获取变更呢?oplog当然是可以帮我们做到这点的,但你必须对MongoDB足够了解,才知道有oplogReplay这样的参数,以及其他一些问题。 如何有效地处理订阅假设在一个应用中需要订阅10个不同集合的变更情况,是否需要开10个tailable cursor去获取oplog的变更呢?如果是100个集合呢?出于效率考虑显然不应该这么做。那么整个过程就会变成一个生产者-消费者模式,由一个线程负责从oplog获取变更,由订阅的线程负责消费这些变更。虽然实现也不是那么复杂,并且多半可以找到开源实现,但是涉及多线程就已经足够让初学者头疼一阵的了。公平地说,上面这些还不算严重的问题,下面这些问题可能会更让人头疼。 如何管理权限想要tail oplog,必须对local.oplog.rs有读权限。实际上这相当于对整个数据库都有了读权限,因为所有的变更都会在这里体现出来。DBA可能会阻止你这么做,因为这实在不是一个很安全的做法。 如何数据回滚极端情况下,如果应用处理不当,MongoDB中可能发生数据回滚rollback的问题。如果仅仅通过跟踪oplog,则会出现已经通知出去的变更被回滚的情况。 幸运的是上面这些问题现在都不是问题了,因为change stream帮我们规避了这些复杂的细节。 使用方法由于各种驱动都会有不同的语法和API,从shell中尝试使用change stream可能是最简便的方法。这并不妨碍你随后在各种驱动中的使用,因为shell中能实现的功能在驱动中一定有对应的语法。下面就以shell为例看看change stream应该如何使用。 打开一个shell,订阅你需要关注的集合比如: var cursor = db.bar.watch();为了便于演示,我们在这个shell中不断遍历这个游标以获取新数据: while(true) { if (cursor.hasNext()) { print(JSON.stringify(cursor.next())); }}打开另一个shell,向bar集合中插入一条数据: db.bar.insert({y: 1})此时第一个shell中会立即输出变更数据: {"_id":{"_data":{"$binary":"glzquiIAAAACRmRfaWQAZFzquiK0lDNo+K0DpwBaEARUMrm0ruVACoftuxjt1RtCBA==","$type":"00"}},"operationType":"insert","fullDocument":{"_id":{"$oid":"5ceaba22b4943368f8ad03a7"},"y":1},"ns":{"db":"test","coll":"bar"},"documentKey":{"_id":{"$oid":"5ceaba22b4943368f8ad03a7"}}}这里的一些字段的简单介绍。更完整的介绍请查阅文档change events: _id: 用于恢复断点时使用。即知道这个值,应用断开后下次重启里就可以从这个断点之后开始恢复获得变更;operationType: 操作类型,常见的值包括: insertupdatedeletens: 正在操作的命名空间fullDocument: 完整的文档从断点恢复 var cursor = db.bar.watch([], {resumeAfter: <\_id>})此时使用hasNext()/next()即可获取到随后的变更。 注意事项{readConcern: 'majority'}为了避免被回滚的更新被发布出去,change stream选择只在一个变更到达大多数节点(不可能被回滚)时,才会将这些变更发布到应用。使用的方式即{readConcern: "majority"}。因此以下这些情况下change stream都是不会向应用通知任何变更的: 禁用了readConcern;从旧版本升级,但没有更新featureCompatibilityVersion;PSA架构中S宕机;断点可恢复时间因为change stream是依赖于oplog工作的,自然也会面临oplog面临的所有问题。问题之一就是oplog被覆盖。因此想要保证断点可以恢复,必须保证应用在oplog window的时间内请求断点。 删除集合如果在订阅集合变更过程中集合被删除,则会收到一条invalid信息通知,表示集合已不再可用: { "_id" : { "_data" : BinData(0,"glzqxCcAAAACFFoQBFQyubSu5UAKh+27GO3VG0IE") }, "operationType" : "invalidate"}参考资料Tailable cursor: https://docs.mongodb.com/manual/core/tailable-cursors/生产者-消费者模式: https://zh.wikipedia.org/wiki/%E7%94%9F%E4%BA%A7%E8%80%85%E6%B6%88%E8%B4%B9%E8%80%85%E9%97%AE%E9%A2%98关于回滚: https://docs.mongodb.com/manual/core/replica-set-rollbacks/变更事件: https://docs.mongodb.com/manual/reference/change-events/Change Stream介绍文档:https://docs.mongodb.com/manual/changeStreams/作者简介张耀星,MongoDB亚太区首席技术咨询服务顾问。在MongoDB的开发、应用和咨询服务上有多年实践经验。作为MongoDB认证专家,曾经为不同行业的各类大型客户提供过培训、性能调优、架构设计等各类MongoDB相关技术服务。 ...

May 27, 2019 · 1 min · jiezi

FortuneCommons正式开源啦

Fortune Commons 是笔者在工作以来的一些技术积累,虽然是很[低端],但是对于Java入门或者初入Java开发的工作者来说,也是一个不错的学习资源,今天特地整合出来。Github地址:https://github.com/landy8530/... 版本说明版本约束Spring:5.x+JDK:1.8+版本计划为适应目前国内各个行业不同的技术分布,目前计划三个大版本的计划,说明如下: 版本功能说明1.x数据缓存处理和excel/pdf导出组件,集成Spring MVC2.x数据缓存处理和excel/pdf导出组件,集成Restful API3.x数据缓存处理和excel/pdf导出组件,集成Restful API,并且计划加入Spring Boot/Spring Cloud等注意:以上各个版本都可以增加其他组件。 工程说明目前最新版本为v1.0.x,含有以下子工程(子模块),分别说明如下(也可参考wiki): commons-core主要是本项目所需的一些核心功能实现,比如BeanCopier工具封装,读取yml文件工具,Freemarker解析实现,ApplicationContext工具类,Spring容器初始化后统一操作的listener实现以及其他一些工具类支持。 commons-datacache本模块动态实现了各主流缓存中间件的实现,可以自由切换,依赖于commons-nosql模块。目前实现了以下几种: 本地内存(Memory)MongodbMemcachedRedis(即将实现)commons-nosqlNoSql模拟关系型数据库的CRUD操作,目前有Mongodb实现。 commons-export实现了excel和pdf导出组件 commons-web封装了web端常见的一些配置操作 commons-web-springmvc封装了spring mvc的一些配置操作,依赖于commons-web子模块。 fortune-commons-example本项目的演示模块,主要是用于测试用途。 如何对该开源项目进行贡献代码大多是手敲,所以难免会有错误,你可以帮我Bug,提交issues或者PR。很多知识点我可能没有涉及到,所以你可以对其他知识点进行补充或者加入其他的组件。为了使项目更加的透明化,便利化,也可以参与wiki的编写工作。为什么要做这个开源组件?初始想法源于自己工作中遇到的各种坑,主要目的是为了通过这个开源平台来帮助一些在学习 Java 或者直接在自己公司中使用或者扩展自己的项目。 Git操作说明切换分支fork本工程后可以按照如下操作即可, 切换到master分支,并且更新最新远程库中的代码 git checkout mastergit pull/git fetch创建分支创建自己的本地分支,以master为源创建 git checkout -b fortune-commons-export查看是否创建成功 git branch fortune-commons-beanutils* fortune-commons-export fortune-commons-memcached master星号(*)表示当前所在分支。现在的状态是成功创建的新的分支并且已经切换到新分支上。 同步分支把新建的本地分支push到远程服务器,远程分支与本地分支同名(当然可以随意起名) git push origin fortune-commons-export:fortune-commons-export创建标签git tag -a v1.0.1 -m "fortune commons v1.0.1"git push origin v1.0.1

May 26, 2019 · 1 min · jiezi

小说搜索站快速搭建2内容页解析

三方框架 JSOUPokhttp解析要素 翻章:上一章翻章:下一章目录内容 表设计 /** * 内容 */ private String content; @Field("content_title") private String contentTitle; @Field("chapter_url") private String chapterUrl; @Field("next_chapter_url") private String nextChapterUrl; @Field("last_chapter_url") private String lastChapterUrl;解析代码 public BookChapter content(String url) { BookChapter bookChapter = new BookChapter(); BookSite bookSite = getSite(url); try { Document document = download(url); Element titleElement = document.selectFirst(bookSite.getContentTitle()); if (titleElement != null) { bookChapter.setName(titleElement.text()); } Element chapterElement = document.selectFirst(bookSite.getChapterUrl()); if (chapterElement != null) { bookChapter.setChapterUrl(chapterElement.absUrl("href")); } Element nextElement = document.selectFirst(bookSite.getNextChapterUrl()); if (nextElement != null) { bookChapter.setNextChapterUrl(nextElement.absUrl("href")); } Element lastElement = document.selectFirst(bookSite.getLastChapterUrl()); if (lastElement != null) { bookChapter.setLastChapterUrl(lastElement.absUrl("href")); } Element contentElement = document.selectFirst(bookSite.getContent()); if (contentElement != null) { contentElement.select("a").remove(); contentElement.select("script").remove(); contentElement.select("style").remove(); bookChapter.setContent(contentElement.html()); } } catch (IOException e) { log.error(e.getMessage(), e); } return bookChapter; }最终结果 ...

May 26, 2019 · 1 min · jiezi

Python爬虫入门教程-6100-蜂鸟网图片爬取之一

1. 蜂鸟网图片简介国庆假日结束了,新的工作又开始了,今天我们继续爬取一个网站,这个网站为 http://image.fengniao.com/ ,蜂鸟一个摄影大牛聚集的地方,本教程请用来学习,不要用于商业目的,不出意外,蜂鸟是有版权保护的网站。 2. 蜂鸟网图片网站分析第一步,分析要爬取的网站有没有方法爬取,打开页面,找分页 http://image.fengniao.com/index.php?action=getList&class_id=192&sub_classid=0&page=1&not_in_id=5352384,5352410http://image.fengniao.com/index.php?action=getList&class_id=192&sub_classid=0&page=2&not_in_id=5352384,5352410http://image.fengniao.com/index.php?action=getList&class_id=192&sub_classid=0&page=3&not_in_id=5352384,5352410http://image.fengniao.com/index.php?action=getList&class_id=192&sub_classid=0&page=4&not_in_id=5352384,5352410上面的页面发现一个关键的参数page=1这个就是页码了,但是另一个比较头疼的问题是,他没有最后的页码,这样我们没有办法确定循环次数,所以后面的代码编写中,只能使用while了 这个地址返回的是JSON格式的数据,这个对爬虫来说,非常友好!省的我们用正则表达式分析了。 分析这个页面的头文件,查阅是否有反爬措施 发现除了HOST和User-Agent以外,没有特殊的点,大网站就是任性,没啥反爬,可能压根不在乎这个事情。 第二步,分析图片详情页面,在我们上面获取到的JSON中,找到关键地址 关键地址打开之后,这个地方有一个比较骚的操作了,上面图片中标注的URL选的不好,恰好是一个文章了,我们要的是组图,重新提供一个新链接 http://image.fengniao.com/slide/535/5352130_1.html#p=1 打开页面,你可能直接去找规律了,找到下面的一堆链接,但是这个操作就有点复杂了,我们查阅上述页面的源码 http://image.fengniao.com/slide/535/5352130_1.html#p=1http://image.fengniao.com/slide/535/5352130_1.html#p=2http://image.fengniao.com/slide/535/5352130_1.html#p=3....网页源码中发现了,这么一块区域 大胆的猜测一下,这个应该是图片的JSON,只是他打印在了HTML中,我们只需要用正则表达式进行一下匹配就好了,匹配到之后,然后进行下载。 第三步,开始撸代码。 3. 蜂鸟网图片写代码from http_help import R # 这个文件自己去上篇博客找,或者去github找import threadingimport timeimport jsonimport reimg_list = []imgs_lock = threading.Lock() #图片操作锁# 生产者类class Product(threading.Thread): def __init__(self): threading.Thread.__init__(self) self.__headers = {"Referer":"http://image.fengniao.com/", "Host": "image.fengniao.com", "X-Requested-With":"XMLHttpRequest" } #链接模板 self.__start = "http://image.fengniao.com/index.php?action=getList&class_id=192&sub_classid=0&page={}&not_in_id={}" self.__res = R(headers=self.__headers) def run(self): # 因为不知道循环次数,所有采用while循环 index = 2 #起始页码设置为1 not_in = "5352384,5352410" while True: url = self.__start.format(index,not_in) print("开始操作:{}".format(url)) index += 1 content = self.__res.get_content(url,charset="gbk") if content is None: print("数据可能已经没有了====") continue time.sleep(3) # 睡眠3秒 json_content = json.loads(content) if json_content["status"] == 1: for item in json_content["data"]: title = item["title"] child_url = item["url"] # 获取到链接之后 img_content = self.__res.get_content(child_url,charset="gbk") pattern = re.compile('"pic_url_1920_b":"(.*?)"') imgs_json = pattern.findall(img_content) if len(imgs_json) > 0: if imgs_lock.acquire(): img_list.append({"title":title,"urls":imgs_json}) # 这个地方,我用的是字典+列表的方式,主要是想后面生成文件夹用,你可以进行改造 imgs_lock.release()上面的链接已经生成,下面就是下载图片了,也非常简单 ...

May 26, 2019 · 1 min · jiezi

Flaskpython3supervisorredisdockernginx技术架构web项目docker化二

背景手里有一个web项目,代码按照前端代码库、后端代码库分别在GitHub上,分散带来的结果是,不容易持续集成,比如你可能需要很多的job去保证一个项目的正常运作,但是这个项目也不是特别大,所以尝试将代码融合,于此同时将代码docker化,用于持续部署。 技术架构原来的代码使用gunicorn+gevent+supervisor+flask+DB的架构;具体的细节如下: 本地服务器搭建了一个nginx域名服务器,里面区分PC端还是手机端;访问域名通过nginx,访问前端静态页面的内容静态页面中加载指定地址的数据,提供数据的服务由flask后端提供接口;后端提供的接口,通过访问redis缓存和mongodb数据库,返回相应的数据; docker-compose上篇文章说了flask项目是怎么拆分和组合的,但是上次仅仅是使用docker,多个容器之间使用的--link连接起来的,本篇文章将介绍如何使用docker-compose代替原来的多个docker命令; docker compose是什么可以自行搜索,我直接上我的docker-compose.yml version: '3' services: flask: image: "flask:latest" restart: always depends_on: - mongoDB - redisDB tty: true stdin_open: false environment: SLEEP_SECOND: 10 container_name: flask logging: driver: "json-file" options: max-size: "200M" max-file: "10" command: "gunicorn manage:app -k gevent -w 4 -b 0.0.0.0" volumes: - $HOME/logs:/app/logs networks: - inline-network ports: - "8000:8000" matrix: image: "flask:latest" restart: always depends_on: - mongoDB - redisDB tty: true stdin_open: false environment: SLEEP_SECOND: 10 container_name: matrix command: "flask matrix" volumes: - $HOME/logs:/app/logs - /etc/localtime:/etc/localtime networks: - inline-network broadcast: image: "flask:latest" restart: always depends_on: - mongoDB - redisDB tty: true stdin_open: false environment: SLEEP_SECOND: 10 container_name: broadcast command: "flask broadcast" volumes: - $HOME/logs:/app/logs - /etc/localtime:/etc/localtime networks: - inline-network redisDB: image: "redis:latest" container_name: redis restart: always networks: inline-network: aliases: - redis ports: - "6379:6379" mongoDB: image: "mongo:latest" restart: always container_name: mongo ports: - "27017:27017" volumes: - /var/lib/mongo:/data/db networks: inline-network: aliases: - mongo networks: inline-network: driver: "bridge" ```解释:所有的启动的dontainer都在inline-network网络环境中,所以可以直接使用aliases指定的名字作为数据库连接时候的host,本来是不打算将数据库的端口的,只给flask用,但是后面由于切换的时候是现切换数据库,在切换后段flask的镜像,所以就将数据库端口和宿主机绑定了。其中flask、matrix、broadcast,都是之前代码中的功能,使用supervisor启动的,现在单独启动三个docker进程去完成。前端docker前端的PC端和移动端,都使用npm构建成dist文件,然后通过nginx定向到指定的dist文件内容就可以,所以我们对前端的代码也进行了docker化,使用的是nginx; ...

May 25, 2019 · 2 min · jiezi

小说搜索站快速搭建1架构图

技术栈 模板 thymeleaf框架 Spring Boot 2数据库 Mongodb缓存 Redis搜索 聚合源站搜索框架图 仅展示交流使用:免费小说阅读网

May 25, 2019 · 1 min · jiezi

Python爬虫入门教程-5100-27270图片爬取

获取待爬取页面今天继续爬取一个网站,http://www.27270.com/ent/meinvtupian/ 这个网站具备反爬,so我们下载的代码有些地方处理的也不是很到位,大家重点学习思路,有啥建议可以在评论的地方跟我说说。 为了以后的网络请求操作方向,我们这次简单的进行一些代码的封装操作。 在这里你可以先去安装一个叫做 retrying 的模块 pip install retrying这个模块的具体使用,自己去百度吧。嘿嘿哒~ 在这里我使用了一个随机产生user_agent的方法 import requestsfrom retrying import retryimport randomimport datetimeclass R: def __init__(self,method="get",params=None,headers=None,cookies=None): # do something def get_headers(self): user_agent_list = [ \ "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/22.0.1207.1 Safari/537.1" \ "Mozilla/5.0 (X11; CrOS i686 2268.111.0) AppleWebKit/536.11 (KHTML, like Gecko) Chrome/20.0.1132.57 Safari/536.11", \ "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.6 (KHTML, like Gecko) Chrome/20.0.1092.0 Safari/536.6", \ "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.6 (KHTML, like Gecko) Chrome/20.0.1090.0 Safari/536.6", \ "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/19.77.34.5 Safari/537.1", \ "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.9 Safari/536.5", \ "Mozilla/5.0 (Windows NT 6.0) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.36 Safari/536.5", \ "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3", \ "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3", \ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_0) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3", \ "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3", \ "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3", \ "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3", \ "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3", \ "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3", \ "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.0 Safari/536.3", \ "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.24 (KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24", \ "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/535.24 (KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24" ] UserAgent = random.choice(user_agent_list) headers = {'User-Agent': UserAgent} return headers #other coderetrying 最简单的使用就是给你想不断重试的方法加上 装饰器 @retry ...

May 23, 2019 · 4 min · jiezi

mongodb的model名称细节

最近研究api设计,顺便研究了下mongodb,教程没有仔细看过,所以使用过程中也遇到了一些诡异的现象。 比如我使用中发现,我创建的模型名称,在对应数据库的collections内的名称不一致。我很纳闷,比如我创建的如下: const PersonModel = Mongoose.model("person", { firstname: String, lastname: String});当我将一条数据写入后,用工具Robo 3T发现,名称居然变成了people。 后来查了相关资料,原来mongodb有自己的一套规则,详细的规则,比如我这条:mongoose/lib/utils.js。当然这个是历史版本的例子了。关于这个现象,最新文档中也指出: The first argument is the singular name of the collection your model is for. Mongoose automatically looks for the plural version of your model name. For example, if you use const MyModel = mongoose.model('Ticket', mySchema);Then Mongoose will create the model for your tickets collection, not your ticket collection. 一般情况他会创建一个复数的model,这种person算特殊的了。所以你写得model不一定在查询的时候会一样,即便不一样也不要惊讶哦~

May 22, 2019 · 1 min · jiezi

服务器部署

什么是服务器?1.组成部分2.分类3.特点业务场景 部署什么?如何选择安全防御措施步骤创建主机创建用户安装运行环境把项目同步到服务器安装进程管理程序使用ip和端口调试,没问题,使用nginx作为反向代理使用域名访问(先申请再绑定)

May 21, 2019 · 1 min · jiezi

使用-TypeScript-开发-HapiJS-应用

初始化 npm 项目yarn init添加依赖yarn add hapi添加开发依赖要在开发中使用 TypeScrip,同时至少需要有一个工具,可以一直监听项目文件的变更,并实时的将变更更新至启动的服务中,我选择使用 Nodemon,首先添加以下几个开发依赖 yarn add typescript -Dyarn add nodemon -D接下来,我们需要为 node 与 hapi 安装类型定义库: yarn add @types/node -Dyarn add @types/hapi -D安装完成之后, package.json 文件看起来像下面这样的: { "name": "hapiserver", "version": "0.0.1", "description": "API server", "main": "index.js", "author": "Your Name", "license": "MIT", "dependencies": { "hapi": "^18.1.0" }, "devDependencies": { "@types/hapi": "^18.0.2", "@types/node": "^12.0.2", "nodemon": "^1.19.0", "typescript": "^3.4.5" }}注意:你的 dependencies 与 devDependencies 配置中,版本号可能与我的不同。配置 TypeScript设计项目文件目录结构在项目的根目录下,创建一个名为 src 的目录,用于包含系统的所有源代码文件,接着,创建一个名为 dist 的目录,用于保存由 typescript 编译后的 javascript 文件。 ...

May 21, 2019 · 2 min · jiezi

Python爬虫入门教程-3100-美空网数据爬取

1.美空网数据-简介从今天开始,我们尝试用2篇博客的内容量,搞定一个网站叫做“美空网”网址为:http://www.moko.cc/, 这个网站我分析了一下,我们要爬取的图片在 下面这个网址 http://www.moko.cc/post/13020...然后在去分析一下,我需要找到一个图片列表页面是最好的,作为一个勤劳的爬虫coder,我找到了这个页面 http://www.moko.cc/post/da39d...列表页面被我找到了,貌似没有分页,这就简单多了,但是刚想要爬,就翻车了,我发现一个严重的问题。 http://www.moko.cc/post/==da3... 我要做的是一个自动化的爬虫,但是我发现,出问题了,上面那个黄色背景的位置是啥? ID,昵称,个性首页,这个必须要搞定。 我接下来随机的找了一些图片列表页,试图找到规律到底是啥? http://www.moko.cc/post/978c7...http://www.moko.cc/post/junda...http://www.moko.cc/post/slavi.........没什么问题,发现规律了 http://www.moko.cc/post/==个... 这就有点意思了,我要是能找到尽量多的昵称,不就能拼接出来我想要得所有地址了吗 开干!!! 手段,全站乱点,找入口,找切入点,找是否有API .... .... 结果没找着 下面的一些备选方案 趴这个页面,发现只有 20页 http://www.moko.cc/channels/p... 每页48个模特,20页。那么也才960人啊,完全覆盖不到尽可能多的用户。 接着又找到 http://www.moko.cc/catalog/in... 这个页面 确认了一下眼神,以为发现问题了,结果 哎呀,还么有权限,谁有权限,可以跟我交流一下,一时激动,差点去下载他们的APP,然后进行抓包去。 上面两条路,都不好弄,接下来继续找路子。 无意中,我看到了一丝曙光 关注名单,点进去 哈哈哈,OK了,这不就是,我要找到的东西吗? 不多说了,爬虫走起,测试一下他是否有反扒机制。 我找到了一个关注的人比较多的页面,1500多个人 http://www.moko.cc/subscribe/... 然后又是一波分析操作 2.美空网数据- 爬虫数据存储确定了爬虫的目标,接下来,我做了两件事情,看一下,是否对你也有帮助 确定数据存储在哪里?最后我选择了MongoDB用正则表达式去分析网页数据对此,我们需要安装一下MongoDB,安装的办法肯定是官网教程啦! https://docs.mongodb.com/mast...如果官方文档没有帮助你安装成功。 那么我推荐下面这篇博客 https://www.cnblogs.com/hacky...安装MongoDB出现如下结果 恭喜你安装成功了。 接下来,你要学习的是 关于mongodb用户权限的管理 http://www.cnblogs.com/shiyiw...mongodb索引的创建 https://blog.csdn.net/salmone...别问为啥我不重新写一遍,懒呗~~~ 况且这些资料太多了,互联网大把大把的。 一些我经常用的mongdb的命令 链接 mongo --port <端口号>选择数据库 use admin 展示当前数据库 db 当前数据库授权 db.auth("用户名","密码")查看数据库 show dbs查看数据库中的列名 show collections 创建列 db.createCollection("列名")创建索引 db.col.ensureIndex({"列名字":1},{"unique":true})展示所有索引 db.col.getIndexes()删除索引 db.col.dropIndex("索引名字") 查找数据 db.列名.find()查询数据总条数 db.列名.find().count() 上面基本是我最常用的了,我们下面实际操作一把。 ...

May 21, 2019 · 6 min · jiezi

Python爬虫入门教程-4100-美空网未登录图片爬取

简介上一篇写的时间有点长了,接下来继续把美空网的爬虫写完,这套教程中编写的爬虫在实际的工作中可能并不能给你增加多少有价值的技术点,因为它只是一套入门的教程,老鸟你自动绕过就可以了,或者带带我也行。 爬虫分析首先,我们已经爬取到了N多的用户个人主页,我通过链接拼接获取到了 http://www.moko.cc/post/da39d... 在这个页面中,咱们要找几个核心的关键点,发现平面拍摄点击进入的是图片列表页面。接下来开始代码走起。 获取所有列表页面我通过上篇博客已经获取到了70000(实际测试50000+)用户数据,读取到python中。 这个地方,我使用了一个比较好用的python库pandas,大家如果不熟悉,先模仿我的代码就可以了,我把注释都写完整。 import pandas as pd# 用户图片列表页模板user_list_url = "http://www.moko.cc/post/{}/list.html"# 存放所有用户的列表页user_profiles = []def read_data(): # pandas从csv里面读取数据 df = pd.read_csv("./moko70000.csv") #文件在本文末尾可以下载 # 去掉昵称重复的数据 df = df.drop_duplicates(["nikename"]) # 按照粉丝数目进行降序 profiles = df.sort_values("follows", ascending=False)["profile"] for i in profiles: # 拼接链接 user_profiles.append(user_list_url.format(i))if __name__ == '__main__': read_data() print(user_profiles)数据已经拿到,接下来我们需要获取图片列表页面,找一下规律,看到重点的信息如下所示,找对位置,就是正则表达式的事情了。 快速的编写一个正则表达式<p class="title"><a hidefocus="ture".*?href="(.*?)" class="mwC u">.*?\((\d+?)\)</a></p> 引入re,requests模块 import requestsimport re# 获取图片列表页面def get_img_list_page(): # 固定一个地址,方便测试 test_url = "http://www.moko.cc/post/da39db43246047c79dcaef44c201492d/list.html" response = requests.get(test_url,headers=headers,timeout=3) page_text = response.text pattern = re.compile('<p class="title"><a hidefocus="ture".*?href="(.*?)" class="mwC u">.*?\((\d+?)\)</a></p>') # 获取page_list page_list = pattern.findall(page_text)运行得到结果 ...

May 21, 2019 · 2 min · jiezi

使用-mongoose-操作-mongodb-增删改查

使用 mongoose 操作 mongodb 的测试文件连接数据库1.1 引入 mongoose1.2 连接指定的数据库(URL 只有数据库是变化的)1.3 获取连接对象1.4 绑定连接完成的监听(用来提示连接成功)得到对应特定集合的 Model2.1 字义 Schema(描述文档结构)2.2 定义 Model(与集合对应,可以操作集合)通过 Model 或其实例对集合数据进行 CRUD 操作3.1 通过 Model 实例的 save() 添加数据3.2 通过 Model 的 find()/findOne() 查询多个或一个数据3.3 通过 Model 的 findByIdAndUpdate() 更新某个数据3.4 通过 Model 的 deleteOne() 删除匹配的数据*/下载mongoose依赖包 npm install --save mongoose 下载md5加密依赖包 npm install --save blueimp-md5下面的代码位置: db_test.js// 引入 md5 依赖const md5 = require('blueimp-md5')1. 连接数据库// 1.1 引入 mongooseconst mongoose = require('mongoose')// 1.2 连接指定的数据库(URL 只有数据库是变化的)mongoose.connect('mongodb://localhost:27017/zp_test', {useNewUrlParser: true})// 1.3 获取连接对象const conn = mongoose.connection// 1.4 绑定连接完成的监听(用来提示连接成功)conn.on('connected', function () { console.log('数据库连接成功!')})2. 得到对应特定集合的 Model// 2.1 字义 Schema(描述文档结构)const userSchema = mongoose.Schema({ username: {type: String, require: true}, // 用户名 password: {type: String, required: true}, // 密码 type: {type: String, required: true} // 用户类型: 求职者/老板})// 2.2 定义 Model(与集合对应,可以操作集合)const UserModel = mongoose.model('users', userSchema) // 集合名: users3. 通过 Model 或其实例对集合数据进行 CRUD 操作 3.1 通过 Model 实例的 save() 添加数据function testSave() { // user 数据对象 const user = { username: 'mandy', password: md5('1234'), type: '求职者' } // 创建 Model 实例 const userModel = new UserModel(user) // 或者像下面这样添加数据 // const userModel = new UserModel({username: 'Tom', password: md5('3333'), type: '老板'}) // 保存到数据库 userModel.save(function (err, user) { console.log('save', err, user) })}// testSave() 3.2 通过 Model 的 find()/findOne() 查询多个或一个数据function testFind() { // 查找多个 UserModel.find(function (err, users) { // 如果有匹配返回一个[user, user...], 如果没有一个匹配的返回[] console.log('find()', err, users) }) // 查找一个 UserModel.findOne({_id: '5cdf99ebf3539334948ae2c8'}, function (err, user) { // 如果有匹配返回的是一个user console.log('findOne()', err, user) })}// testFind() 3.3 通过 Model 的 findByIdAndUpdate() 更新某个数据function testUpdate() { UserModel.findByIdAndUpdate({_id: '5cdfacc55684652a08b49014'}, {username: '一个老板'}, function (err, user) { console.log('findByIdAndUpdate()', err, user) })}// testUpdate() 3.4 通过 Model 的 remove() 删除匹配的数据function testDelete() { UserModel.deleteOne({_id: '5cdf9d761a1c050c70bc0d09'}, function (err, result) { console.log('deleteOne()', err, result) })}// testDelete()运行: ...

May 18, 2019 · 2 min · jiezi

mongo分片集群搭建

mongodb集群搭建方式1.master-slave 主从复制目前官方已不推荐使用 2.Replica Sets 副本集MongoDB 的副本集不同于以往的主从模式。在集群Master故障的时候,副本集可以自动投票,选举出新的Master,并引导其余的Slave服务器连接新的Master,而这个过程对于应用是透明的。可以说MongoDB的副本集,是自带故障转移功能的主从复制。 3.sharding 分片Sharding cluster是一种可以水平扩展的模式,在数据量很大时特给力,实际大规模应用一般会采用这种架构去构建。sharding分片很好的解决了单台服务器磁盘空间、内存、cpu等硬件资源的限制问题,把数据水平拆分出去,降低单节点的访问压力。每个分片都是一个独立的数据库,所有的分片组合起来构成一个逻辑上的完整的数据库。因此,分片机制降低了每个分片的数据操作量及需要存储的数据量,达到多台服务器来应对不断增加的负载和数据的效果 docker-compose搭建mongodb分片集群1.config server集群搭建docker-compose代码csrs1: image: mongo volumes: - /home/work/dbdata/mongo/cs/rs1:/data/db command: mongod --noauth --bind_ip_all --configsvr --replSet csrs --dbpath /data/db csrs2: image: mongo volumes: - /home/work/dbdata/mongo/cs/rs2:/data/db command: mongod --noauth --bind_ip_all --configsvr --replSet csrs --dbpath /data/db csrs3: image: mongo volumes: - /home/work/dbdata/mongo/cs/rs3:/data/db command: mongod --noauth --bind_ip_all --configsvr --replSet csrs --dbpath /data/db集群节点互联// 进入primary节点容器(注意一定要是primary节点), config server 默认端口是27019docker-compose exec csrs1 mongo --port 27019// 初始化副本集rs.initiate()// 将另外两个节点加入到当前的副本集rs.add('csrs2:27019')rs.add('csrs3:27019')// 查看副本集状态rs.status()2.shard server集群搭建docker-compose代码shrs1: image: mongo volumes: - /home/work/dbdata/mongo/sh/rs1:/data/db command: mongod --noauth --bind_ip_all --dbpath /data/db --shardsvr --replSet shrs shrs2: image: mongo volumes: - /home/work/dbdata/mongo/sh/rs2:/data/db command: mongod --noauth --bind_ip_all --dbpath /data/db --shardsvr --replSet shrs shrs3: image: mongo volumes: - /home/work/dbdata/mongo/sh/rs3:/data/db command: mongod --noauth --bind_ip_all --dbpath /data/db --shardsvr --replSet shrs集群节点互联// 进入primary节点容器(注意一定要是primary节点), shard server 默认端口是27018docker-compose exec shrs1 mongo --port 27018// 初始化副本集rs.initiate()// 将另外两个节点加入到当前的副本集rs.add('shrs2:27018')rs.add('shrs3:27018')// 查看副本集状态rs.status()3.router mongos 集群搭建docker-compose代码mongo: image: mongo command: mongos --noauth --bind_ip_all --configdb csrs/csrs1:27019,csrs2:27019,csrs3:27019将mongos连接到shard set// 进入primary节点容器(注意一定要是primary节点), mongos 默认端口是27017docker-compose exec mongo mongo --port 27017// 将另外两个节点加入到当前的副本集sh.addShard('shrs1:27018')// 查看副本集状态sh.status()3.mongo-express 图形化界面安装docker-compose代码 mongo-express: image: mongo-express restart: always ports: - 10081:8081浏览器连接GUI客户端localhost:10081

May 16, 2019 · 1 min · jiezi

PyMongo-基础操作指令

1. 创建连接Connectionimport pymongoconn = pymongo.MongoClient('localhost',27017) # 安装完成启动服务后,默认本地连接conn = pymongo.MongoClient()默认上述地址以及端口conn = pymongo.MongoClient() #连接数据库db = conn['like_name'] #获取名字的数据库col = db['name_list'] #获取名字的集合2. 查看全部聚集名称db.collection_names()3.查看聚集的一条记录db.Account.find_one()db.Account.find_one({"UserName":"keyword"})4.查看聚集的记录统计db.Account.find().count()5.聚集查询结果排序db.Account.find().sort("UserName") --默认为升序db.Account.find().sort("UserName",pymongo.ASCENDING) --升序db.Account.find().sort("UserName",pymongo.DESCENDING) --降序6.聚集查询结果多列排序db.Account.find().sort([("UserName",pymongo.ASCENDING),("Email",pymongo.DESCENDING)])7.添加记录db.Account.insert({"AccountID":21,"UserName":"baiyun"})8.修改记录db.Account.update({"UserName":"baiyun"},{"$set":{"Email":"zte_bjc@126.com","Password":"666"}})9.删除记录db.Account.remove() -- 全部删除db.Test.remove({"UserName":"keyword"}) -- 按照条件删除10.原子操作常用命令$set用来指定一个键并更新键值,若键不存在并创建。 { $set : { field : value } }$unset 用来删除一个键 { $unset : { fiel- d : 1} }$inc $inc可以**对文档的某个值为数字型(只能为满足要求的数字)的键进行增减的操作。 { $inc : { field : value } }$push 用法:{ $push : { field : value } }把value追加到field里面去,field一定要是数组类型才行,如果field不存在,会新增一个数组类型加进去。$pushAll 同$push,只是一次可以追加多个值到一个数组字段内。 { $pushAll : { field : value_array } }$pull ...

May 13, 2019 · 1 min · jiezi

DOClever安装以及使用介绍

DOClever被赞为目前最好用的接口管理平台,强大之处在哪?试他一试。一、什么是DOClever?官网地址:http://doclever.cn/controller...DOClever与目前postman、swagger不同之处在于,不仅仅能满足接口文档开发、测试、数据mock等,还更轻量级,也对postman、swagger、RAP支持导入。注意:以下来自官网拷贝!(^▽^) DOClever是一个可视化免费开源的接口管理工具 ,可以分析接口结构,校验接口正确性, 围绕接口定义文档,通过一系列自动化工具提升我们的协作效率。DOClever前后端全部采用了javascript来作为我们的开发语言,前端用的是vue+element UI,后端是express+mongodb,这样的框架集成了高并发,迭代快的特点,保证系统的稳定可靠。主要特性:• 可以对接口信息进行编辑管理,支持 get,post,put,delete,patch 五种方法,支持 https 和 https 协议,并且支持 query,body,json,raw,rest,formdata 的参数可视化编辑。同时对 json 可以进行无限层次可视化编辑。并且,状态码,代码注入,markdown 文档等附加功能应有尽有。• 接口调试运行,可以对参数进行加密,从 md5 到 aes 一应俱全,返回参数与模型实时分析对比,给出不一致的地方,找出接口可能出现的问题。如果你不想手写文档,那么试试接口的数据生成功能,可以对接口运行的数据一键生成文档信息。• mock 的无缝整合,DOClever 自己就是一个 mock 服务器,当你把接口的开发状态设置成已完成,本地 mock 便会自动请求真实接口数据,否则返回事先定义好的 mock 数据。• 支持 postman,rap,swagger 的导入,方便你做无缝迁移,同时也支持 html 文件的导出,方便你离线浏览!• 项目版本和接口快照功能并行,你可以为一个项目定义 1.0,1.1,1.2 版本,并且可以自由的在不同版本间切换回滚,再也不怕接口信息的遗失,同时接口也有快照功能,当你接口开发到一半或者接口需求变更的时候,可以随时查看之前编辑的接口信息。• 自动化测试功能,目前市面上类似平台的接口自动化测试大部分都是伪自动化,对于一个复杂的场景,比如获取验证码,登陆,获取订单列表,获取某个特定订单详情这样一个上下文关联的一系列操作无能为力。而 DOClever 独创的自动化测试功能,只需要你编写极少量的 javascript 代码便可以在网页里完成这样一系列操作,同时,DOClever 还提供了后台定时批量执行测试用例并把结果发送到团队成员邮箱的功能,你可以及时获取接口的运行状态。• 团队协作功能,很多类似的平台这样的功能是收费的,但是 DOClever 觉得好东西需要共享出来,你可以新建一个团队,并且把团队内的成员都拉进来,给他们分组,给他们分配相关的项目以及权限,发布团队公告等等。二、DOClever环境依赖以及使用DOClever的使用,依赖nodejs和MongoDB,注意,这里的安装都是在windows系统上!(^▽^)1、安装nodejs去官网下载nodejs:https://nodejs.org/en/download/ 选择windows版本64位下载,下载完成后双击msi文件安装 至此,安装完成!win+r 输入cmd 表示安装成功!!(^▽^)PS:如果想配置环境变量等,可以参考此文:https://www.cnblogs.com/liuqi... 2、安装MongoDB去官网下载MongoDB:https://www.mongodb.com/downl... 选择windows版本64位下载,下载完成后双击msi文件安装 选择自定义路径 ...

May 12, 2019 · 1 min · jiezi

The-default-storage-engine-wiredTiger

win7 32位系统在安装mongodb数据库时遇到问题。 2016-01-05T17:44:48.381+0800 I STORAGE  [initandlisten] exception in initAndListen: 28663 Cannot start server. The default storage engine 'wiredTiger' is not available with this build of mongod. Please specify a different storage engine explicitly, e.g. --storageEngine=mmapv1., terminating根据错误提示得知是由于当前数据库引擎不支持32系统所导致的。 解决这个问题很简单,只要切换下MongoDB的默认数据库引擎即可,当然最好是升级到64位系统,毕竟没有2GB的限制而且新引擎的性能也有很大的提升。 $ mongod -dbpath "d:\mongodb\data" -storageEngine=mmapv1

May 11, 2019 · 1 min · jiezi

webflux-用户管理界面

一个简单的用户管理的CRUD已经完成,现在我们需要在页面上展示,方便用户管理。尽管现在已经流行前后分离开发,但是在一些小公司做的项目并不需要前端开发人员,页面也是后端开发在写。所以这次我们使用thymeleaf来开发页面。1 集成thymeleafpom文件依赖: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency>开启thymeleaf spring.thymeleaf.enabled=true2 创建资源目录在resources目录下创建templates和static目录,templates下放你的html页面,static下放你的css以及js。这些目录是thymeleaf默认的,如果需要修改成别的目录,可以自行配置。感觉没必要修改了。 去bootstrap网站下载生产环境的文件放在static目录下,这样我们写页面就不用关心样式。 3 创建首页controller @Controllerpublic class Index { @GetMapping("/") public String index(){ return "index"; }}页面 首页: <!DOCTYPE html><html lang="zh-CN"><head><meta charset="UTF-8" /><title>欢迎页面</title><link rel="stylesheet" href="/css/bootstrap.css" /></head><body> <div class="container"> <nav class="navbar navbar-default"> <div class="container-fluid"> <!-- Brand and toggle get grouped for better mobile display --> <div class="navbar-header"> <a class="navbar-brand" href="#">Mike Study</a> </div> <!-- Collect the nav links, forms, and other content for toggling --> <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1"> <ul class="nav navbar-nav"> <li class="active"><a href="#">首页</a></li> <li><a href="#">实战课程</a></li> </ul> <form class="navbar-form navbar-left"> <div class="form-group"> <input type="text" class="form-control" placeholder="你想学点啥?"> </div> <button type="submit" class="btn btn-default">Go</button> </form> <ul class="nav navbar-nav navbar-right"> <li><a href="#">上班摸鱼</a></li> <li><a href="#">下班充电</a></li> </ul> </div> <!-- /.navbar-collapse --> </div> <!-- /.container-fluid --> </nav> <div class="row"> <div class="jumbotron"> <h1>案例上手 Spring Boot WebFlux!</h1> <p>本课程是一个系列基础教程,目标是带领读者上手实战,课程以新版本 Spring Boot 2.0 WebFlux 的核心概念作为主线。围绕 Spring Boot 2.0 WebFlux 技术栈的系列教程,目标是带领读者了解 Spring Boot 2.0 WebFlux 各种特性,并学会使用 Spring Boot 相关技术栈上手开发项目。</p> <blockquote> <p>只有不断地学习才能进步</p> <footer> Mike <cite title="Source Title">Liu</cite> </footer> </blockquote> </div> </div> <div class="row"> <div class="col-md-3"> <img src="https://img.mukewang.com/55f8d5080001293c06000338-240-135.jpg" alt="..." style="height: 140px; width: 100%; display: block;" class="img-thumbnail"> <a href="/users"><p>mongodb开发用户管理系统<span class="label label-danger">mongodb</span></p></a> <p>发布时间:<i class="glyphicon glyphicon-calendar"></i>2019-01-01</p> </div> <div class="col-md-3"> <img src="https://img.mukewang.com/55f8d5080001293c06000338-240-135.jpg" alt="..." style="height: 140px; width: 100%; display: block;" class="img-thumbnail"> <p>mongodb开发用户管理系统<span class="label label-danger">mongodb</span></p> <p>发布时间:<i class="glyphicon glyphicon-calendar"></i>2019-01-01</p> </div> <div class="col-md-3"> <img src="https://img.mukewang.com/55f8d5080001293c06000338-240-135.jpg" alt="..." style="height: 140px; width: 100%; display: block;" class="img-thumbnail"> <p>mongodb开发用户管理系统<span class="label label-danger">mongodb</span></p> <p>发布时间:<i class="glyphicon glyphicon-calendar"></i>2019-01-01</p> </div> <div class="col-md-3"> <img src="https://img.mukewang.com/55f8d5080001293c06000338-240-135.jpg" alt="..." style="height: 140px; width: 100%; display: block;" class="img-thumbnail"> <p>mongodb开发用户管理系统<span class="label label-danger">mongodb</span></p> <p>发布时间:<i class="glyphicon glyphicon-calendar"></i>2019-01-01</p> </div> </div> <div class="row"> <div class="col-md-3"> <img src="https://img.mukewang.com/55f8d5080001293c06000338-240-135.jpg" alt="..." style="height: 140px; width: 100%; display: block;" class="img-thumbnail"> <p>mongodb开发用户管理系统<span class="label label-danger">mongodb</span></p> <p>发布时间:<i class="glyphicon glyphicon-calendar"></i>2019-01-01</p> </div> <div class="col-md-3"> <img src="https://img.mukewang.com/55f8d5080001293c06000338-240-135.jpg" alt="..." style="height: 140px; width: 100%; display: block;" class="img-thumbnail"> <p>mongodb开发用户管理系统<span class="label label-danger">mongodb</span></p> <p>发布时间:<i class="glyphicon glyphicon-calendar"></i>2019-01-01</p> </div> <div class="col-md-3"> <img src="https://img.mukewang.com/55f8d5080001293c06000338-240-135.jpg" alt="..." style="height: 140px; width: 100%; display: block;" class="img-thumbnail"> <p>mongodb开发用户管理系统<span class="label label-danger">mongodb</span></p> <p>发布时间:<i class="glyphicon glyphicon-calendar"></i>2019-01-01</p> </div> <div class="col-md-3"> <img src="https://img.mukewang.com/55f8d5080001293c06000338-240-135.jpg" alt="..." style="height: 140px; width: 100%; display: block;" class="img-thumbnail"> <p>mongodb开发用户管理系统<span class="label label-danger">mongodb</span></p> <p>发布时间:<i class="glyphicon glyphicon-calendar"></i>2019-01-01</p> </div> </div> </div></body></html> ...

May 9, 2019 · 4 min · jiezi

WebFlux-集成MongoDb

上一篇文章中我们已经简单搭建了webflux的框架,今天就集成mongodb完成一个用户管理系统。1. 安装MongoDb直接去官网下载安装包:https://www.mongodb.com/downl... 选择对应的操作系统,我的是windows,然后选择zip,还是msi。我下载的zip也就是绿色免安装。如果msi的就直接下一步,下一步按要求安装就好了。zip启动方式:到bin目录下打开cmd命令窗口 运行: mongod.exe --dbpath C:\Tools\mongodb\dbdbpathshi 是设置数据备份目录,必须要设置,否则启动不了。 bin目录下的 mongo.exe是mongodb的查询客户端,可以执行查询操作。一些查询命令可以直接去官网看。show dbs:显示当前所有文档库,相当于数据库use test:选择test库db.user.find():查询所有user文档数据db.user.drop():删除所有user文档 2.集成mogodbpom文件依赖: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-mongodb-reactive</artifactId> </dependency>配置连接: spring.data.mongodb.host=localhostspring.data.mongodb.database=testspring.data.mongodb.port=270173.dao层package com.mike.dao;import org.springframework.data.mongodb.repository.ReactiveMongoRepository;import org.springframework.stereotype.Repository;import com.mike.po.User;/** * The class UserDao.java */@Repositorypublic interface UserDao extends ReactiveMongoRepository<User, String>{}ReactiveMongoRepository 已经帮你实现了增删该查,如果需要别的方法,需要自己添加实现接口。具体写法和JPA类似 4.service层package com.mike.service;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.data.mongodb.core.MongoTemplate;import org.springframework.data.mongodb.core.query.Criteria;import org.springframework.data.mongodb.core.query.Query;import org.springframework.stereotype.Service;import com.mike.dao.UserDao;import com.mike.po.User;import reactor.core.publisher.Flux;import reactor.core.publisher.Mono;/** * The class UserService.java */@Servicepublic class UserService { @Autowired private UserDao userDao; @Autowired private MongoTemplate mongoTemplate; public Mono<User> saveOrUpdateUser(User user){ return userDao.save(user); } public Mono<User> findById(String id){ return userDao.findById(id); } public Flux<User> findAll(){ return userDao.findAll(); } public void deleteById(String id){ // 使用mongoTemplate来做删除 直接使用提供的删除方法不行 Query query = Query.query(Criteria.where("id").is(id)); mongoTemplate.remove(query, User.class); //userDao.deleteById(id); 这样无法删除,不知道为什么 }}5.controller层package com.mike.controller;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Controller;import org.springframework.ui.Model;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.PathVariable;import org.springframework.web.bind.annotation.PostMapping;import org.springframework.web.bind.annotation.RequestBody;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.ResponseBody;import com.mike.po.User;import com.mike.service.UserService;import reactor.core.publisher.Flux;import reactor.core.publisher.Mono;/** * The class UserController.java */@Controllerpublic class UserController { @Autowired private UserService userService; @PostMapping("/user") public String save(User user,final Model model){ Mono<User> u = userService.saveOrUpdateUser(user); model.addAttribute("user", u); return "redirect:/users"; } @GetMapping("/user/find/{id}") @ResponseBody public Mono<User> find(@PathVariable("id") String id){ return userService.findById(id); } @GetMapping("/users") public String findAll(final Model model){ Flux<User> users= userService.findAll(); model.addAttribute("users", users); return "user"; } @GetMapping("/user/delete/{id}") public String delete(@PathVariable("id") String id){ userService.deleteById(id); return "redirect:/users"; }}6.po层package com.mike.po;import org.springframework.data.annotation.Id;import org.springframework.data.mongodb.core.mapping.Field;/** * The class User.java */public class User { @Id @Field("_id") private String id; private String name; private String sex; private String job; private String address; private String phone; /** * @return the id */ public String getId() { return id; } /** * @param id the id to set */ public void setId(String id) { this.id = id; } /** * @return the name */ public String getName() { return name; } /** * @param name the name to set */ public void setName(String name) { this.name = name; } /** * @return the sex */ public String getSex() { return sex; } /** * @param sex the sex to set */ public void setSex(String sex) { this.sex = sex; } /** * @return the job */ public String getJob() { return job; } /** * @param job the job to set */ public void setJob(String job) { this.job = job; } /** * @return the address */ public String getAddress() { return address; } /** * @param address the address to set */ public void setAddress(String address) { this.address = address; } /** * @return the phone */ public String getPhone() { return phone; } /** * @param phone the phone to set */ public void setPhone(String phone) { this.phone = phone; } }7. 总结和正常的关系型数据库的操作一样,只不过有些问题想不明白。我上面的删除旧出现了无法删除的问题,最后使用的mongodbTemplate完成的,这是个同步操作。为什么会出现这样的问题呢?这就是响应式编程的坑,如果你不理解就会出现问题。增删改查完了,但是没有页面展示,写一篇写页面。 ...

May 9, 2019 · 2 min · jiezi

VueCliNodemongodb打造个人博客含前台展示及后台管理系统下

前文上篇:https://segmentfault.com/a/11...中篇:https://segmentfault.com/a/11... github地址:https://github.com/ssevenk/ss... 现在只剩下把东西展示出来了 页面 这里有四种页面(其实是四个组件):文章,杂谈,收藏,具体的文章或杂谈前三个虽然布局一样,但功能有细微差别,同时考虑到以后可能要针对不同种类做不同的布局方法我还是定义了三个组件ShowBlogs、ShowEssays、ShowArticles以及具体的那个TheOne 可以看到它们共用一个抬头的logo和导航栏所以我把这一块部分写进front组件里剩下的部分用路由决定展示哪一个组件 使用嵌套路由 数据展示这个很简单,就在最开始请求后端获取到数据后 created() { this.getData(); },methods: {getData() { this.$axios.get("/data/blog").then(res => { this.blogs = res.data; this.show = this.blogs; });}用v-for将router-link循环出来每一个数据都是一个link,可以跳转到具体的内容页面 <router-link class='ShowBlogs-blog' :to="{ name:'TheOne',params:{ kind:'blog',id:item._id }}" tag="li" v-for="item in show.slice((currentPage-1)*pageSize,currentPage*pageSize)" :key="item._id" > <span>{{ item.date }}</span> {{ item.title }} </router-link>分页在上面的代码中你应该注意到了,看到了一个很熟悉的东西 show.slice((currentPage-1)*pageSize,currentPage*pageSize)这是我们在后台管理系统中用过的分页这里不再赘述 搜索不过搜索功能有点和后台管理不一样这一次我定义了一个show数组点击搜索之后,调用函数来进行搜索,把搜索出来的结果存放在show中所以我们展示的一直都是show数组 由于三个页面都用到了搜索框所以我把搜索框单独做成了一个组件并没有引入到main.js中使其成为全局组件因为我们希望它是作为ShowBlogs、ShowEssays、ShowArticles这三个组件的子组件存在的,方便调用父组件提供的方法 import mySearch from "./mySearch";每个组件都引入一次点击搜索时,向父组件发射搜索框里的内容,并调用父组件的方法 //mySearch.vue methods:{ search() { this.$emit('search',this.content) } }在父组件中 <mySearch @search="searchfor"></mySearch>methods:{searchfor(s) { this.show = (this.blogs.filter(item => { if (item.title.includes(s)) { return item; } }));}}针对每个组件,搜索框的颜色不一样这里用$route.path来判断写在搜索框的组件里 ...

May 9, 2019 · 1 min · jiezi

VueCliNodemongodb打造个人博客含前台展示及后台管理系统中

前言前文:VueCli+Node+mongodb打造个人博客(含前台展示及后台管理系统)(上)https://segmentfault.com/a/11... github地址:https://github.com/ssevenk/ss... 在上篇文章中我们完成了后端的配置,实现了对数据的增删查改现在只需要前端页面发送对应的请求给后端即可 引入axios在开始搭建组件前,我们先要确定前端异步请求的方式这里我用的是axios先在main.js中将其引入 import axios from 'axios'Vue.prototype.$axios = axios;这样我们就可以在自定义的组件中,直接用this.$axios来发起异步请求 后台管理系统 这里我把它分成了两个组件管理组件(Manage.vue)以及编辑组件(Edit.vue) 管理组件(Manage.vue)该组件的页面显示如图 核心部分就是那个表格了用来展示已经存在的数据并对其进行操作 左侧导航分成了三个类别,文章,杂谈和收藏当我们点击对应的类别时,并没有在切换组件,而是在更新数据在Manage.vue中我们定义一个名叫things的空数组,来保存当前需要显示的数据以及一个kind值,来保存当前需要显示的数据种类我们先令kind值为blog,默认显示 “文章” 数据 data() {return { kind: "blog", things: [], }当组件初始化时,调用生命周期函数created()向后端发起对应种类的请求后端返回对应的数据,存进things里面 created() { this.getData("blog"); //第一次默认先获取文章的数据 }, methods: { getData(kind) { this.kind = kind; this.$axios.get(`/data/${kind}`) .then(res => { this.things = res.data; }) }每次点击左侧的导航栏上的按钮,就调用一次getData(),并传入对应的kind参数由此来更新things和kind更新要显示的数据和种类比如点击杂谈的话,就调用getData(essay) 数据表格这里我用了Element-ui的组件库来制作表格基本上就是官方案例 可以看到跟我的效果基本上一样所以这部分可以直接参考官网教程https://element.eleme.cn/#/zh...在数据绑定的时候,选择我们之前建好的things数组 :data='things'搜索右上角有一个搜索输入框输入后可以即时显示搜索的结果在数据表格里一开始我想新定义一个show数组,来存放搜索后的结果然后给输入框绑定键盘事件来调用搜索函数但后来发现Element官网的做法异常简单可以直接在el-table表格绑定的数据上做文章 :data="((things.filter(data=>!search||data.title.toLowerCase().includes(search.toLowerCase())“其中search是双向绑定在输入框上的数据 分页数据太多了肯定需要分页还是用到了我们的Element-ui组件库 <el-pagination @current-change="handleCurrentChange" :page-size="pageSize" :current-page="currentPage" :total=" things.length" layout="total, prev, pager, next" ></el-pagination>PageSize设置为5,当前默认页为1 data() {return { kind: "blog", things: [], currentPage: 1, pageSize: 5, search: ""};}当点击页码切换的时候,把页码更新 ...

May 9, 2019 · 2 min · jiezi

零基础入门Python数据分析只需要看懂这一张图附下载链接

摘要在做数据分析的过程中,经常会想数据分析到底是什么?为什么要做数据数据分析?数据分析到底该怎么做?等这些问题。对于这些问题,一开始也只是有个很笼统的认识。 最近这两天,读了一下早就被很多人推荐的《谁说菜鸟不会数据分析》这本书。发现对这些问题讲的还是比较透彻,随后对这本书的核心内容做了一个笔记。 说明:笔记主要以思维导图的方式呈现。 目录 一、数据分析概述数据分析指用适当的统计分析方法对收集来的大量数据进行分析,将它们加以汇总和理解并消化,以求最大化地开发数据的功能,发挥数据的作用。 二、确定分析目的和思路主要讲数据分析的方法论,如何利用管理学、营销学等知识从宏观上指导数据分析的过程,为的是明确分析的目的和思路,以免南辕北辙,分析了很久却不能解决要面临的问题。 其中PEST用于对行业的宏观环境分析,5W2H可以用于用户的行为、业务问题分析、逻辑树可以将某业务问题拆分为子问题进行专题分析、4P是一种营销理论,用来信进行公司产品的运营情况分析、用户行为分析可以用来分析各级指标之间的逻辑关系。 三、如何准备数据“巧妇难为无米之炊”。数据就好比谚语中的米。做为数据分析师更是应该知道“米”的两个方面。第一:“米的构造”「理解数据」,第二“米”从哪里「数据来源」。 四、数据预处理在做数据分析之前需要将数据清洗、加工、转换等一些步骤以使得数据成为可以用于建模分析的规整数据。 五、数据分析方法(微观)当完成数据的处理之后,现在需要真正的从细节上对数据进行分析,并且在微观上也有一套比较固定的数据分析方法可供参考使用。 六、可视化图表及美化当数据分析的工作完成之后,需要对分析结果进行可视化展示及美化。 七、数据分析报告数据分析报告是对整个数据分析过程的一个总结和呈现。通过报告,把数据分析的起因、过程、及结果完整的展现出来、提供给决策者参考。 八、总结以上是对整本书做的一个核心内容精炼,并生成了完整版思维导图及内容的PDF版本。由于长图过大,利用动图展示: 完整版内容gif: 完整版思维导图gif: 以上Pdf内容及完整版思维导图获取: 1、关注公众号「Python专栏」,后台回复:菜鸟数据分析,即可获得完整版pdf。

May 7, 2019 · 1 min · jiezi

mongoDB获取范围内数据删除不符合条件的数据聚合计算均值

查找一个时间范围的数据db['Copy(1)_of_20180401'].find({'t':{$gte:'2018-04-01 20:00:00'},'t':{$lte:'2018-04-01 21:00:00'}})取反后删除db['Copy(1)_of_20180401'].remove({'t':{$not:{$lte:'2018-04-01 22:00:00'}}})聚合计算平均值db['Copy(1)_of_20180401'].aggregate({"$group":{_id: 'avg',avg_value:{"$avg":"$uf"}}});这篇文章写的很全面:https://blog.csdn.net/zwq9123...

May 3, 2019 · 1 min · jiezi

使用pymongo解析文本格式日志后放入mongo

思路拿到的文本是二进制的,在查看文件的时候可以加上-rb以二进制读的形式打开。 文件格式如下: b’2019-03-29 10:55:00\t0\192.168.31.123:8080\t5\t12345678\t0\t44560\t953864\t2\t192.168.31.123\tmd5\n’可以看到日志里面的分隔符是制表符t,行末尾有换行符n 处理日志文件的基本逻辑 链接数据库逐行读txt将一行的数据分割为数组将数据放入list将list逐条转化为json存入数据库分片集群的mongo链接用于存储日志的mongo集群有三个分片:flux-stat-1.mongo:27017,flux-stat-2.mongo:27017和flux-stat-3.mongo:27017. 假设用户名是flux-stat,密码是flux-stat-mongo123,登录方法: from pymongo import MongoClientimport urllib.parse#创建MongoDB链接对象username = urllib.parse.quote_plus(‘flux-stat’)password = urllib.parse.quote_plus(‘flux-stat-mongo123’)uri = 'mongodb://%s:%s@flux-stat-1.mongo:27017, flux-stat-2.mongo:27017, flux-stat-3.mongo:27017/admin' % (username,password)client = MongoClient(uri)注意:username和password必须要进行转换,否则链接不正确 pymongo逐行读取日志文本with open(“d:/user/ServerLog/test.txt”,’rb’) as file: for line in file: #这里放操作file.close()分割日志内容转化为json,并把每行json放到数组中#引入json模块import jsontotal=[]logList = line.split(‘\t’) #line分割为listkeyList = [‘time’,’status’,’ip’,’type’,’userid’,’v1’,’v2’,’v3’,’v4’,’ip’,’md5’] #key的listlogDict = dict(zip(keyList, logList)) #把二者以字典的方式拼起来logJson = json.dumps(logDict) #把字典转换为string形式的json#json转字典的方法为dictinfo = json.loads(json_str)total.append(logJson)日志存入mongoinsert_one和insert_many是两种插入的方式,但是采用insert_one进行逐条插入会进行大量的访问,大幅降低插入效率,采用insert_many进行json为基本单位的list批量插入,效率明显提高。 db = client['log'] #获取数据库coll = db[‘data’] #获取表coll.insert_many(total) #插入到数据库

May 2, 2019 · 1 min · jiezi

PHP7-mongoDB扩展使用

最近在做的项目需要将PHP5.6升级到PHP7.0,使用过PHP-mongo扩展的同学应该知道,PHP7.0的mongodb扩展是完全不兼容PHP5.6的mongo扩展的,php-mongodb改如何使用呢。 下面直接说明各种方法的使用: 1.mongodb连接:private function connect($confArr) { try{ $connStr = "mongodb://" . $confArr['host'] . ":" . $confArr['port'] . "/" . $confArr['db_name']; $options = array( 'username' => $confArr['username'], 'password' => $confArr['password'], 'readPreference' => $confArr['read_preference'], 'connectTimeoutMS' => intval($confArr['connect_timeout_ms']), 'socketTimeoutMS' => intval($confArr['socket_timeout_ms']), ); $mc = new MongoDB\Driver\Manager($connStr, $options); return $mc; } catch(Exception $e){ return false; }}2.查询find:public function find($query = array(), $fields = array(), $collection, $sort = array(), $limit = 0, $skip = 0) { $conn = $this->connect(); if (empty($conn)) { return false; } try { $data = array(); $options = array(); if (!empty($query)) { $options['projection'] = array_fill_keys($fields, 1); } if (!empty($sort)) { $options['sort'] = $sort; } if (!empty($limit)) { $options['skip'] = $skip; $options['limit'] = $limit; } $mongoQuery = new MongoDB\Driver\Query($query, $options); $readPreference = new MongoDB\Driver\ReadPreference(MongoDB\Driver\ReadPreference::RP_SECONDARY); $cursor = $conn->executeQuery($collection, $mongoQuery, $readPreference); foreach($cursor as $value) { $data[] = (array)$value; } return $data; } catch (Exception $e) { //记录错误日志 } return false;}3.插入操作insert:public function insert($addArr, $collection) { if (empty($addArr) || !is_array($addArr)) { return false; } $conn = $this->connect(); if (empty($conn)) { return false; } try { $bulk = new MongoDB\Driver\BulkWrite(); $bulk->insert($addArr); $writeConcern = new MongoDB\Driver\WriteConcern(MongoDB\Driver\WriteConcern::MAJORITY, 6000); $result = $conn->executeBulkWrite($collection, $bulk, $writeConcern); if ($result->getInsertedCount()) { return true; } } catch (Exception $e) { //记录错误日志 } return false;}4.删除delete:public function delete($whereArr, $options = array(), $collection) { if (empty($whereArr)) { return false; } if (!isset($options['justOne'])) { $options = array( 'justOne' => false, ); } $conn = $this->connect(); if (empty($conn)) { return false; } try { $bulk = new MongoDB\Driver\BulkWrite(); $bulk->delete($whereArr, $options); $writeConcern = new MongoDB\Driver\WriteConcern(MongoDB\Driver\WriteConcern::MAJORITY, 30000); $result = $conn->executeBulkWrite($collection, $bulk, $writeConcern); return true; } catch (Exception $e) { //记录错误日志 } return false;}5.执行command操作:private function command($params, $dbName) { $conn = $this->connect(); if (empty($conn)) { return false; } try { $cmd = new MongoDB\Driver\Command($params); $result = $conn->executeCommand($dbName, $cmd); return $result; } catch (Exception $e) { //记录错误 } return false;}6.统计count:public function count($query, $collection) { try { $cmd = array( 'count' => $collection, 'query' => $query, ); $res = $this->command($cmd); $result = $res->toArray(); return $result[0]->n; } catch (Exception $e) { //记录错误 } return false;}7.聚合distinct:public function distinct($key, $where, $collection) { try { $cmd = array( 'distinct' => $collection, 'key' => $key, 'query' => $where, ); $res = $this->command($cmd); $result = $res->toArray(); return $result[0]->values; } catch (Exception $e) { //记录错误 } return false;}8.aggregate操作:public function aggregate($where, $group, $collection) { try { $cmd = array( 'aggregate' => $collection, 'pipeline' => array( array( '$match' => $where, ), array( '$group' => $group, ), ), 'explain' => false, ); $res = $this->command($cmd); if (!$res) { return false; } $result = $res->toArray(); return $result[0]->total; } catch (Exception $e) { //记录错误 } return false;}

April 30, 2019 · 2 min · jiezi

亚马逊又挂了只是因为半价清仓活动

昨天亚马逊又挂了,为什么是又呢,因为每年亚马逊都要挂几次。 昨天是什么日子让亚马逊又挂了呢?不就是因为清仓促销吗……你的骄傲呢,高可用呢,负载均衡呢,分布式呢,三驾马车怎么一驾都不管用了呢? 不就是在国内弄个促销么……不就是被羊毛党盯上了么……至于么你,一挂就是几个小时,我反正在下午2点看看是上不去了,下午4点多还是不行。你再看看我们的淘宝,差距啊,知道自己为什么清仓了么。 行业里有句话是这么形容的:亚马逊的黑五就是淘宝的日常。 有一说一,淘宝的1111真的是全球独一档,后面的技术栈我不是阿里人就不瞎吹了,但是技术能力绝对是第一档。唯一。 这就不得不吹一波mongodb了,我最爱的数据库,没有之一。高可用、分布式样样有,样样精。关键时候就能看出一个数据库的健壮程度了。 从技术角度出发,电商领域一定会有非常多的关系型、非关系型、结构化、半结构化的数据,那么这些数据在大并发上来的时候如何有效的去做复杂场景的兼容,就要看程序员们的了。 当今互联网的宠儿,时代下应运而生的代名词:大数据,你们一定多少听过点。那请问,你们都知道有哪些大数据时代下的产物吗? Hadoop系列?Python?Dashboard?如果你能说出这些,说明你和其他人都差不多吗,能再多说点吗? 给大家说一个新名词:数据中台。什么是数据中台,是可以做数据实时汇聚的平台。这才是大数据时代下,每家机构单位都应该上的大数据产品!而小胖反观一圈市面上的所有大数据产品,别看那么多花里胡哨的包装介绍,最后脱掉衣服看本质,就是个hadoop改造啊。 那Hadoop系列最大的问题是什么知道吗?就是离线计算,我们行话叫T+1计算,在当今这个大数据时代下,数据就是价值,现在就看哪家企业能够把手里的数据变现,变现的方式有很多。但是针对不同的业务场景,去实现起来,代价可不小。 而目前市面上就有那么一款真正的数据中台产品,它可以做到数据的实时采集,而更令我惊喜的是在整个采集的过程中,他竟然支持各种关系型、非关系型数据源,多表关联,数据质量校验,数据建模,数据清洗,数据过滤等功能。 也就是说,当数据从源端落地到目标端的时候,数据已经按照既定的规则全部汇聚好了。这得省多少功夫啊。 那你肯定要杠我了,说这不就是个ETL么,你知道ETL的效率吗?我用下来kettle的效率在几百OPS反正,而同样的机器,这款产品的OPS可以达到2-3w!这还是普通配置的情况下,根据官网给出的数据,7-8w 的OPS是可以保证的。 说了那么多,也不给大家卖关子了,这个产品的名字叫:Tapdata,为了方便大家工作,贴个官网:http://www.tapdata.io 我是通过他们免费的云版了解到的,虽然是个阉割版,但是数据采集功能真的很吸引我,帮我解决了不少困难。也贴个福利给大家:https://cloud.tapdata.io Tapdata和普通大数据产品的区别是什么呢?我从他们架构师那里要来一张图,给大家分享下,你看了就明白了: 如果你对数据中台感兴趣的话,可以直接去联系这个架构师,或者他们的产品经理,这个人就是:我

April 30, 2019 · 1 min · jiezi

从项目中由浅入深的学习koa-mongodb4

序列文章从项目中由浅入深的学习vue,微信小程序和快应用 (1)从项目中由浅入深的学习react (2)从项目中由浅入深的学习typescript (3) 前言node.js的出现前端已经可以用js一把梭,从前端写到后台。本文从后台利用node的框架koa+mongodb实现数据的增删改查和注册接口,前端利用umi + dva +ant-design-pro来实现数据渲染。实现一个小全栈project,就是so-easy1.效果图react-koa 全栈项目,欢迎star 2.技术栈koa:node框架koa-bodyparser:解析body的中间件koa-router :解析router的中间件mongoose :基于mongdodb的数据库框架,操作数据nodemon:后台服务启动热更新 3.项目目录├── app // 主项目目录│ ├── controllrts // 控制器目录(数据处理)│ │ └── ... // 各个表对应的控制器│ ├── middleware // 中间件目录│ │ └── resFormat.js // 格式化返回值│ ├── models // 表目录(数据模型)│ │ ├── course.js // 课程表│ │ └── user.js // 用户表│ └── utils // 工具库│ │ ├── formatDate.js // 时间格式化│ │ └── passport.js // 用户密码加密和验证工具├── db-template // 数据库导出的 json 文件├── routes // 路由目录│ └── api // 接口目录│ │ ├── course_router.js // 课程相关接口│ │ └── user_router.js // 用户相关接口├── app.js // 项目入口└── config.js // 基础配置信息 ...

April 26, 2019 · 1 min · jiezi

mongodb

本文分两部分,分布式和单机。单个db的存储引擎,物理和数据存储简介,事务实现等。分布式架构,分布式涉及的复制集,分片等可靠性和扩展性保障。 第一部分 单机存储引擎介绍引擎wiredTiger(简称WT)支持行存储、列存储以及LSM等3种存储形式,Mongodb使用时,只是将其作为普通的KV存储引擎来使用,mongodb的每个集合对应一个WT的table,table里包含多个Key-value pairs,以B树形式存储。mongodb的集合和索引都对应一个wiredTiger的table。并依赖于wiredTiger提供的checkpoint + write ahead log机制提供高数据可靠性,目前支持单机事务。按照Mongodb默认的配置,WiredTiger的写操作会先写入Cache,并持久化到WAL(Write ahead log journal),每60s或log文件达到2GB时会做一次Checkpoint,将当前的数据持久化,产生一个新的快照。Wiredtiger连接初始化时,首先将数据恢复至最新的快照状态,然后根据WAL恢复数据,以保证存储可靠性。Wiredtiger的Cache采用Btree的方式组织,每个Btree节点为一个page,root page是btree的根节点,internal page是btree的中间索引节点,leaf page是真正存储数据的叶子节点;btree的数据以page为单位按需从磁盘加载或写入磁盘。Wiredtiger采用Copy on write的方式管理修改操作(insert、update、delete),保证一致性,并且不像InnoDB一样,需要一个DoubleWriteBuffer保证非disk block 512B写时对原有页可能发生conrrupt。修改操作会先缓存在cache里,持久化时,修改操作不会在原来的leaf page上进行,而是写入新分配的page,每次checkpoint都会产生一个新的root page。内存结构:B树,索引页和数据页,新插入跳表(有序)更新list(变更),wal,copy onn write物理结构: 写入:写入页的跳表,不改变原值更新:写入更新数组中读取:若有update合并做checkpoint时,将更新和写入到新的页中,生成新的page_root快照。ACID:隔离用的未提交快照。注意这个快照只是读用的。写还是到最新页。journal:并发,预先分配slots,申请用CAS,buffer和lsn 刷盘关于快照,缓存,数据每个事务有自己的快照(可能是旧的page_root),新的事务会获取当前最新page_root,checkpoint对最新的最持久化和生成新page_root到磁盘,按需读取到内存。新建连接会先进行磁盘的数据的数据恢复,最新快照+WAL,按需读入内存cache 驱逐:略数据清理: compact 加的是DB级别的互斥写锁,同一个DB上的读写都会被阻塞compact基本不需要额外的空间,wiredtiger compact的原理是将数据不断往前面的空洞挪动,并不需要把数据存储到临时的位置(额外的存储空间)。运行中内存占用存储引擎cache,集合,索引元数据,新写入数据MongoDB Driver 会跟 mongod 进程建立 tcp 连接,并在连接上发送数据库请求,接受应答,tcp 协议栈除了为连接维护socket元数据为,每个连接会有一个read buffer及write buffer,用户收发网络包,buffer的大小通过如下sysctl系统参数配置,分别是buffer的最小值、默认值以及最大值。500个类似的连接就会占用掉 1GB 的内存 ,ss -m其他并发大时排序等主备节点差异,备节点buffer存储oplog 分布式 扩展性分片:范围,hash迁移步骤:集合分片开启后,默认会创建一个新的chunk,shard key取值[minKey, maxKey]内的文档(即所有的文档)都会存储到这个chunk。当使用Hash分片策略时,可以预先创建多个chunk,以减少chunk的迁移。 一个 Sharded Cluster 里可能有很多个 mongos,如果所有的 mongos 的 Balancer 同时去触发迁移,整个集群就乱了,为了不出乱子,同一时刻只能让一个 Balancer 去做负载均衡。Balancer 在开始负载均衡前,会先抢锁(config.locks集合下的一个特殊文档),抢到锁的 Balancer 继续干活,没抢到锁的则继续等待,一段时间后再尝试抢锁。 Step1: mongos 发送 moveChunk 给源 shardmongos 接受到用户发送的迁移 chunk 命令,或者因负载均衡策略需要迁移 chunk,会构建一个 moveChunk 的命令,并发送给源 shard。Step2:源 shard 通知目标 shard 开始同步 chunk数据源 shard 收到 mongos 发送的 moveChunk 命令后,会向目标 shard 发送 _recvChunkStart 的命令,通知目标 shard 开始迁移数据(真正的数据迁移由目标shard 主动发起)。接下来,源 shard 会记录该 chunk 在迁移过程中的所有增量修改操作。Step3: 目标 shard 同步 chunk 数据到本地目标 shard 接受到 _recvChunkStart 命令后,就会启动单独的线程来读取 chunk 数据并写到本地,主要步骤包括: 目标 shard 创建集合及索引(如果有必要) 如果迁移的集合在目标 shard 上没有任何 chunk,则需要先在目标 shard 上创建集合,并创建跟源 shard 上集合相同的索引 目标 shard 清理脏数据 (如果有必要) 如果目标 shard 上已经存在该 chunk 范围内的数据,则肯定为某次迁移失败导致的脏数据,先将这些数据清理掉。 目标 shard 向源 shard 发送 _migrateClone 命令读取 chunk 范围内的所有文档并写入到本地,即迁移 chunk 全量数据,迁移完后更新状态为 STEADY(可以理解为全量迁移完成的状态)。 源 shard 会不断调用查询目标 shard 上的迁移状态,看是否为 STEADY 状态,如果已经是 STEADY 状态,就会停止源 shard 上的写操作(通过对集合加互斥写锁实现)。接下来发送 _recvChunkCommit 告诉目标 shard 不会再有新的写入了。 目标 shard 的迁移线程不断向源 shard 发送 _transferMods 命令,读取迁移过程中的增量修改,并应用到本地,增量迁移完成后,向源确认 _recvChunkCommit 的结果。 源 shard 收到 _recvChunkCommit 的结果,整个数据迁移的步骤完成。Step4:源 shard 更新 config server 元数据数据迁移完成后,源 shard 就会向 config server 更新 chunk 对应的 shard 信息,同时也会更新 chunk 的版本信息,这样 mongos 发现本地版本更低就会主动的 reload 元数据,具体机制参考 MongoDB Sharded Cluster 路由策略。Step5:源 shard 删除 chunk 数据chunk 迁移到目标 shard 后,源上的 chunk 就没有必要再保存了,源 shard 会将 chunk 数据删除,默认情况下源 shard 会将删除操作加入到队列,异步删除,如果 moveChunk 时,指定了 _waitForDelete 参数为 true,则同步删除完再返回。一旦源shard 查询到目标 shard 进入到 STEADY 状态了,源 shard 就会进入临界区,测试源上的写就会排队等待。等整个迁移完了,这些等待的写操作就会继续执行,但此时 chunk 的版本号已经更新了,会告诉客户端版本过低,客户端重新从 config server 读取配置,此时拿到的路由信息里 chunk 已经在目标 shard 了,然后写会发往目标 shard 。复制集:数据同步Secondary初次同步数据时,会先进行init sync,从Primary(或其他数据更新的Secondary)同步全量数据,然后不断通过tailable cursor从Primary的local.oplog.rs集合里查询最新的oplog并应用到自身。oplog: 幂等(incr会转为set),循环覆盖,顺序保证:写入 oplog前,会先加锁给 oplog 分配时间戳,并注册到未提交列表里,正式写入 oplog,在写完后,将对应的 oplog 从未提交列表里移除,在拉取 oplog 时若未提交列表为空,所有 oplog 都可读,否则只能到未提交列表最小值以前的 oplogSecondary 拉取到一批 oplog 后,在重放这批 oplog 时,会加一个特殊的 Lock::ParallelBatchWriterMode 的锁,这个锁会阻塞所有的读请求,直到这批 oplog 重放完成 ...

April 24, 2019 · 2 min · jiezi

MongoDb4x集群

脚本port="28017 28018 28019 28020"dir=/usr/local/mongodb/shardexec=/usr/local/mongodb/bin/mongodfunction create(){ for i in $port do mkdir -p $dir/$i/conf mkdir -p $dir/$i/data mkdir -p $dir/$i/log donecat >>$dir/28017/conf/mongod.conf<<'EOF'systemLog: destination: file path: /usr/local/mongodb/shard/28017/log/mongodb.log logAppend: truestorage: journal: enabled: true dbPath: /usr/local/mongodb/shard/28017/data directoryPerDB: true #engine: wiredTiger wiredTiger: engineConfig: # cacheSizeGB: 1 directoryForIndexes: true collectionConfig: blockCompressor: zlib indexConfig: prefixCompression: trueprocessManagement: fork: true pidFilePath: /usr/local/mongodb/shard/28017/mongod.pidnet: port: 28017replication: oplogSizeMB: 2048 replSetName: my_replEOF for i in 28018 28019 28020 do \cp $dir/28017/conf/mongod.conf $dir/$i/conf/ sed -i '' "s#28017#$i#g" $dir/$i/conf/mongod.conf done}function start(){ for i in $port do $exec -f $dir/$i/conf/mongod.conf done}function stop(){ for i in $port do kill -9 `cat ${dir}/$i/mongod.pid` done}if [[ $1 == 'start' ]];then echo "Service Start" startelif [[ $1 == 'create' ]];then echo "Service Create" createelif [[ $1 == 'stop' ]];then echo "Service Stop" stopelif [[ $1 == 'restart' ]];then stop startelse echo "Only Can use (create|start|stop|restart)"fi加入集群mongo --port 28017config = {_id: 'my_repl', members: [ {_id: 0, host: '127.0.0.1:28017'}, {_id: 1, host: '127.0.0.1:28018'}, {_id: 2, host: '127.0.0.1:28019'}] }rs.initiate(config)

April 24, 2019 · 1 min · jiezi

mongodb数据库及数据分页

在做自己的一个小项目时,新学习了mongodb非关系型数据库,使用了mongoose封装好的查询方法,包括数据库分页用到的limit和skip方法,这里记录下。1. mongodb数据库连接参照官网文档对应的参数如下:mongodb://[username:password@]host1[:port1][,host2[:port2],...[,hostN[:portN]]][/[database][?options]]使用mongoose进行数据库的连接const dataBaseUrl = config.admin.username ? `mongodb://${config.admin.username}:${config.admin.pwd}@${config.host}/share-resource` : `mongodb://${config.host}/share-resource`;mongoose.connect(dataBaseUrl, { useNewUrlParser: true });若出现警告信息:要求使用新的编译方式,则在连接的时候加上useNewUrlParser: trueDeprecationWarning: current URL string parser is deprecated, and will be removed in a future version. To use the new parser, pass option { useNewUrlParser: true } to MongoClient.connect.mongoose.connect(dataBaseUrl, { useNewUrlParser: true });在连接数据库时,对连接操作进行监听处理mongoose.connection.on('connected', function() { console.log('Mongoose connection open to ' + dataBaseUrl);});/* 连接数据库异常 */mongoose.connection.on('error', function(err) { console.log('Mongoose connection error:' + err);});/* 连接数据库断开 */mongoose.connection.on('disconnected', function() { console.log('Mongoose connection disconnected');});2. 数据类型(mongoose中提供的schemaTypes)数据类型有:String,Number,Date,Buffer,Boolean,ObjectId,Array,Mixed,Map,Decimal128在数据库直接用insert方法进行数据插入时,若不强制指定数字的类型,则默认是插入double型数字3. mongoose对数据库操作的方法3.1 数据的插入先要新建schema文件const mongoose = require('../database/mongodbHelper');const Message= mongoose.Schema;const RecordModel = new Message({ message: String, name: String, num: Number,},{ versionKey: false});module.exports = mongoose.model('using_records', RecordModel);在使用schema对进行数据的插入时,若直接插入,则会在新的集合中多出一个_v字段,这个代表的是集合的版本号,可以在schema中加入versionKey: false来删除_v字段数据插入:使用save方法const record= new Record({ message: req.body.message, name: req.body.name, num: req.body.num,});record.save((err, docs) => { if (err) { res.send({ 'status': -1, 'msg': '插入失败' }); } else { res.send({ 'status': 200, 'msg': '插入成功', 'result': ''}); }});3.2 数据的查询使用find方法record.find((err, docs) => { if (err) { res.send({ 'status': -1, 'msg': '参数错误' }); } else { res.send({ 'status': 200, 'msg': '查询成功', 'result': docs}); }});3.3 数据的更新更新一条数据:updateOne/* 第一个参数为查询参数,第二个为要更新的内容,第三个为回调方法 */record.updateOne({_id: id}, updateInfo, (err, doc) => { if(err) { res.send({'status': -1, 'msg': '更新失败', 'result': ''}); } else { res.send({'status': 200, 'msg': '更新成功', 'result': ''}); }})更新多条数据:updateManyrecord.updateMany({user_info: {$elemMatch: {user_id: userId, status: 1, is_delete: 1}}}, {$set: {'user_info.$.is_delete': 3}}, (err, doc) => { if(err) { res.send({'status': -1, 'msg': '参数错误'}); } else { res.send({'status': 200, 'msg': '清空成功'}); }})3.4 数据的删除/* 第一个为要删除的内容的参数 */record.findOneAndDelete({_id: req.body.id}, (err, doc) => { if(err) { res.send({'status': -1, 'msg': '删除失败'}); } else { res.send({'status': 200, 'msg': '删除成功'}); }})4. 数据库的分页操作(limit和skip方法)limit()方法为限制数据库每次查询的数据条数;skip(param)跳过param条数据不查询/* page: 页码;pagesize: 每页的数量 */let page = req.body.page;let pagesize = req.body.pagesize;let queryResult = collection.find(queryCondition).limit(pageSize).skip((page - 1) * pageSize).sort({'_id': -1});queryResult.exec((err, value) => { if(err) { reject(err); } else { resolve({total, value}); }})5.匹配数据匹配数据中的数组里的某个对象里的某个字段,使用$set来设置对应的值$set: {'user_info.$.status': 1}$elemMath只匹配第一条数据,当数组里存在多条一样的数据时,只返回第一条数据let arr = [ { is_delete: 1, name: 'a' }, { is_delete: 1, name: 'b' }]{$elemMatch: {is_delete: 1}}只匹配arr的第一条数据aggregate匹配多条数据/* aggregate聚合操作,$unwind将数组拆分成单个元素 * $group 分组依据 * $sum 统计 * $project 将返回值进行筛选,是否返回筛选完后的某个字段 * */ message.aggregate([ { $match: { 'user_info.user_id': id, 'user_info.is_delete': 0 } }, { $unwind: '$user_info' }, { $group: { _id: {status: '$user_info.status',}, count: {$sum: 1} } }, { $project: { '_id': 0, 'status': '$_id.status', 'count': 1 } } ]).then()对于匹配数组里的某项中的某个字段let arr = [ { is_delete: 1, name: 'a' }, { is_delete: 1, name: 'b' }]/* 匹配arr中的name */$match: { 'arr.name': 'a'}/* 分组筛选 */$ group: { _id: {name: '$arr.name'}}对对象中的数组进行插入数据操作let obj = { id: 1, arr: [ { is_delete: 1, name: 'a' }, { is_delete: 1, name: 'b' } ]}{'$push': {arr: {name: 'c', is_delete: 0}}}正在努力学习中,若对你的学习有帮助,留下你的印记呗(点个赞咯^_^)往期好文推荐: ...

April 23, 2019 · 2 min · jiezi

mongoDB 4.0.3 安装(centos篇)

1、到/usr/local下下载mongoDB的安装包curl -O https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-4.0.3.tgz2、解压:tar -zxvf mongodb-linux-x86_64-4.0.3.tgz 3、改名mv mongodb-linux-x86_64-4.0.3.tgz mongodb-4.0.3 4、进入文件夹,增加data和logs文件夹cd mongodb-4.0.3mkdir datamkdir logs5、进入bin目录,新增配置文件cd ../bintouch mongodb.conf6、按照需求编辑配置文件dbpath=/usr/local/mongodb-4.0.3/datalogpath=/usr/local/mongodb-4.0.3/logs/mongodb.logport=27027fork=trueauth=truebind_ip=0.0.0.0注:如果要配置superviser的话,也需要写在这里面 7、将mongod命令写入环境变量:vi /etc/profile.d/mongo.shexport PATH=$PATH:/root/mongodb/binsource /etc/profile注:此步骤也可以省略,加入superviser,写上启动命令就行了 8、启动:mongod --dbpath=/usr/local/mongodb-4.0.3/data --logpath=/usr/local/mongodb-4.0.3/logs/mongodb.log --logappend --fork注:如果没有配置环境变量,写上mongod的完整路径即可 9、最后进入mongo,配置数据库,细节就不描述了,和windows命令行语法一样

April 22, 2019 · 1 min · jiezi

阿里云centOS部署vue全家桶+node+koa2+mongo项目

写在前面 文章有丢丢长,前端开发第一次部署项目,有问题请及时提出,以免误导其他童鞋,轻拍~, 更新系统sudo yum update 安装mongo1. 添加MongoDB源在/etc/yum.repos.d/下创建名为mongodb-org-4.0.repo文件。并在文件中添加如下内容 [mongodb-org-4.0]name=MongoDB Repositorybaseurl=https://repo.mongodb.org/yum/...$releasever/mongodb-org/4.0/x86_64/gpgcheck=1enabled=1gpgkey=https://www.mongodb.org/stati...2. 安装sudo yum install -y mongodb-org3. 配置MongoDB及其他MongoDB的配置文件位于/etc/mongod.conf # network interfacesnet: port: 27017 bindIp: 127.0.0.1 # Enter 0.0.0.0,:: to bind to all IPv4 and IPv6 addresses or, alternatively, use the net.bindIpAll setting.把127.0.0.1 改成0.0.0.0, 原因见注释。 启动和停止MongoDB sudo systemctl start mongodsudo systemctl restart mongodsudo systemctl stop mongod设置开机自启 sudo systemctl enable mongod4. 配置阿里云服务器端口此项不是必须,本地连接远程数据库调试、查看方便些就开启由于centos只开启了一些基础端口(如80),其他都关闭,要自己配置。(当时我明明把mongodb启动了,本地就是访问不了远程) 顺便把你后台接口端口开下。(配置端口方法在文末) 5. 验证(阿里云没开端口就访问不了)在自己浏览器中输入你服务器地址加端口号,例如: http://133.xxx.xx.xx:27017,若界面友好,如出现 It looks like you are trying to access MongoDB over HTTP on the native driver port.恭喜你,完成! ...

April 21, 2019 · 2 min · jiezi

mongodb安装与应用

一、安装数据库如果必要可先更新yum包管理,下面以CentOS系统为例:$ yum -y update1、安装Mongodb查看当前系统版本$ cat /etc/redhat-release打开 https://repo.mongodb.org/yum/… ,选择适合自己系统的mongo版本,然后编辑Mongodb安装源,下面以3.6为例:$ sudo vi /etc/yum.repos.d/mongodb-org-3.6.repo编辑内容(根据自己的mongo版本替换下面的3.6字样):[mongodb-org-3.6]name=MongoDB Repositorybaseurl=https://repo.mongodb.org/yum/redhat/$releasever/mongodb-org/3.6/x86_64/gpgcheck=1enabled=1gpgkey=https://www.mongodb.org/static/pgp/server-3.6.asc安装:$ yum install mongodb-org2、修改Mongo配置mongo配置文件路径:/etc/mongod.conf,但如果用mongod命令直接运行(非service运行),此时若不用-f指定配置文件,mongo会使用内在配置,dbpath也默认存放在/data/db下$ vi /etc/mongod.conf#————————————————————————# 可修改数据库存放位置storage: dbPath: /var/lib/mongodb# 若要支持远程连接,需将默认的127.0.0.1改为:net: bindIp: 0.0.0.0# 若需数据库权限认证,开启下面配置:security: authorization: enabled若远程连接工具Robo 3T还是不能连接,需在防火墙增加端口:$ sudo /sbin/iptables -I INPUT -p tcp –dport 27017 -j ACCEPT二、运行数据库1、启动$ service mongod start # 自动使用配置/etc/mongod.conf# 或手动指定配置文件启动:$ sudo mongod -f /etc/mongod.conf –fork # –fork后台运行# 若以权限认证运行$ sudo mongod -f /etc/mongod.conf –auth –fork若启动出现timeout错误时,可查看service里的pid与config里的pid是否相同,不一致则修改:$ sudo vi /usr/lib/systemd/system/mongod.service # 修改pid$ systemctl daemon-reload # 重新加载service服务$ service mongod start2、重启$ service mongod restart3、关闭$ service mongod stop或手动关闭$ sudo mongod -f /etc/mongod.conf –shutdown4、随系统启动默认安装后即随机启动,无须设置$ chkconfig mongod on三、操作数据库1、数据管理1.) 数据库操作# 进入mongo$ mongo# 显示当前数据库,默认定位test数据库> db;# 显示所有数据库> show dbs;# 切换/创建数据库> use xxx;# 从指定主机克隆数据库> db.cloneDatabase(‘主机’);# 从指定主机上复制A库数据到B库> db.copyDatabase(‘A库’, ‘B库’, ‘主机’); # 删除数据库> db.dropDatabase();# 修复数据库> db.repairDatabase();# 查看当前db的链接机器地址> db.getMongo(); # 退出mongo> exit;2.) 集合(表)/记录操作# 显示所有集合> db.getCollectionNames();# 创建集合> db.createCollection(‘users’, {size: 1024, max: 1000}); # 最大1M,1000条# 显示第一条记录> db.users.findOne();# 显示集合下所有记录> db.users.find();# 新增记录> db.users.save({name: ‘wang’, age: 8});# 根据条件查询> db.users.find({age: 8}); # 修改记录(后面两个参数:第一个表示找不到则新建一条,第二个表示更新多条)db.users.update({name: ‘wang’}, {$set: {sex: ‘male’}}, false, true)# 删除记录db.users.remove({age: 8}); 2、用户管理1.) 用户类型 常用的用户类型有:read/readWrite:读写指定数据库 userAdminAnyDatabase:所有数据库的用户管理权限,有分配角色和用户的权限,但没有读写的权限,在admin库授权 readWriteAnyDatabase:所有数据库的读写权限,在admin库授权 root:超级账号,超级权限,在admin库授权2.) 常用用户指令# 显示所有用户> show users;# 新建用户> db.createUser({user:‘用户名’,pwd:‘密码’,roles:[{role:‘角色’,db:‘库’}]});# 若当前数据库与目标库相同,可简写:> db.createUser({user:‘用户名’,pwd:‘密码’,roles:[‘角色’]});# 追加用户权限> db.grantRolesToUser(‘用户名’,[{role:‘角色’,db:‘库’}]);# 修改用户权限> db.updateUser(‘用户名’,{roles:[‘角色1’,‘角色2’]});# 更新密码> db.changeUserPassword(‘用户名’,‘密码’);# 或> db.updateUser(‘用户名’,{pwd:‘密码’});# 删除用户> db.dropUser(‘用户名’);3.) 管理用户创建root、AnyDatabase角色用户、cluster集群等用户只能在admin库下创建> use admin;# 超级管理员> db.createUser({user:‘root’,pwd:‘密码’,roles:[‘root’]});# user管理员账号> db.createUser({user:‘用户名’,pwd:‘密码’,roles:[‘userAdminAnyDatabase’]});# 任意库读写账号> db.createUser({user:‘用户名’,pwd:‘密码’,roles:[‘readWriteAnyDatabase’]};# 普通读写用户(可以在admin库下创建,但认证时也需在admin库)> db.createUser({user:‘用户名’,pwd:‘密码’,roles:[{role:‘readWrite’,db:‘业务库’}]});4.) 普通用户创建mongoDB的权限是跟随库的,用户在哪个库下创建的,则需在哪个库进行auth认证,如果认证库和读写目标库一致,则连接时authSource参数可省略(见第四节:连接数据库)> use xxx;> db.createUser({user:‘用户名’,pwd:‘密码’,roles:[‘readWrite’]);如果提示SCRAM-SHA-256 requires undigested passwords错误,需要加mechanisms,如下:> db.createUser({user:‘用户名’,pwd:‘密码’,roles:[‘readWrite’],mechanisms: [‘SCRAM-SHA-1’]});四、连接数据库1、shell方式连接:1.)普通登录如果mongoDB未开启认证模式,所有用户的权限与root一样,任意操作。$ mongo# 自定义host和端口$ mongo –host 主机 –port 端口2.)认证登录mongoDB的权限是跟随库的,普通用户在哪个库新建授权,就需要在哪个库进行auth验证$ mongo –host 主机 –port 端口 -u ‘用户名’ -p ‘密码’ –authenticationDatabase ‘身份认证所用库’root和AnyDatabase角色的权限只能在admin数据库里认证,如:$ mongo -u root -p 123456 –authenticationDatabase admin也可以进入数据库后再授权:$ mongo> use 认证数据库;> db.auth(‘用户名’, ‘密码’);2、URI方式连接:1.)未认证数据库mongo://主机:端口/数据库名2.)认证数据库a.)普通数据用户若当前用户认证的数据库与要操作的数据库相同,连接方式如下:mongo://用户名:密码@主机:端口/数据库名若不同,则需添加authSource,例如:在admin库里认证了,角色db却是xxxmongo://用户名:密码@主机:端口/xxx?authSource=adminb.)root和所有数据库用户若连接用户是root、*AnyDatabase角色、cluster集群的认证库都是adminmongo://用户名:密码@主机:端口/数据库名?authSource=adminc.)认证机制mongoDB支持多种认证机制, 常用的有’MONGODB-CR’ 和 ‘SCRAM-SHA-1’ 两种,官方推荐 ‘SCRAM-SHA-1’,此时不需要带在URI里,但如果是’MONGODB-CR’类型需加添加在URI上。mongo://用户名:密码@主机:端口/数据库?authMechanism=认证机制&authSource=身份认证所用库 ...

April 19, 2019 · 2 min · jiezi

mongoose报错: getaddrinfo ENOTFOUND localhost localhost:27017

{ Error: getaddrinfo ENOTFOUND localhost localhost:27017 at errnoException (dns.js:50:10) at GetAddrInfoReqWrap.onlookup [as oncomplete] (dns.js:92:26) code: ‘ENOTFOUND’, errno: ‘ENOTFOUND’, syscall: ‘getaddrinfo’, hostname: ’localhost’, host: ’localhost’, port: 27017 }mongoose.connect(‘mongodb://localhost:27017/db1’,{ poolSize:5, useNewUrlParser: true},err=>{ if(err){ console.error(err) }else { console.log(‘mongodb 连接成功’) }})将localhost修改为 127.0.0.1 试试mongoose.connect(‘mongodb://127.0.0.1:27017/db1’,{ poolSize:5, useNewUrlParser: true},err=>{ if(err){ console.error(err) }else { console.log(‘mongodb 连接成功’) }})

April 16, 2019 · 1 min · jiezi

Node.js 连接 MongoDB, 查找数据并返回

刚接触MongoDB,想利用node.js连接数据库,查找信息并返回,出现了一些问题.异步操作(node.js利用回调进行异步)使得信息获取不到.最后通过以下方法解决:查询是否存在 username为 joke 且 password为 456 的文档mongodb:node.js:(这里我是把login函数分装且导出的…)将response传入效果如下:

April 16, 2019 · 1 min · jiezi

Python MongoDB 一些聚合查询方法

MongoDB 的聚合查询语法一直让我难以很好的入门,如果不是因为项目需要,我很少会用到它,但是用多了之后,会越来越喜欢它,尤其是接触了一些聚合查询方法后,我发现 MongoDB 真的在业务中提高了不少效率。总之,MongoDB 真香~~~下面是我的一些平时使用聚合查询的记录data 集合数据格式{ “_id” : ObjectId(“5caef7f2c0cd2730919a038f”), “sn” : “1904010010000001”, “dev_id” : 200, “dt” : ISODate(“2036-02-07T14:29:00.000Z”), “data” : { “BT” : 20.0, “CSQ” : 23, “GPSLati” : 39.8679244, “GPSLongti” : 116.6568387, “Humidity” : 0.0, “Temprature” : 0.0, “Voltage” : 0.0 }}查询所有 sn 下的最新一条数据sn = [‘1904010010000001’, ‘1904010010000002’, ‘1904010010000003’]pipeline = [ {’$match’: {‘sn’: {’$in’: sn}}}, {’$group’: {’_id’: “$sn”, “data”: {’$last’: “$data”}, “dt”: {’$last’: “$dt”}}}, {’$sort’: {“dt”: 1}}]db.data.aggregate(pipeline)返回结果(避免数据过长,仅显示一个数据)[ { ‘_id’: ‘1812010009000100’, ‘data’: { ‘Ap’: 1009.7, ‘BT’: 20.0, ‘CSQ’: 24, ‘GPSLati’: 39.8681678, ‘GPSLongti’: 116.6591262, ‘Humidity’: 31.400000000000002, ‘Temprature’: 21.5, ‘Voltage’: 0.98, ‘WindDir’: 0, ‘WindSpeed’: 0.0 }, ‘dt’: datetime.datetime(2019, 4, 14, 17, 44) }]查询某个 sn 10 小时内每隔 10 分钟统计的平均值sn = ‘1904010010000001’pipeline = [ {’$project’: {‘date’: {’$substr’: ["$dt", 0, 15]}, ‘data’: ‘$data’}}, {’$group’: { ‘_id’: “$date”, ’temprature’: {’$avg’: ‘$data.Temprature’}, ‘humidity’: {’$avg’: ‘$data.Humidity’}, ‘wind_speed’: {’$avg’: ‘$data.WindSpeed’}, ‘wind_dir’: {’$avg’: ‘$data.WindDir’} }}, {’$limit’: 60}, {’$sort’: {’_id’: -1}}]db.data.aggregate(pipeline)返回结果(避免数据过长,仅显示一个数据)[ { ‘_id’: ‘2019-04-14T01:3’, ’temprature’: 10.861538461538462, ‘humidity’: 18.70769230769231, ‘wind_speed’: 0.49230769230769234, ‘wind_dir’: 167.6153846153846 }]原文地址: Python MongoDB 一些聚合查询方法我的博客: 时空路由器 ...

April 14, 2019 · 1 min · jiezi