Python开发之Mongodb进阶

MongoDB 索引

索引通常能够极大的提高查询的效率,如果没有索引,MongoDB在读取数据时必须扫描集合中的每个文件并选取那些符合查询条件的记录。

这种扫描全集合的查询效率是非常低的,特别在处理大量的数据时,查询可以要花费几十秒甚至几分钟,这对网站的性能是非常致命的。

索引是特殊的数据结构,索引存储在一个易于遍历读取的数据集合中,索引是对数据库表中一列或多列的值进行排序的一种结构

MongoDB使用 createIndex() 方法来创建索引。

db.collection.createIndex(keys, options)

语法中 Key 值为你要创建的索引字段,1 为指定按升序创建索引,如果你想按降序来创建索引指定为 -1 即可。

创建数据:

执行如下代码,向集合中插入10万条文档

for(i=0;i<100000;i++){db.t1.insert({name:'test'+i,age:i})}

查找姓名为‘test10000’的文档,并使用explain()这条命令进行查询性能分析

db.t1.find({name:'test10000'}).explain('executionStats')
{
    "queryPlanner" : {
        "plannerVersion" : 1,
        "namespace" : "mycol.t1",
        "indexFilterSet" : false,
        "parsedQuery" : {
            "name" : {
                "$eq" : "test10000"
            }
        },
        "winningPlan" : {
            "stage" : "COLLSCAN",
            "filter" : {
                "name" : {
                    "$eq" : "test10000"
                }
            },
            "direction" : "forward"
        },
        "rejectedPlans" : [ ]
    },
    "executionStats" : {
        "executionSuccess" : true,
        "nReturned" : 1,
        "executionTimeMillis" : 59,
        "totalKeysExamined" : 0,
        "totalDocsExamined" : 100000,
        "executionStages" : {
            "stage" : "COLLSCAN",
            "filter" : {
                "name" : {
                    "$eq" : "test10000"
                }
            },
            "nReturned" : 1,
            "executionTimeMillisEstimate" : 50,
            "works" : 100002,
            "advanced" : 1,
            "needTime" : 100000,
            "needFetch" : 0,
            "saveState" : 781,
            "restoreState" : 781,
            "isEOF" : 1,
            "invalidates" : 0,
            "direction" : "forward",
            "docsExamined" : 100000
        }
    },
    "serverInfo" : {
        "host" : "python",
        "port" : 27017,
        "version" : "3.0.6",
        "gitVersion" : "1ef45a23a4c5e3480ac919b28afcba3c615488f2"
    },
    "ok" : 1
}

上面查询结果的executionTimeMillis表示整体的查询时间,单位是毫秒,利用普通查询花了59毫秒

建立索引查询,给name升序创建索引

db.t1.createIndex({name:1})
2018-08-17T22:20:25.605+0800 I INDEX    [conn1] build index on: mycol.t1 properties: { v: 1, key: { name: 1.0 }, name: "name_1", ns: "mycol.t1" }
2018-08-17T22:20:25.607+0800 I INDEX    [conn1]      building index using bulk method
2018-08-17T22:20:26.005+0800 I INDEX    [conn1] build index done.  scanned 100000 total records. 0 secs
2018-08-17T22:20:26.089+0800 I COMMAND  [conn1] command mycol.$cmd command: createIndexes { createIndexes: "t1", indexes: [ { key: { name: 1.0 }, name: "name_1" } ] } keyUpdates:0 writeConflicts:0 numYields:0 reslen:113 locks:{ Global: { acquireCount: { r: 1, w: 1 } }, MMAPV1Journal: { acquireCount: { w: 200004 }, acquireWaitCount: { w: 1 }, timeAcquiringMicros: { w: 43354 } }, Database: { acquireCount: { W: 1 } }, Collection: { acquireCount: { W: 1 } }, Metadata: { acquireCount: { W: 4 } } } 458ms
{
    "createdCollectionAutomatically" : false,
    "numIndexesBefore" : 1,
    "numIndexesAfter" : 2,
    "ok" : 1
}

