数据库基础 非关系型数据库 MongoDB 和 redis

时间:2023-03-08 17:14:54

数据库基础 非关系型数据库 MongoDB 和 redis


1 NoSQL简介

访问量增加,频繁的读写

直接访问(硬盘)物理级别的数据,会很慢 ,关系型数据库的压力会很大

所以,需要内存级的读写操作,作为缓存

1 NoSQL的使用

为了加快读写操作时间 减少服务器压力

先访问(非关系型数据库)缓存中的数据

(缓存中没有) 再去(关系型数据库)物理存储中访问数据

即 请求 --->> nosql --->> mysql

2 NoSQL的特点

优点 : 高可扩展性
分布式计算
低成本
架构灵活
没有复杂的关系 缺点:
没有标准化(产品不一样)
有限的查询功能功能
最终一致是不直观的程序

3 NoSQL的分类

非关系数据库的分类:

     列存储 :
文档存储:
键值对:
图存储:
对象存储:
xml数据库:

redis,mongodb 中 储存 一些 经常使用(不常改变)的数据

2 MongoDB

基本概念

MongoDB 是一个 基于分布式文件存储的NoSQL,

也可以做硬盘级别的读写

面向集合,json风格的文件形式

完整的索引支持,对任何属性都可以索引

mongodb 的三元素:

数据库

database

数据库 是一个集合的物理容器

集合(表)

collection

集合 ,存储多个文档(结构不固定)---相当于关系型数据库的表

{'name':'a','gender':'male'}
{'book':'a','gender':'male'}
{'name':'a','age':18}

文档(行)

document ,文档,就是对象 由键值对构成 json的扩展形式 bson

{'name':'a','gender':'male'}

与关系型数据库的对比:

mongodb       关系型

database       database
collection table
document row
field column
index index
primary key 主键自己维护

mongodb的安装和部署 (windows):

注意:

偶数版本是稳定版本,奇数版本是开发版本

32bit的mongodb最大只能存放 2G 数据,64bit 没有限制

**服务端 mongod 默认端口27017**

     1 官网下载64bit
自定义安装后 2 在mongodb\下创建 data文件夹; 在data\下创建db文件夹 即 mongodb\data\db; 3 然后命令行 cd D:/mongodb/bin 切换路径 输入mongod.exe --dbpath D:/mongodb/data/db 4 出现如下,启动成功 # 2017-11-07T09:40:38.843+0800 I COMMAND [initandlisten] setting featureCompatibilityVersion # to 3.4
# 2017-11-07T09:40:38.845+0800 I NETWORK [thread1] waiting for connections on port 27017 5 web访问 本地端口 127.0.0.1:27017
It looks like you are trying to access MongoDB over HTTP on the native driver port.
说明服务已开启 6 配置 Mongodb服务 # 创建 mongodb.cfg 文件 写入如下配置 systemLog:
destination: file
path: D:\mongodb\data\log\mongod.log
storage:
dbPath: D:\mongodb\data\db # 创建 data\log 文件夹 7 命令行下 执行 mongod.exe --config "D:\mongodb\mongod.cfg" --install net start mongodb
net stop mongodb mongod.exe --remove 移除mongodb服务

客户端

mongo.exe

界面版mongodb

安装配置完成,可以使用图形界面版mongodb,这里推介 robomongo robo 3t

mongodb操作

  • 1 数据库操作

      没有数据的 集合 和 数据库不会显示
    
      db     查看当前的数据库名称; 所有物理上存在的数据库
    
      db.stats() 查看当前的数据库信息;
    
      show dbs   查看所有的数据库
    
      use day1   直接创建 并切换数据库
    
      db.dropDatabase() 删除库,需要先切换到当前库
    
      exit 退出
    ctrl + c
  • 2 集合操作

      db.createCollection("stu")
    
      show collections
    
      show tables
    
      db.t1.insert({"name":"aaa"})  # 创建 t1 同时 插入数据
    
      db.stu.drop()
  • 3 数据类型 和 数据操作

mongodb的数据类型

object ID --- 文档ID ---唯一标识
string 必须是有效的utf-8
boolean
integer 整数可以是 32 64的 取决于服务器
double 储存浮点数
arrays 数组 列表
object 文档可以嵌套
null
timestamp
date new Date()

Object ID

不重复 文档的唯一性(可以自己定义 没有定义的mongodb会默认)

默认 12字节的 16进制数

