MySQL
单机Mysql:
Memcached + MySQL+垂直拆分
主从读写分离
SQL的优势与局限:
优势:
- 保持数据的一致性
- 以标准化为前提,数据更新开销小
- 可以进行JOIN等复杂查询
- 存在很多实际成果和专业技术信息
补充:
Oracle Timesten 、 Altibase、SolidDB、extremeDB
局限:
- 难以实现大量数据的写入处理
- 难以为有数据更新的表做索引或表结构变更
- 难以实现字段的不固定时的写入
- 难以对简单查询需要快速返回处理结果
分表分库+水平拆分+MySQL集群
重难点
- CAP定理:重点
- 最终一致性技术基础:难点
- Nosql的分类与特点:重点
Nosql概述
Nosql = Not Only SQL
nosql数据库的主要驱动力是其能运行在大型集群上。随着数据量的增加,垂直扩展受到局限,需要将数据库部署在分布式系统中;
Nosql数据结构模式
模式 | 描述 | 应用 | 代表数据库 |
---|---|---|---|
键值存储 | 将一个大型文件和一个简单的文本字符串关联起来的简单方式 | 字典、图像、查询缓存 | Redis、MemCache |
文档存储 | 通过单个单元来存储树形结构的分层信息的方式 | 数据本身自带结构,办公文档、订单、发票 | MongoDB、CouchDB |
列族存储 | 通过一个行号和列作为键,来存储稀疏矩阵数据 | Web信息爬取、大型的稀疏表 | HBase、Cassandra |
图存储 | 一种存储图的顶点和边的方式 | 社交网络、朋友圈查询 | Neo4j |
CAP定理:
- Consistency:一致性
- Availability: 可用性
- Paratition Tolorency :分区容错性
组合:
CA:传统关系型数据库 – RDBMS
AP:大型网站 – CouchDB、Cassandra、DynamoDB
CP:牺牲可用性 – MongoDB、HBase、Redis
ACID
- A:原子性
- C:一致性
- I:隔离性
- D:永久性
BASE:
- BA: 基本可用性
- S: 软状态
- E: 最终一致性
最终一致性技术
- 严格一致性
- 读写一致性
- 会话一致性
- 单调一致性
- 最终一致性
策略:
Paxos算法
Raft算法
向量时钟
图数据库
特点:
- 采用非关系的图结构直接存储数据、用节点和边表示数据;边由属性描述,节点有多种不同的标签,节点和边之间存在多种不同的类型
- 支持基于路径的高性能便利访问方法
- 支持横向扩展、支持多种开发语言
- 支持跨平台部署、简单易用
图领域:
- 图数据库:主要用于联机事务图的持久化技术,通常直接实时地被应用程序访问
- 图计算引擎:主要用于离线图分析技术
图数据库的逻辑架构
- 节点:E-R图中的实体,包含零个或多个属性,包含相应标签
- 关系:E-R图中的关系,有起始和终止节点,包含属性和标签
- 属性:描述节点或关系的信息属性
- 标签:节点、关系的类别
- 索引:与SQL中索引相似
- 路径:遍历执行时的具体顺序
Neo4j特点:
- 兼容ACID图形数据库
- java实现,AGPL许可
- 面向图设计存储和检索方式,可以快速实现基于图的计算
- 免索引邻接机制:
将关系预先保存到关系列表中的免索引机制,来保证关系查询的性能。
1.数据库中的每个节点都会维护与它相邻节点的引用
2.每个节点都相当于与它的相邻节点的微索引,查询时间和图的整体规模无关,只与邻近节点的数量成正比
Cypher
- 其语法在于要从图中查询什么目标数据,而不是怎么去查
特点:
- 丰富的数据类型、库函数
- 命令灵活多样,简单的语法能实现复杂的路径查询操作
- 关键词大小写不敏感,习惯大写
- 属性名称、标签与关系类型名称区分大小写
- 支持变量定义,变量名称大小写敏感,由字母、下划线、数字构成,
必须字母开头
,变量可在整条语句中使用
CQL语句构成:
定义查询结果
定义查询条件
进行聚合
定义排序
Match (n)
WHERE id = 1 and age = 18
RETURN n
不适应场景:
- 记录大量基于时间的数据,如日志、传感器时间序列
- 对于大规模PB级数据进行分布式处理
- 音频、视频等二进制数据存储
- 适合SQL的结构化数据
Neo4j
Neo4j数据操作基础
创建节点
- 无属性节点
create (node_name:label_name)
#ex:
create(emp:Employee)
- 属性节点
create( node_name:label_name { name:value,name:value,...})
#ex:
create(
dept:Dept{
deptno:10,dname:"datascience",location:"taiyuan" })
create(
n:Person{
name:"wu jing",born:1974 })
return n ;
- 多标签节点
create(
andy:Person:Student:Writer{
name:"andy",age:23 });
- 使用UNWIND子句展开一个集合为一个列表,一次创建多个节点
unwind [ {name:"Alice",age:32},{name:"Bob",age:42 } ] as row
create(n:Person)
set n.name = row.name,n.age = row.age
return n;
- with做承前启后的作用,把上一句输出传到其他子句中去
with ["a","b","c"] as coll
foreach(value in coll | create(:person{
name:value }))
- 带有时间函数,创建时间属性的节点
with apoc.date.Format(timestamp(), 'ms' , 'yyy-MM-dd HH:mm:ss' , 'CTT') as createTime
create(testnode:TestNode{
pro:"test",createTime:createtime })
return testnode ;
- 创建上图中的节点
create(sally:Person{
name:"Sally",
age:23})
create(john:Person{
name:"john",
age:27})
create(gdb:Book{
title:"Graph database",
authors:[sally,john]})
查找节点
Match(node_name:label_name)
where condition bool-operation condition
return node_name,node_property
- 查询id=1的节点
Match(n)
where id(n) = 1
return n ;
- 查询所有节点
Match(n)
return n;
- born属性小于1955的节点
Match(n)
where n.born < 1955
return n;
- 调用apoc函数,查询节点标签
Match(n{name:"Tom"})
return apoc.node.labelsn(n);
- 查询具有指定label的节点
Match(n:Movie)
return n;
- 查询节点是否有指定属性
Match(n{name:"Tom"})
return n;
- 关系查询
Match(n:Movie)
where n.released >= 1990 and n.released < 2000
return n.title
- 正则表达式
Match (n)
where n.name = "(?i)Wang.*"
return n Limit 5;
- skip和order
Match(n)
return n
order by n.name
skip 3
limit 5 ;
重点:where,set 语句在return之前;limit、skip 、order by 语句均在return语句后进行
- 合并查询结果
Match(p:Person)
return p.age,p.name
union
Match(p.customer)
return p.age , p.name ;
- key函数查看节点或者关系的属性键
Match(a)
where a.name = "kkk"
return keys(a);
节点修改
- set可增加属性,可修改属性,可复制节点(除id)
create(p:Person{name:"aaa",sex:"female"})
where id(p)=1
set p.age , p.sex="male"
return keys(p)
节点删除
Match(andy:Person{name:"andy",age:18})
#Remove andy
remove andy.age
remove andy:Person
创建关系
Create (node:label) -[关系变量:relationship_label] -> (node2:label2)
return relationship;
- 没有关系属性的节点间关系
Match(a:Person),(b:Movie)
Where a.name = "aaa" and b.title = "bbb"
create (a)- [r:directed]->(b)
return r ;
- 创建关系时,如果没有where子句,将会在任意开始节点及终止节点之间建立关系,创建完全图,每条边都有不同的id
- 创建有关系属性的节点间关系
create(node:label){properties list} - [relation_name:label]{relation-properties-list} -> (node2:label2){...}
return relation_name ;
create(a:Book{name:"面向程序设计"})-[r:base{weight:3}] -> (b:Book{name:"web开发"})
return a,r,b ;
- 匹配节点,创建关系
Match(a:Person),(b:Movie)
where a.name = "Tom" and b.title = "forrest gump"
create (a)-[r:acted_in {roles:["forrest"]}]->(b)
return r;
关系查询
- type函数返回关系类型
Match()-[r]-()
return type(r),r ;
Match(a:Person)-[r:acted_in]->(b:Movie)
where a.name = "Tom"
return b;
- 双向关系查询
Match(a:Person)-[ar:acted_in]->(b:Movies)<-[ar:acted_in]-(Coactors)
return Coactors;
- 查找直接关系
Match(a:Person{name:"Tom"})-->(b)
return a,b;
- 孤立节点
Match(n) where not (n)--(n)
return n;
- labels函数查看节点标签
Match(:Person{name:"kkk"})-[r]->(m)
return apoc.node.labels(m);
- properties()函数,查看关系属性
Match(:Person{name:"kkk"}-[r]->(movies))
return properties(r),r.roles;
复杂关系查询案例
- 查询我的朋友中还不是我朋友的人
create(a:Person{name:"wang"})-[:fof]->(b:Person{name:"zhang"})
-[:fof]->(c:Person{name:"li"})
Match (a:Person{name:"wang"})-[:fof]->()-[:fof]->(b)
where not (a)-[:fof]->(b)
return b.name ;
- 帮助Tom引荐和cruise以前和他们都合作过的演员(他俩有合作演员的交集)
match (
tom:Person{name:"Tom"})-[:acted_in]->(m)
<-[:acted_in]-(coActors),
(coActors)-[:acted_in]->(m2)
<-[:acted_in]-(cruise:Person{name:"Cruise"})
- 向Tom推荐还没和他合作过的演员
Match(
tom:Person{name:"Tom"}-[:acted_in]->(m)
<-[:acted_in]-(coActors)
)
where not (tom)-[:acted_in]->()<-[:acted_in]-(coActors)
return coActors.name as Recommended,count(*) as strength
order by strength desc;
关系删除
先查再删
Match (n:Student{name:"wang"})-[s:Study{year:4}]->(c:Course{name:"Nosql"})
delete s.year,s;
- delete可以删节点、关系、属性
- remove只能删属性
删除指定关系:
#查
match()-[r]->() return type(r)
#删
match()-[r:acted_in]-()
delete r;
路径操作
- nodes(path)函数返回路径中所有的节点
- relationships(path)函数返回路径中所有关系
match p = (a)-->(b)-->(c)
where a.name = "aaa" and c.name = "ccc"
return nodes(p),relationships(p);
- 运算符* ,表示以某个节点为中心查询与该节点存在指定长度的节点
[:fof*1..2] 距离为1或2
[:fof*..15] 距离小于15
() - [ r ] - ( ) 忽略方向
- foreach子句
match p = (begin)-[*]->(end)
where begin.name = "陈五" and end.name="xx"
foreach (n in nodes(p)|set n.marked = true)
文档数据库
- Mongodb非常适合树形结构数据的存储,不需要复杂费时的跨表链接就可以高效访问相关数据
JSON
JOSN是一种轻量级的数据交换格式,类似于C语言结构体系的名称、键值对表示方法,支持内嵌的文档对象和数组对象,数据内容可以嵌套的KV文本形式存储。
- 优点是数据简单,易于读写,层次结构清晰,格式都是压缩的,占用带宽小,易于程序解析;
BSON
BOSN,主要被用于MongoDB数据库中的数据存储和网络传输格式。
- 优势:在于BOSN将每一个元素的长度存在元素的头部信息中,这样基于读取到的元素长度经过计算就能直接定位到指定的内容上进行读取,数据访问效率更高。
MongoDB概念
默认数据库:
- admin: 管理员操作数据库
- local: 不会被复制,所储存的集合只能在各自的服务器实例上
- config: 分布式分片设计,用于存储配置相关的信息
- test: 默认连接的数据库
数据关系
MongoDB使用嵌套和引用链接两种方式表示文档数据之间的关系;
- 嵌套:指文档利用符合类型,嵌套的数量和深度没有限制,MongoDB目前版本限制一个文档最大为16MB,优点在于维持数据逻辑上的完整性,可以将一整项数据作为一个整体操作;
- 引用链接:更接近关系型数据库传统意义上的“引用”,是两个文档之间的关系。引用链接通过DBRef对象建立,对象中存储如何根据数据库名称、集合名称及文档对象标识找到目标文档的引用信息。
集合
集合:相当于一个没有固定模式的表,包含多个文档,集合模式自由,结构可变,在同一集合中可以有多种结构的文档;
- 每个集合用名称来唯一标识,名称是任意的UTF-8字符;
文档
文档相当于RDB中的一行,每一个文档都有一个特殊的Key,即“_id”,用来作为文档在集合中的唯一标识,用作主键,其值唯一、不可变,可以是除了数组之外的任何类型;
标识
MongoDB中集合在主键设计上并没有采用自动增长的主键,因为在分布式服务器之间做数据同步很麻烦,采用ObjectId的方式,使用12字节存储文档的唯一标识,包含产生时间、机器标识、进程和自动序列4个内容;
3个层面
数据库操作
集合操作
MongoDB集合的创建很灵活,不一定先创建集合再插入数据,可以直接插入数据;如果集合不存在,则会自动先创建集合,再插入文档数据;
db.createCollection(“ilog”,{capped:true,size:6142800,max:10000})
文档操作
MongoDB
bin
- Bin目录下的mongod.cfg文件为数据库配置文件,使用文本编辑器修改配置,包括数据库多节点部署地址、分片、复制集、安全管理、网络接口等信息。
文档操作
CURD操作
- 新增文档
#insert可单可多,字典形式
db.ct.insert({"name":"wang","age":28})
db.ct.insert({"name":"aa","age":18},{...},{...})
# insertmany是列表
db.ct.insertMany([{"name":"zhang","age":22},{"name":"zhao",age:32}])
插入校验
-
插入数据时,MongoDB只对数据做基本检查:检查文档的基本结构,若没有“_id”字段,自动增加。
-
且所有文档的大小必须小于16MB,为了防止不良的模式设计,且保持性能一致。
MongoDB只进行基本检查,所以很容易插入非法数据,因此只允许信任的源连接数据库。主流语言的所有驱动程序,都会在将数据插入到数据库之前做大量的数据校验。
查询文档
- 查找所有
db.ct.find({}).pretty()
db.person.find()
db.person.find({" ":" "})
- 查询单个
db.ct.findOne({"age":32})
db.ct.findOne({"age":32,{"name":1}})
# 查询并排序
db.ct.findOne().limit(2).skip(3)
db.ct.findOne().sort({"age":1,"name":18})
嵌套查询
#精确匹配查询
Db.person.find({"phone":{"home":123,"mobile":321}})
#点查询,用.代表属性,简化嵌套查询
Db.person.find({"phone.home":123})
查询数组
db.person.find(
{favorite_number:20})
db.person.find(
{favorite_number:{$all:[6:8]}})
db.person.find(
{favorite_number:[6:8]})
#return size arry
db.person.find(
{favorite_number:{$size:3}})
#return subarray
db.person.find(
{favorite_number{$slice:2/-2}})
#find语句后可做限制
Db.person.find(...)
.skip(x)
.limit(x)
.sort({age:1/-1})
删除文档
# delete all documents
db.ct.drop()
db.ct.remove()
remove可以删除集合中的所有文档,但不会删除集合本身,也不删除集合中的元信息。
- 删除指定文档
db.ct.remove(”age”:{$gt:29})
删除数据是永久的,不能撤销,也不能恢复。
删除速度:删除文档通常很快,但是如果要清空整合集合,drop直接删除集合会更快。
其代价是:不能制定任何限定条件,整个集合都会被删除,所有元素都消失。
修改文档
- 修改器
Db.collection.update(
<query>, //update的查询条件,类似sql update查询内where后面的。
<update>, // update的对象和一些更新的操作符(如$,$inc...)等
参数如下:
{
Upsert:<boolean>, //可选,这个参数的意思是,如果不存在update的记录,是否插入objNew,true为插入,默认是false,不插入。
Multi:<boolean>, //可选,mongodb 默认是false,只更新找到的第一条记录,如果这个参数为true,就把按条件查出来多条记录全部更新。
writeConcern:<document>, //可选,抛出异常的级别。
collation:<document>, //参数可选,用于定义查询执行时的语言规范、字符串比较是否区分大小写。
arrayFilters:[<filterdocument1>,...]}) //参数可选,细化配置指定更新数组中满足条件的元素。
案例
db.ct.update(
{"_id":objectId("5c..")} # query
{"Name":"yan","age":38}) # update
db.ct.update(
{"Name":"c"},
{$set:{"Name":"ccc","age":32}},
false, 如果没查到,是否新增
false) 更新第一条,还是全部
- 使用updateMany为集合增加一个新的字段
db.ct.updateMany(
{},
{$set:{inroll_date:new Date()}}
)
- 使用updateMany为集合删除一个新的字段
db.ct.updateMany(
{},
{$unset:{inroll_date:” xxx”}}
)
语句块&游标
Print("Hello world!")
Tojson(new Object())
Tojson(new Object("a"))
- 游标
db.ct.find().forEach(Printjson)
var curson = db.ct.find()
printjson(curson[4])
Curson.count()
Curson.length()
- 循环结构
For(var i=0 ,len = cursor.length ; i<len ;i++)
printjson(cursor[i]);
var arr = db.ct.find().toArray()
printjson(arr[2]);
文档聚合和管道操作
文档聚合计算常用管道操作符,每个操作符都会接受一连串的文档,对这些文档做一些类型转换,最后将转换后的文档作为结果传递给下一个操作符,对于最后一个管道操作符,是将结果返回给客户端。
- 不同的管道操作符可以按任意顺序组合在一起使用,而且可以被重复任意多次。
MongoDB提供三种聚合运算:聚合管道、单一目的的聚合方法、MR函数
聚合管道
- 管道是指通过一个处理序列,分阶段、分步骤地将一系列文档处理成所需要的结果,类似面向对象编程思想中方法链的依次调用过程。这些构件包括筛选、投射、分组、排序、限制和跳过。
案例:
有一个保存杂志文章的集合,要找出发表文章最多的作者,且每篇文章被保存为MongoDB的一个文档,可以按照如下步骤创建聚合管道;
(1)将每个文章文档中的作者投射出来;
(2)将作者按照名字顺序,统计每个名字出现的次数;
(3)将作者按照名字出现次数降序排列;
(4)将返回结果限定为前5个;
db.articles.aggregate(
{"$project":{"author":1}},
{"$group":{"_id":"$author","count":{"$sum":1}}},
{"$sort":{"$count":-1}},
{"$limit":5})
- project比普通查询功能更加强大,使用project可以从子文档字段中提取字段,可以重命名字段,还可以在这些字段上进行一系列有意思的操作。
- $project可以指定包含或不包含一个字段,它的语法与查询中的第二个参数类似。
管道
MongoDB不允许单一的聚合操作占用过多的系统内存,如果发现占用20%以上的内存,这个操作就会直接输出错误。允许将输出结果利用管道放入一个集合中是为了方便以后使用。如果能够通过$match操作迅速减小结果集的大小,就可以使用管道进行实时聚合。由于管道会不断包含更多的文档,会越来越复杂,所以集合不可能实时得到管道的操作结果。
# 去重
db.ct.distinct("item")
db.ct.find(
{status:"A"}).count()
MongoDB索引机制
数据库中通过给集合创建索引能够提高文档数据查询性能和效率,是一种空间换时间的方法;
- 索引是对数据库表中一列或多列的值进行排序的一种结构
- MongoDB采用B-tree索引,各个数据库创建的索引存储在系统自动创建的system.indexes集合中,每个集合的_id字段默认创建索引。
- 创建和维护索引需要一定的时间和空间,为提高索引使用效率,需要分析日常业务数据查询操作的执行计划。对于某些写入频繁的操作,在某些场合下,索引反而会有副作用,会降低数据写入数据库的性能。
全文索引技术
MongoDB针对文本类型的字段支持全文索引的创建与维护,一个集合只允许创建一个全文索引。
- 创建全文索引
db.collection.createIndex(comments:”text”)
A.查询包含bad的文档
>db.collection.find({$text:{$search:”bad”}})
B.查询包含bad或spoiled的文档
>db.collection.find({$text:{$search:”bad spoiled”}})
C.查询包含bad和不包含ok的文档
>db.collection.find({$text:{$search:”bad -ok”}})
D.查询包含not ok完整词组的文档
>db.collection.find({$text:{$search:”\”not ok\ “”}}
MongoDB数据库的架构
分片与集群
- 分片:是指将数据拆分,将其分散存储在不同的机器上,也可称为分区。
- 几乎所有数据库软件都能进行手动分片,应用需要维护与若干不同数据库服务器的连接,每个连接都完全独立。
- MongoDB的数据分片以集合为单位,集合中的数据通过片键被分成多个部分,片键必须有索引。
chunk
MongoDB数据存储的横向扩展机制中,会把数据分为chunks,每个chunk代表这个shard server内部的一部分。
- Chunk的产生主要有
- splitting,当一个chunk的大小超过配置中的chunk size时,MongoDB的后台进程会把这个chunk切分成更小的chunk,避免过大;
- balancing,在MongoDB中,balancer是一个后台进程,负责chunk迁移,从而均衡各个shard server的负载
组件
- A.shard: 用于存储实际的数据块,实际生产环境中一个shard角色可由几台机器组成一个replica set承担,防止主机单点故障;
- B.Config server:配置服务器,存储了整个cluster元数据,其中包括分块信息等;
- C.Router:前端路由,客户端由此访问接入,整个集群看上去像单一数据库,前端应用可以透明访问分布集群中的数据。
优势
- A.对集群进行抽象,实现用户对分布式集群中数据库的透明性访问;
- Mongos掌握统一入口的路由器,将客户端发来的请求准确无误地路由到集群中的一个或者一组服务器上,同时会把接收到的响应拼装起来返回给客户端;
- B.保证集群总是可以读写
- MongoDB的分片和复制机制结合使用,在确保数据分片到多台服务器的同时,也确保每份数据都有相应的备份,这样确保当有服务器故障时,其他的备份节点可以立即接替工作;
- C.使集群易于扩展
- 当系统需要更多的空间和资源时,MongoDB可以按需方便地扩展系统容量;
数据冗余复制集
- MongoDB复制就是将数据同步到多个服务器的过程
- 复制提供了数据的冗余备份,并在多个服务器上存储数据副本,极大地提高数据的可用性,并保证数据的安全性.
- 复制还允许从硬件故障和服务中断中恢复数据。
MongoDB各个节点常见的搭配方式为一主一从或一主多从。
主节点:所有写入操作都在主节点上,主节点记录在其上的所有操作,存储为oplog日志文件。
从节点:定期轮询主节点或许这些操作;
- 仲裁者不存储数据,只负责通过心跳包来确认集群中集合的数量,并在需要主服务器选举的时候作为仲裁决定结果。
副本集配置语法mongod --port “端口号” --dbpath “路由地址” --replSet “引用集合”
副本集添加语法rs.add(HOST_NAME:PORT)
复制集机制
- 当复制集内存活成员数量不足大多数时,整个复制集将无法选举出主节点,复制集将无法提供写服务,处于只读状态;
- 集群中的节点可以设置其选举为主节点的优先级,Priority为0时,将不会被选取,Priority的值越大,越容易被选取;
- Delayed节点,数据落后于主节点一段时间,用于错误恢复;
分布式系统
-
MongoDB提供基于文件的分布式存储系统
GridFS
,用于存储和恢复超过16MB的文件- GridFS用fs.files和fs.chunks两个集合存储一个文件。
-
MongoDB安装目录中的bin文件夹中Mongofiles.exe是一个GridFS管理工具,可实现二进制文件的存取。
例如添加文件
mongofiles.exe -d gridfs put song.mp3
查看文件
db.fs.files.find()
日志功能
Journaling有两个重要内存视图:private view和shared view,通过内存映射实现,后者修改磁盘上文件内容。
- 当写入操作时,MongoDB进程首先修改内存中的数据
- 若没有开启Journaling功能,系统将每60s自动刷新shared view对应的内存变化到磁盘;
- 若开启Journaling,会额外开启private view,将数据同步至此。
写入步骤
MongoDB写入操作关键步骤如下:
(1)数据写入到private view中,此时没有与磁盘连接,数据不刷新至磁盘;
(2)private view数据批量复制到Journal,该操作周期性完成;
(3)使用Journal日志中的写操作引起的数据文件变化更新到shared view中的数据;
(4)重新使用shared view 对private view进行映射,使其占用的空间恢复到初始值,实现周期性地将shared view刷新到磁盘,进行数据同步
(5)执行数据同步后,通知Journal日志已经刷入,一旦日志文件中只包含全部刷入的写操作,则不再用于恢复。
MongoDB管理
文档数据的导入与导出
Mongoexport工具实现文档导出,Mongoimport工具实现文档导入;
mongoexport -d dbname -c collectionname -o file -type josn/csv -f field
mongoimport -d dbname -c collectionname --file filename --headerline --type josn/csv -f field
Mongodump备份数据
mongodump -h dbhost -d dbname -o dbdirectory
MongoDB监控
MongoDB提供mongostat
和mongotop
两个命令来监控MongoDB的运行情况。
-
Mongostat状态检测工具在命令行下使用,会间隔固定时间获取MongoDB当前运行状态并输出,若发现数据库突然变慢或其他问题,首先用此命令查看数据库状态。主要监控指标除了包含体现数据库负载方面的每秒查询、更新、删除等操作数量类指标,还有内存使用情况、网络流量、数据库连接数等;
-
Mongotop内置工具可以用来跟踪一个实例,查看哪些读写操作耗费大量时间,监控结果会显示每个集合读写用时的统计数据。
键值数据库 Redis
逻辑架构
不同类型的键值数据库提供的键值操作有三类:
- (1)set:将值存储到key对应的内存空间中,通过键访问,若键的数据已存在,旧的数据替换;
- (2)Get:读取key对应的数据;
- (3)Del:删除key对应的键值数据;
支持的数据类型:
- List
- String
- Set
- Zset
- Hash
- Stream
基础
Redis服务命令:
(1)卸载服务:redis-server --service-uninstall
(2)开启服务:redis-server --service-start
(3)停止服务:redis-server --service-stop
(4)重命名服务:redis-server --service-name name
(1)geo:为Geo类键值数据提供丰富操作,支持基于地理空间索引半径查询,如使用geoadd命令添加地理坐标;geopos查询;geodist距离查询;georadius返回距离某地一定半径范围内的地理坐标哈希值,可以按距离排序;
(2)HyperLogLog:一种基数统计的算法命令,仅需12KB内存,返回264个不同元素基数。
(3)Connection:管理客户端的连接;
(4)Transactions:多个原子操作通过MULTI和EXEC指令包起来作为一个整体执行。
(5)Server:管理数据库服务
(6)Scripting:执行缓存中的Lua脚本;
键值管理
Key
字符串
列表
集合
散列
有序集合
Redis集群架构及管理
Redis集群采用无中心架构,每个节点保存数据和整个集群状态,每个节点都和其他所有节点连接。该架构有以下特点:
(1)所有Redis集群节点之间彼此互联,内部使用二进制协议优化传输速度和带宽;
(2)集群中节点的失效是在集群中超过半数的节点检测失效时才生效的;
(3)客户端与Redis节点直接连接,不需要中间代理,客户端也不需要连接集群所有节点,连接集群中任何一个可用节点即可;
(4)Redis集群把所有的物理节点映射到[0-16383]slot上,不一定平均分配,集群负责维护节点与槽及槽与值之间的映射关系;
Redis集群预分好16384个槽,当需要在Redis集群中放置一个K-V时,根据CRC16的mod16384的值,将这个key放到对应的槽中。
Redis的复制
全量同步和部分同步
- 全量同步:
用于处理第一次复制的情况
,通过让主服务器创建并发送RDB文件,以及向从服务器发送保存在缓冲区中的命令来进行同步; - 部分同步:
用于处理从服务器离线后重新连接的复制情况
,当从服务器在离线后重新连接到主服务器时,如果时间允许,主服务器可以将在从服务器断开连接期间执行的最新写命令发送给从服务器,从服务器在接收并执行这些写命令后, 就可以将数据库更新到与主服务器相同的状态,实现一致性;
Redis持久化方式:
RDB和AOF;
- RDB是在指定时间间隔内将内存中的数据集快照写入磁盘,可以手动执行或根据服务器配置选项定期执行。生成的RDB文件是一个经过压缩的二进制文件,通过该文件可以还原生成RDB文件时的数据库状态,其本质是一种快照备份机制,将数据写入二进制文件中;
- AOF将以日志的形式记录服务器所处理的每一个写操作,在redis服务器启动之初会读取该文件来重新构建数据库。使用AOF持久化时,服务器会将每个收到的写命令通过函数追加到文件中。
哨兵机制
- 监控、提醒、自动故障转移
使用哨兵监控管理机制,Redis哨兵是一个分布式系统,可以在一个架构中运行多个哨兵进程,这些进程使用流言协议来接收关于Master是否下线的信息,并使用投票协议来决定是否执行自动故障转移,以及选择哪个Slave作为新的Master。
Redis 主从模式
-
为了保证数据的高可用性,Redis集群支持主从模式,集群中一个主节点可以对应一个或多个从节点,一个从节点只能对应一个主节点,主节点提供数据存储,从节点则是从主节点拉去备份数据。当这个主节点挂掉后,就会从它的从节点中选取一个来充当主节点,从而保证集群不会挂掉。
-
在主从集群架构下,Redis需要进行主从同步,数据可以从主服务器向任意数量的从服务器上同步。从节点也可以有领带的从节点,这使得Redis可执行单层树复制。
-
由于完全实现了发布/订阅机制,从数据库在任何地方同步树时,可订阅一个频道并接收主服务器完整的消息发布记录。
-
同步机制是保障数据读取操作的高可扩展性和数据高可用性的技术基础。
Redis的使用场景
做缓存;
做计数器应用,使用命令原子性的特性;
实现消息队列系统,运行稳定、速度快的特性;
做实时系统、消息系统,使用Pub/Sub构建消息系统;
实现排行榜应用,有序集合;
做过期数据处理,将有序集合的值设置过期时间;
做大型社交网络,多种平台都需要Redis支持;
分布式集群中的session分离,满足一个Web应用系统被大规模访问;
列族数据库
区别
列族数据库与RDB最大的区别就是采用纵向分割的集群架构来存储大数据,相关的多个列组织在一起形成列族
- 列族内的列数据经常被一起访问,同一列族数据物流存储时往往被存储在相同节点上。
特点
列族存储使用行和列的标识符作为通用的键来查找数据
列族数据的一般特点:
- (1)高性能:适用于包含上亿级行、百万级列的大表数据存储,分布式存储具有负载均衡机制,保障数据访问的高效性;
- (2)高可扩展性:能够根据需求,灵活增加集群中的节点数量以提高集群吞吐量;
- (3)灵活的数据存储格式:借助谷歌BigTable的数据存储模式,能够适应结构化、半结构化和非结构化数据格式的存储。它可以根据需求变化,灵活修改数据模型,每行数据包含的列可不同
- (4)按修改时间戳,存储数据的多个版本:列数据存储时包含时间戳,主要作用是当有新数据覆盖的时候,不是直接将老数据从存储介质上删除,而是直接写入新数据。当需要查询时,通过时间戳可获得最新列值。老数据根据配置参数设置会在一段时间之后自行删除,这种机制也极大提高数据写入的效率;
- (5)便捷的数据分发:通过在多个数据中心之间复制数据,可以灵活地在需要时分发数据;
- (6)支持轻量级事务:一般支持分区内单行操作的事务管理机制;
列族存储的优势:
- 动态控制列族中的列
- 根据行ID、列名和时间戳确定数据值
- 控制数据存储位置
- 同一行内的读写都是原子操作
- 数据之间按行排列(一张表格只有一个排序方式)
Cassandra
Cassandra的特殊优点:
(1)没有单点故障:采用去中心化的环形拓扑存储,节点与节点之间是对等关系,无主从之分,不会出现单点故障;
(2)Cassandra数据库提供丰富的数据类型,也支持用户自定义类型;
(3)提供灵活的查询语言:使用Cassandra查询语言访问数据库;
(4)快速写入:Cassandra写入性能非常高,支持负载均衡策略,它执行快速写入,并可以存储数百TB的数据,而不牺牲读取效率。
(5)多数据中心:可以调整节点布局来提高数据中心整体安全性,一个数据中心出现问题时,备用的数据中心有每条记录的完全复制,可快速恢复,继续提供数据服务。
数据模型
从顶至下描述cassandra的数据模型:
-
集群(cluster):keyspace的容器
-
Keyspaces
- Keyspace是数据的最外层容器(包含system keyspace)
- System Keyspace:存储关于集群的元数据,包括:节点令牌、集群名、用于支持动态装载的keyspace和schema的定义、迁移数据、节点是否举荐成功。 System Keyspace无法手动修改。
- 键空间是一个命名空间,定义复制策略和Table的其他选项集合。包括:复制策略取值,复制因子,持久写入
- Keyspace是数据的最外层容器(包含system keyspace)
-
列族(column family)、 列(colimn)
- 列和列族
Keyspace是列族的容器;
列族需要在keyspace中预先定义; - 列族选项
列是最基本的数据单元;
列不需要预先定义;
列的排序;
列的数据结构;
- 列和列族
持久化
Cassandra不会使用回滚和锁机制来实现关系型数据的事务机制,相比较与提供AID,Cassandra提供最终一致性级别用户可配置,让用户决定为每个事务提供强一致性或最终一致性。
- 作为非关系型数据库,为了提供高可用和更快的写入性能,Cassandra支持行级别的原子性和隔离性。
应用场景
列族数据库适合的应用场景描述如下:
- (1)对象数据存储:如头条类、新闻平台类的新闻、网页、图片、音频、视频等数据的存储;
- (2)推荐场景中的画像类数据:如用户画像是一个比较大的稀疏矩阵,蚂蚁风控就是构建在HBase之上的;
- (3)日志类数据存储:如网页浏览日志、电商客户购买日志、音乐视频观看日志等;
- (4)时序数据存储;
- (5)状态跟踪类数据:如订单状态、快递、外卖、包裹物流状态等数据;
- (6)物联网状态和事件历史类数据:如汽车物联网数据、气象感知等物联网数据;
语法
Cassandra数据库用户自动以类型的创建、查询、修改、删除操作
(1)创建cqlsh:
test>create type address(
Province text
City test
Region text
houseNumber text
);
(2)查询所有UDT类型
cqlsh:test>describe types;
(3)查看某个类型
cqlsh:test>describe type address;
(4)修改某个UDT,如添加一个新列语法
alter type <name> ADD column cql_types;
cqlsh:test>alter type address ADD PostalCode text;
(5)修改某个UDT,如重命名某一列
语法 alter type <name> RENAME <column> TO <new_name>;
cqlsh:test>alter type address ADD PostalCode text;
(6)删除某一UDT类型,类型没有被使用的前提下,才能被删除。
语法:drop type <name>
示例:cqlsh:
test>DROP TYPE address
- 1.5CQL和SQL主要区别是:CQL不支持JOIN和子查询。
建表及增删改查
关键字:
Create
Select
Insert
Alter
Delete
Update
操作:
set col = xxx
where key = value
基本语法:CREATE TABLE [IF NOT EXISTS] <name>(
Column cql_type,
...
PRIMARY KEY (column,column...)
)[WITH property = value AND property = value ...
- 查看表结构信息
语法:DESCRIBE TABLE name
示例:cqlsh:ks_test> DESCRIBE TABLE users
(1)添加一列
语法:ALTER TABLE <name> ADD column cql_type;
示例:cqlsh:ks_test>ALTER TABLE users ADD temp varchar;
(2)删除一列
语法:ALTER TABLE <name> DROP column;
示例:cqlsh:ks_test>ALTER TABLE users DROP temp;
(3)删除多列
语法:ALTER TABLE <name> DROP (column,column...);
示例:cqlsh:ks_test>ALTER TABLE users DROP (height,temp);
墓碑
软删除(SQL):不直接执行delete,使用update语句。
Cassandra中,数据被删除时,执行更新操作,在相应的值上放置墓碑。墓碑是一个删除标记,当执行压缩操作时,比墓碑更老的内容都会被清理掉。墓碑配置的时间通常为10天。
- Cassandra的数据删除操作实际上并不是真的删除,它执行插入操作,插入墓碑,记录被删除记录的信息和删除时间。
- 根据条件查询数据的时候,它会满足条件的记录查询,包括墓碑,然后过滤掉删除的数据,再把结果返回。Cassandra中可通过压紧操作并合并SSTable,丢弃墓碑,创建新索引。
压缩
压缩可以节省空间,首先压缩可以被用在构成局部性群组的SSTable中,可以选择是否对个人的局部性群组的SSTable进行压缩。
- 压缩技术可以提升子表的恢复速度,当某个子表服务器停止使用后,需要将上面所有子表移至另一个子表服务器来恢复服务。
- 在转移之前要进行两次压缩
- 第一次压缩减少提交日志中的未压缩状态,减少恢复时间;
- 在文件正式转移之前还要进行一次压缩,将第一次压缩后遗留的未压缩空间进行压缩,完成两步之后的压缩文件就会被转移至另一个子表服务器中。