再次查询并分析

db.t1.find({name:'test10000'}).explain('executionStats')
{
    "queryPlanner" : {
        "plannerVersion" : 1,
        "namespace" : "mycol.t1",
        "indexFilterSet" : false,
        "parsedQuery" : {
            "name" : {
                "$eq" : "test10000"
            }
        },
        "winningPlan" : {
            "stage" : "FETCH",
            "inputStage" : {
                "stage" : "IXSCAN",
                "keyPattern" : {
                    "name" : 1
                },
                "indexName" : "name_1",
                "isMultiKey" : false,
                "direction" : "forward",
                "indexBounds" : {
                    "name" : [
                        "[\"test10000\", \"test10000\"]"
                    ]
                }
            }
        },
        "rejectedPlans" : [ ]
    },
    "executionStats" : {
        "executionSuccess" : true,
        "nReturned" : 1,
        "executionTimeMillis" : 2,
        "totalKeysExamined" : 1,
        "totalDocsExamined" : 1,
        "executionStages" : {
            "stage" : "FETCH",
            "nReturned" : 1,
            "executionTimeMillisEstimate" : 0,
            "works" : 2,
            "advanced" : 1,
            "needTime" : 0,
            "needFetch" : 0,
            "saveState" : 0,
            "restoreState" : 0,
            "isEOF" : 1,
            "invalidates" : 0,
            "docsExamined" : 1,
            "alreadyHasObj" : 0,
            "inputStage" : {
                "stage" : "IXSCAN",
                "nReturned" : 1,
                "executionTimeMillisEstimate" : 0,
                "works" : 2,
                "advanced" : 1,
                "needTime" : 0,
                "needFetch" : 0,
                "saveState" : 0,
                "restoreState" : 0,
                "isEOF" : 1,
                "invalidates" : 0,
                "keyPattern" : {
                    "name" : 1
                },
                "indexName" : "name_1",
                "isMultiKey" : false,
                "direction" : "forward",
                "indexBounds" : {
                    "name" : [
                        "[\"test10000\", \"test10000\"]"
                    ]
                },
                "keysExamined" : 1,
                "dupsTested" : 0,
                "dupsDropped" : 0,
                "seenInvalidated" : 0,
                "matchTested" : 0
            }
        }
    },
    "serverInfo" : {
        "host" : "python",
        "port" : 27017,
        "version" : "3.0.6",
        "gitVersion" : "1ef45a23a4c5e3480ac919b28afcba3c615488f2"
    },
    "ok" : 1
}

可以看到executionTimeMillis仅为2毫秒

索引的命令

建立唯一索引,实现唯一约束的作用

db.t1.createIndex({age:1},{'unique':true})

创建联合索引,对多个属性建立一个索引

db.t1.createIndex({name:1,age:1})

查看文档所有的索引

db.t1.getIndexes()

删除索引

db.t1.dropIndexes()

 安全性

为了更安全的访问MongoDB,需要访问者提供用户名和密码,于是需要在MongoDB中创建用户。

采用了角色-用户-数据库的安全管理模式

常用的角色有:root:只在admin数据库中可用,超级账号,超级权限。Read:允许用户读取指定数据库,readWrite:允许用户读写指定数据库

创建超级管理用户使用admin这个数据库

> use admin
switched to db admin
> db.createUser({user:'admin',pwd:'123',roles:[{role:'root',db:'admin'}]})
Successfully added user: {
    "user" : "admin",
    "roles" : [
        {
            "role" : "root",
            "db" : "admin"
        }
    ]
}

修改配置文件进行授权:编辑vim /data/mongod.conf中打开

python@python:/usr/local/mongodb/bin$ cat mongod.conf 
# where to write log
systemLog: 
  destination: file
  path: /data/db/log.log
  logAppend: true

# where and how to store data 
storage: 
  dbPath: /data/db
 # directoryPerDB: true
 # indexBuildRetry: false
 # preallocDataFiles: true
 # nsSize: 16