前四个字节是当前的时间戳 时间相关
3个字节 机器ID
2个字节的MONGODB进程ID
3个字节 简单的增量值

数据操作:

插入:(没有 集合会自己创建)

  db.stu.insert(document)
  db.stu.insert({name:"gl",gender:0}) db.stu.insertMany([{},{},{}]) 插入多条记录   自动生成的id : _id" : ObjectId("5a043500ffedb64e1403b240") 查询:   db.stu.find() 修改: 覆盖式的 修改: db.stu.update({"name":"a"},{"username":"al"})   用 $set:{} ($unset:{}删除某个属性)只修改某个属性 --   不用的话 会修改整个文档的结构   db.stu.update({匹配条件},{$set:{name:"abc"}}) 只改第一个匹配的   db.stu.update({},{$set:{name:"abc",gender:0}}) {} 匹配全部 只修改第一个   db.stu.update({匹配条件},{$set:{name:"abc"}},{multi:true}) 全部修改 db.stu.update({找不到带的匹配条件},{$set:{name:"abc"}},{upsert:true}) 找不到 插入记录 db.stu.update({匹配条件},{$unset:{"要删除的字段":"任意字符"}}) 清除某一字段 $inc 增加 db.stu.update({},{$inc:{"age":1}},{multi:true}) age自增1 $push 添加 db.stu.update({匹配条件},{$push:{"hobby":"read"}})