# quota:
#   enforced: false
#   maxFilesPerDB: 8
  #smallFiles: false
  #syncPeriodSecs: 60
# repairPath: "/var/lib/mongo/_tmp"
  journal:
    enabled: true
#   debugFlags: 1
   # commitIntervalMs: 100
#processManagement: 
 # fork: true
  #pidFilePath: "/var/run/mongodb/mongod.pid"
net: 
# bindIp: 192.168.11.52 
  port: 27017
  http:
    enabled: true
    RESTInterfaceEnabled: false 
# ssl:
#   mode: "requireSSL"
#   PEMKeyFile: "/etc/ssl/mongodb.pem"
#operationProfiling:
 # slowOpThresholdMs: 100 
  #mode: "slowOp"
security:
  #keyFile: "/var/lib/mongo/mongodb-keyfile"
  #clusterAuthMode: "keyFile"
  authorization: enabled
#replication:
 # oplogSizeMB: 50
  #replSetName: "repl_test"
 # secondaryIndexPrefetch: "all"
View Code

security:

  authorization: enabled

终端连接

./mongo -u admin -p 123 --authenticationDatabase admin

复制

复制提供了数据的冗余备份,并在多个服务器上存储数据副本,提高了数据的可用性,并可以保证数据的安全性,复制还可以允许从硬件故障和服务中断中恢复数据。

复制的作用:

  • 数据备份
  • 数据灾难恢复
  • 读写分离
  • 高(24*7)数据可用性
  • 无宕机维护
  • 副本集对应用程序是透明的

复制的工作原理:

  • 复制至少需要两个节点A、B
  • A是主节点,负责处理客户端请求
  • 其余的都是从节点,负责复制主节点上的数据
  • 节点常见的搭配方式,一主一从,一主多从
  • 主节点记录在其上的所有操作,从节点定期轮询主节点获取这些操作,然后对自己的数据副本执行这些操作,从而保证从节点的数据与主节点一致。
  • 主节点与从节点进行数据交互保障数据的一致性。

例如:

  • 在/data/下创建两个数据库目录
python@python:/data$ sudo mkdir t1
python@python:/data$ sudo mkdir t2
  • 启动mongod,注意replSet的名称必须一致
./mongod --bind_ip 192.168.8.128  --port 27018 --dbpath /data/t1 --replSet rs0
./mongod  --bind_ip 192.168.8.128 --port 27019 --dbpath /data/t2 --replSet rs0
  • 终端连接
./mongo --host 192.168.8.128 --port 27018
./mongo --host 192.168.8.128 --port 27019
  • 初始化
> rs.initiate()
{
    "info2" : "no configuration explicitly specified -- making one",
    "me" : "192.168.8.128:27019",
    "ok" : 1
}

可以看到初始化完成后,提示符也改变了

rs0:PRIMARY> 
  • 查看状态
rs.status()
{
    "set" : "rs0",
    "date" : ISODate("2018-08-18T02:44:20.781Z"),
    "myState" : 1,
    "members" : [
        {
            "_id" : 0,
            "name" : "192.168.8.128:27019",
            "health" : 1,
            "state" : 1,
            "stateStr" : "PRIMARY",
            "uptime" : 753,
            "optime" : Timestamp(1534560126, 1),
            "optimeDate" : ISODate("2018-08-18T02:42:06Z"),
            "electionTime" : Timestamp(1534560126, 2),
            "electionDate" : ISODate("2018-08-18T02:42:06Z"),
            "configVersion" : 1,
            "self" : true
        }
    ],
    "ok" : 1
}
  • 添加复本集
rs0:PRIMARY> rs.add('192.168.8.128:27018')
{ "ok" : 1 }

再次查看状态

rs0:PRIMARY> rs.status()
{
    "set" : "rs0",
    "date" : ISODate("2018-08-18T02:50:14.128Z"),
    "myState" : 1,
    "members" : [
        {
            "_id" : 0,
            "name" : "192.168.8.128:27019",
            "health" : 1,
            "state" : 1,
            "stateStr" : "PRIMARY",
            "uptime" : 1107,
            "optime" : Timestamp(1534560503, 1),
            "optimeDate" : ISODate("2018-08-18T02:48:23Z"),
            "electionTime" : Timestamp(1534560126, 2),
            "electionDate" : ISODate("2018-08-18T02:42:06Z"),
            "configVersion" : 2,
            "self" : true
        },
        {
            "_id" : 1,
            "name" : "192.168.8.128:27018",
            "health" : 1,
            "state" : 2,
            "stateStr" : "SECONDARY",
            "uptime" : 110,
            "optime" : Timestamp(1534560503, 1),
            "optimeDate" : ISODate("2018-08-18T02:48:23Z"),
            "lastHeartbeat" : ISODate("2018-08-18T02:50:13.871Z"),
            "lastHeartbeatRecv" : ISODate("2018-08-18T02:50:13.881Z"),
            "pingMs" : 0,
            "configVersion" : 2
        }
    ],
    "ok" : 1
}
  • 连接复本可以看到它的提示符已经变成了
rs0:PRIMARY> 
  • 再主的上面进行数据添加
rs0:PRIMARY> db.stu.insert({name:'gj'})
WriteResult({ "nInserted" : 1 })
rs0:PRIMARY> db.stu.find()
{ "_id" : ObjectId("5b778a631b379ce4c3594699"), "name" : "gj" }
  • 如果在从的上面进行读取操作,需要设置
rs0:SECONDARY> rs.slaveOk()
rs0:SECONDARY> db.stu.find()
{ "_id" : ObjectId("5b778a631b379ce4c3594699"), "name" : "gj" }
  • 删除从节点
rs.remove('192.168.8.128:27018')
  • 关闭主服务器后,再重新启动,会发现原来的从服务器变成了主服务器,原来的主服务器变成了从服务器

备份

 -h 主机名  -d 数据库  -o备份的文件目录

./mongodump -h 192.168.8.128 -d mycol -o /data/bak

恢复

-h 服务器地址

-d 需要恢复的数据库实例

--dir 备份数据库所在的位置

./mongorestore -h 192.168.8.128:27017 -d mycol1 --dir  /data/bak/mycol/

pymongo模块

Python 要连接 MongoDB 需要 MongoDB 驱动,

安装 pymongo:

sudo  pip3 install pymongo

类MongoCilent:建立连接并创建客户端

建立连接并创建客户端

无安全认证:client = pymongo.MongoClient('mongodb://192.168.8.128:27017')
有安全认证:client = pymongo.MongoClient('mongodb://用户名:密码@192.168.8.128:27017/数据库名')

获得数据库

import pymongo

client = pymongo.MongoClient('mongodb://192.168.8.128:27017')

db = client.py3 # 获得名为py3的数据库
print(db)

类collection的主要方法

  • insert_one
  • insert_many
  • update_one
  • update_many
  • delete_one
  • delete_many
  • find_one
  • find
import pymongo

client = pymongo.MongoClient('mongodb://192.168.8.128:27017')

db = client.py3 # 获得名为py3的数据库
# 获取集合
t1 = db.t1
# 增加
# t1.insert_one({'name':'张三丰'})
# 修改
# t1.update_one({'name':'张三丰'},{'$set':{'name':'张三丰1'}})
# 删除
# t1.delete_one({'name':'张三丰1'})
#查询
# for item in t1.find():
#     print(item)
#     '''
#     {'_id': ObjectId('5b77925d200823c35c2a53ab'), 'name': 'python'}
#     {'_id': ObjectId('5b77a5045a6891148cdc24d1'), 'name': 'jack'}
#     '''
for cursor in t1.find().sort('_id',pymongo.DESCENDING).skip(1).limit(1):
    print(cursor)
    '''
    {'_id': ObjectId('5b77a5045a6891148cdc24d1'), 'name': 'jack'}
    '''

 

posted on 2018-08-18 13:17  Mr.Hui  阅读(829)  评论(0)    收藏  举报

导航