db.stu.update({匹配条件},{$push:{"hobby":{$each:[1,2]}}}) $pop 删除两端 db.stu.update({匹配条件},{$pop:{"hbooy":-1})
db.stu.update({匹配条件},{$pop:{"hbooy":1}) $pull:按照条件删除元素,:"$pull" 把符合条件的统统删掉,而$pop只能从两端删 db.stu.update({匹配条件},{$pull:{"hbooy":["read"]}) $addToSet: 避免添加重复:去重 url db.urls.insert({"_id":1,"urls":[]}) db.urls.update({"_id":1},{"$addToSet":{"urls":'http://www.baidu.com'}}) db.urls.update({"_id":1},{"$addToSet":{"urls":{"$each":[
'http://www.baidu.com',
'http://www.baidu.com',
'http://www.xxxx.com']}}}) 保存:按照 _id 字段   如果一个文档存在的话是 覆盖式的修改,   不存在是添加 insert   db.stu.save({name:"emily",gender:2}) 新增   db.stu.save({_id:"23010132",name:"emily",gender:2}) 有的话覆盖 删除: 1、删除多个中的第一个 db.user.deleteOne({ 'age': 8 }) 2、删除国家为China的全部 db.user.deleteMany( {'addr.country': 'China'} ) 3、删除全部 db.user.deleteMany({})   db.stu.remove({gender:0},{justOne:true}) # 是否只删除一条   db.stu.remove({}) 删除全部  
  size:   db.createCollection("sub",{capped:true,size:10}) # 超过 size大小 会覆盖 capped

数据查询:

db.sub.find() 查询所有

db.sub.find().pretty() 查询所有 格式化输出

db.sub.findOne() 查询第一个

比较运算符:

  < $lt
  <= $lte
  > $gt
  >= $gte
  != $ne
= {"条件":"条件"}    db.sub.find({age:{$gte:18}})
   逻辑及运算符:   且 ,   或 $or[数组]    db.sub.find({$or:[{age:{$gte:18}},{gender:0}],name:"alex"}) 非 not    db.sub.find({age:{$not:{$gte:18}}}) 取余: db.sub.stu.find({age:{$mod:[2,1]}}) 除2余1 的age 范围运算符:   $in   $nin    db.sub.find({age:{$in:[18,20]}}) 支持正则表达式:   使用 正则 表达式
   db.stu.find({name:{$regex:"^黄.*?[a,e]$"}}) 取 指定的 字段 db.stu.find({"name":{$regex:"^al"}},{"name":1,"age":1,"_id":0}) 只选择查看name age 不要 id 查找数组 {hobby:["tea","music"]} db.stu.find({"hobby":"tea"}) 包含tea的全部查找 db.stu.find({"hobby.3":"tea"}) 查看第三个 hobby 是 tea的 db.stu.find({"hobby":{$all:["tea","music"]}}) 查看 hobby 既有 tea 又有 music的 db.stu.find({},{"_id":0,"hobby":{"$slice":[0,3]}}) 查找 所有记录的 第一到第三个hobby 支持函数   db.stu.find({$where:function(){return this.age>20}})   this 指的是文档对象   mongodb 用的是 js的编辑器 limit(n) 选择 n 条 显示的个数 skip(m) 跳过m条   db.sub.find().skip(1).limit(2) 投影:只选择某个字段   db.sub.find({},{name:1,gender:0}) # 表示只查看name字段 排序:   db.sub.find().sort({age:-1}) # 1 -1 正序逆序
  db.sub.find().sort({age:-1,"_id":1}) # 先按 age 降序 再按 id正序 count:计数   db.sub.count({name:{$gte:18}})
  
distinct:消除重复   db.sub.distinct("gender",{age:{$lte:20}})

mongoDB的高级操作

aggregate 聚合:

  主要用于计算数据 类似 sum avg

  db.stu.aggregate([{管道:{表达式}}])

常用的管道

  $ group -----group by
  $ match ----过滤数据
  $ project ---- 修改文档的结构
  $ sort
  $limit
  $skip
  $unwind 表达式:
  $ sum
  $ avg
  $ min
  $ max
  $ push
  $ first
  $ last   分组 --- group   db.sub.aggregate([
  {$group:{_id:"$gender",counter:{$sum:1}}}
  ])   # $push ---将 符合条件的数据存入 一个新文档中
  db.sub.aggregate([
  {$group:{_id:"$gender",counter:{$push:"$$ROOT"}}}
  ]) 过滤 --- match db.sub.aggregate( [
{$match:{age:{$gt:20}}} ,
{$group:{_id:"gender",counter:{$sum:1}}}
] ) # 先过滤结果 然后 再 分组操作 # $project
# $sort
# $limit
# $skip   顺序=====   db.stu.aggregate([
  {$match:{age:{$gt:20}}},
  {$group:{_id:"gender",counter:{$sum:1}}},
  {$project:{_id:1,counter:1}}, # 投影
  {$sort:{_id:-1}},
  {$skip:{1}},
  {$limit:1},   ]) unwind 拆分数组   db.t1.aggregate([
  {$unwind:"$size"}
  ]) /* 1 */
{
"_id" : 1.0,
"title" : "t-shirt",
"size" : "M"
} /* 2 */
{
"_id" : 1.0,
"title" : "t-shirt",
"size" : "L"
} /* 3 */
{
"_id" : 1.0,
"title" : "t-shirt",
"size" : "S"
}

索引

创建大量的数据

     mongodb的shell 同时也是javascript的编译器 ---- 支持js语法

  for(i=0;i<1000000;i++){
  db.t1.insert({name:"test"+i,age:i})
  }   查看 查询状态 --- explain("executionStats") 查询工具   db.t2.find({name:"name10000"}).explain("executionStats") 创建索引   db.t2.ensureIndex({属性:1}) # 1 表示升序 -1 表示降序 唯一索引:唯一约束
  db.t2.ensureIndex({属性:1},{"unique":true}) 联合索引
  db.t2.ensureindex({name:1,age:1}) 查看文档的所有索引:
  db.t1.getIndexes() 删除索引
  db.t1.dropIndex(索引名)

数据备份与恢复

备份

  mongodump -h dbhost -d dbname -o dbdirectary

  mongodump -u xxx -p xx --authenticationDatabase py3 -d xxx -o xxx

恢复

  mongorestore -h dbhost -d dbname --dir dbdirectary

账户安全

1 数据库权限

# root 只在admin数据库中可以使用 ==== 数据库后台 DBA使用

# read 允许访问指定的数据库 普通用户
# readWrite 允许读写指定的数据库 普通用户 2 创建 admin 管理员账号 # use admin 权限 授权的数据库
# db.createUser({user:"admin",pwd:"123",roles:[role:"root",db:"admin"]}) 3 启用安全认证 修改配置文件: security:
authorization:enabled 重启服务: 连接:
# mongo -u 用户名 -p 密码 --authenticationDatabase py3

主从 副本集

# 1 主从关系

	工作服务器
请求 ---->>负载均衡
备份服务器 备份服务器 # 2 数据的冗余备份 并在多个服务器上储存数据副本 , 提高数据的可用性, 和安全性 复制还允许 从硬件故障和服务中断中恢复数据 # 3 复制至少需要两个节点 A,B。。
A是主节点 负责处理客户端的请求
其余的都是从节点,负责复制主节点上的数据
搭配方式有:一主多从 一主一从
#心跳 主节点会记录在其上所有的操作- - 从节 点定期的轮训主节点上的操作记录, 对自己 的数据副本进行相同的操作 保持主从一致性
#无宕机切换 主节点宕机--变成从节点,从节点变成主节点
#备份 # 创建副本集 --- 实现数据备份 开启两个服务终端 mongod --bind_ip xxxxxx --port 20717 --dbpath 数据库文件夹 --replSet rs0 mongod --bind_ip xxxxxx --port 20718 --dbpath 数据库文件夹 --replSet rs0 再开两个客户端 -- 连接终端 mongo --host xxxxx --port xxxx mongo --host xxxxx --port xxxx rs.initiate() 设置初始化 设为主节点
rs.status() 查看状态 rs.add("ip,port") 添加 从节点 在主的数据库中写入数据 从的数据库也会有 # 心跳 在从的数据库中查询需要 设置权限 rs.slaveOk() 删除节点:
rs.remove("ip,port") 模拟主从切换 两个服务端 关闭 开启 --- 再连接 主从自动切换

与python的交互:

from pymongo import *

#  建立连接
client=MongoClient("mongodb://localhost:27017") # client = MongoClient("127.0.0.1",27017) # 切换数据库 use
db=client.sub db = client["sub"] # 获取集合
t1=db.t1 # print(db.collection_names()) --- 查看当前所有的表 # 增加 insert_one # ob=t1.insert({"name":"张三"}) # 数据的ID 对象
# t1.insert_one({"name":"张三"}) # 数据的ID 对象
# print(ob) # 5a06c29a36dc0d1b24ab4132 ObjectId("5a06c26436dc0d1a5c4891ab"), # print(t1.insert_many[,,,].inserted_ids) inserted_ids -- 返回id # 修改 update_one update_many 全部修改 # t1.update({"name":"李四"},{"$set":{"name":"张三"}})
# t1.update_many({"name":"张三"},{"$set":{"name":"李四"}}) # 删除 delete_one delete_many # t1.delete_many({"name":"张三"}) # 查询 find # find() 得到的是一个 迭代器 # # cursors=t1.find({"age":{"$gt":1}}).skip(1).sort("age",-1)
# for cur in cursors:
# print(cur["name"]) from pprint import pprint for item in cursors:
print(item) # 格式化显示 数据

3 redis

  • 简介:

Redis 是基于内存的数据库(数据和key放在内存里)。内存满了以后就会开始自动删除数据

url一般放在redis里面,爬取到的数据放在MongoDB里面

MongoDB,只用来保存数据。爬到的数据存到里面就不管了。

Redis, 更像一个队列,有源源不断的URL进去,有源源不断的URL出来,

URL用过一次以后不需要,随着爬取到的数据一起存入MongoDB里面

MongoDB 不是内存型数据库,只不过把所有文件索引存到内存里而已。

同样的机型,用MongoDB会比Redis存更多,但Redis响应更快。关键的是看量有多大

可以把MongoDB当成MySQL, 把Redis当成内存

数据交换用Redis. 数据持久化用MongoDB

  • 下载安装预配置

下载:

github  windows版本  https://github.com/MicrosoftArchive/redis

下载解压完成

启动 redis :

 redis-server     安装路径+ redis.windows.conf

安装 redis windows 服务:

redis 的安装与部署 6379

windows 下

===服务端===

配置 service
redis-server --service-install D:\redis\redis.windows.conf
删除 service
redis-server --service-uninstall 启动redis-server --service-start
停止 redis-server --service-stop ===客户端=== 启动 redis-cli
退出 exit =============================================== 配置文件 redis.conf daemonize yes 后台运行 pidfile --- 记录pid port bind ip loglevel 日志信息 save 900 1 900s 1次写操作 -- 物理写操作一次
save 300 10
save 60 10000 dbfilename 默认物理存储 路径 ==================================================================================== redis-server --service-install redis.windows.conf # 安装为 windows service服务 redis-server --service-uninstall # 卸载 redis-server --service-start # 启动 redis-server --service-stop # 停止

redis 的配置文件:

daemonize no -->> daemonize yes   是否在前端或后端运行(守护进程)

bind 127.0.0.1  默认是绑定本机  注释后 允许slaver端 连接到 master端

redis master slaver 测试:

redis-server 路径+redis.windows.conf   #启动 master server 端 redis 

redis-cli    启动 mster client 端 

set key 123

get key ---123

redis-cli -h ip                  #启动 slaver 端 redis
get key --123
  • redis 语法:

      # 数据库切换 select 1 (默认36个数据库 默认在0)
    
      # 数据类型
    
        string 是基本数据类型
    
            是 redis的基本数据类型
    
            最大能存储 512 MB的数据
    
            string 类型是二进制安全的  -- 可以为任何数据 数字 图片 序列化 对象
    
      基本命令:
    
          设置键值:
    set key value 获取 值
    get key --value
    mget key1,key2 -- value1,value2 # 获取多个值 运算: incr key --- 将 key 对应的 value 加 1
    incrby key increment 将 key 对应的 value 加 increment decr key ---- 将 key 对应的 value 减 1
    decrby key decrment 将 key 对应的 value 减 decrement 追加--在原有的基础上添加 值: append key value 在原有的基础上添加 值 字符长度: strlen key 键命令: keys * 查看当前所有的key 通配符 *
    keys *1* 正则匹配 exits key 是否存在 1 0 真假 type key 查看键 对应值的属性 expire key seconds 设置 有效时间 ttl key 查看剩余 时间 del key 删除 键值对 ============================================== hset 字典 hash 命令:( 存对象 属性+值 )
    hash用于 存储 对象 ; 对象的格式是 键值对 命令:
    设置: hset key field value | hset py name "allp" hmset key field value [field value] 获取
    hget py name -- allp hgetall py 获取所有的属性和值 --- 奇数的是属性 偶数的是值
    hkeys py 获取所有的属性
    hvals py 获取所有的值 hlen py 属性的个数
    hstrlen py name ---某个属性对应的值得长度 hexists py name ----是否存在属性
    删除:
    hdel py name ---删除某一个属性 ============================================================ list命令:( 数组-- 两侧都可以进入数据) 一个键 维护多个值
    添加的 顺序
    设置: lpush py1 alex
    rpush py1 egon linsert py1 before alex name
    linsert py1 after egon male lrange py1 0 -1 查看所有元素 blpop py1 alex
    brpop py1 egon # 阻塞 时间 timeout lpop py1 # 不会等待
    rpop py2 llen py1 # 列表长度 lindex py1 index ltrim py1 0 3 只保留 索引 0到3的数据 其他的都删除 ====================================================== set 命令 (无序 唯一不重复 ): 设置:
    sadd key members(可以多个)
    获取:
    smembers key scard key 元素个数 交集:
    sinter py1 py2
    差集:
    sdiff py1 py2
    并集:
    suinion py1 py2 判断是否在某个集合中
    sismember py1 haha spop py1 随机删除一个元素 并返回 srandmember py1 3 随机取出3个元素 =============================================================== zset 有序集合: sorted set
    按照权重排序 设置:
    zadd myset 权重 "xxx" 权重 "ooo"
    zadd zcourse_set 0 scrapy 1 django 5 scrapy-redis 获取:
    zrangebyscore zcourse_set 0 1 # 权重 zrange myset start stop # zrange py1 0 -1 zcard myset ---查看元素个数 zcount myset min max --返回权重 在 min 和max之间值 的 元素个数 zscore myset xxx 查看元素的权重 redis 内部实现发布订阅 信息推送 消息的格式:
    类型:
    subscribe:订阅成功
    unsubscribe:取消订阅
    message: 其他终端发布消息
    命令:
    订阅:
    subscribe py111 可以多个频道 取消订阅:
    ctrl c 发布:(另外的一端 发布消息)
    publish 频道 消息 # redis.conf 配置主从 主
    修改 绑定
    bind ip ==== 主 master 从
    bind ip ==== 从 slavor
    slaveof 主ip port 登录 redis-cli -h ip主 redis-cli -h ip从 自动备份主的数据 自动完成 数据备份
  • 与python的交互

      # import redis  # 引入模块
    
      # 连接
    
      # r=redis.StrictRedis(db=0,host="localhost",port=6379,)
    
      # 数据操作 1 
    
      	直接操作
    # r.set("0","0") # 数据操作 2 pipeline 缓存多条命令 然后一次性执行 # pipe=r.pipeline()
    # pipe.set("name","hello")
    # pipe.get("name")
    #
    # pipe.execute() # print(r.ping()) # 自己实现封装 # class myRedis:
    # def __init__(self,host,port):
    # self._redis=redis.StrictRedis(host,port)
    # def set(self,key,value):
    # self._redis.set(key,value)
    # def get(self,key):
    # return self._redis.get(key)
    # myr=myRedis("127.0.0.1",6379)
    # print(myr.get("py5").decode("utf-8"))