单机数据库
分布式数据库
TDDL原理与最佳实践java
B+树的特色是叶子节点是块状,一个叶子里面有多个数据,相邻数据是存在一块儿的,123,456起等,
而磁盘也是按块的,B+树的数据是按块存储的正好和磁盘的块的概念是相符的,
因此在数据库里面大多采用了B+树或者相似的一种结构来存储数据。
在java中实现treemap时选择是红黑树而不是B+树,B+面向的是磁盘的结构,
java的treemap面向的是内存,随机读写数据快;
另一个缘由是,B+树为了减小分裂的次数会在每一个叶子结点中预留几个空洞来存入将来的数据,
这个特色在磁盘中是能够的,由于磁盘都是白菜价,浪费一点没有关系,但内存就不同了;
红黑树本质是一颗二叉树,每一个叶子只有一条数据,不须要预留任何的空间,
在内存中就不会形成空间的浪费,也就是这两点,才选择了红黑树来实现java中的treemap.
任何一种技术脱离了它的场景都是没有意义的;mysql
有些数据库不是按行存的,是按列存的,这样可对列的特色进行压缩,减小存储空间,
进行max/min等运算也快,但这种特色不适合更新;
没有一种数据库读的快写的也快,读写性能的差距也是因为背后所使用的存储不一样致使的,
如hbase使用的是lsm-tree这种存储,它的特色是写的很快,顺序写,但读的稍微有点慢,
因此hbase常常用来存日志,产生的量,对读的性能要求不高。
要判断读的多仍是写的多,若是读的多,还要判断读的特色,是随机读仍是范围的读,写的话,
是只写仍是会有删除和更新;B树是面向磁盘的,红黑树是面向内存的;web
主表和索引表都是映射,查询若是使用到了索引,就会分为两步,第一步先查索引,第二步经过索引再查主表,
这一步叫回表。若是查询中的列(seletc a,b)都是索引中的列,那么就只有一步,只要查索引表就能够了,
就不须要回表了,这叫索引覆盖。 若是索引包含全部的列,也就是主键对记录的索引,就是聚簇索引。
若是索引包含部分的列,就是非聚簇索引, 非聚簇索引在查询的时候就可能须要作一个回表的操做才能查出全部的列来。
索引太多时有两个缺点, 占空间,对记录进行写操做时也要修改索引,写入性能的降低。
没有索引时就会全表扫描。
主表的每一条记录都是一个KV映射,K就是主键,V就是整条记录,而索引就是列对K的索引而已。
区分度比较高就比较适合作索引列。
从某种程度上说,索引也是一种关系型数据库的表。
关系型的数据库都有索引的概念,而NoSQL是没有索引的概念的,好比hbase就没有索引,只能按照rowkey来查,
不能按照其余字段来查。算法
sql语句会通过解析器,通过解析变成一个数据结构, 查询优化器会分析这个数据结构是什么意思,怎样执行会比较快,
会生成一个执行计划,这个计划就包括查字典的整个过程。
SQL->Optimizer->plan,底下的存储按照plan就会获得高效并且准确的结果。
那么查询优化器会作哪些事情呢?
1.索引选择
当where有两个或者多个列都有索引时,就会涉及到索引选择的问题。通常会根据区分度选择合适的索引。
2.下推
select t1.name,t2.name from t1 join t2 on t1.id = t2.id
就是说会让一些操做提早执行来减小中间过程的数据,也就是优化的过程。
3.join策略的选择(本身了解)
index netxt roup、block next roup,sql
mysql语句前加上extern就会展现这条语句的执行计划,能大概读懂
事务也是区分关系型数据库与nosql的指标数据库
产生分布式数据库的缘由,最重要的有两个:
1.单机的硬盘容量(可能还包括内存容量、cpu容量、网络容量)不够,须要存储到多块硬盘上
2.安全性缘由,数据库存到多个地方备份安全
RAID磁盘阵列的出现与分布式数据库的出现类似,一个是空间,一个是安全,
RAID0的概念是把数据拆开存储在两块硬盘上,好比C盘数据存储在A上,D盘存储在B上,RAID1的概念是把数据复制存储在两个地方,好比CD盘在A盘上存一份,也在B盘上存一份。
RAID01或者RAID10就是把二者结合起来,数据既分块,又对每块数据进行备份。
对应到分布式数据库里面,就有了这样的概念:
RAID0对应分布式数据库里的Partition(扩展性,一个分区不够,继续扩)
RAID1对应分布式数据库里的Replication(安全性,一个挂了,不会掉数据)网络
如何将数据复制到不一样的地方去?
1.异步?
2.同步?
假设有两块磁盘0和1(或者就是两个数据库),那如何保证两个数据是彻底同样的?
一种方式是同时向两个地方写,写完就是成功了;
另外一种方式是只写0,让0去写1,这就产生了如何返回数据写成功了?同步是0写完1才返回成功,
异步是写完0就返回成功(若是此时就去读1就会出现尚未写入的数据,就会出现延迟,好处是不用等全部的库都写完,它的写入的延迟会低一些),这是两个点的状况,若是有更多的点(就是把一个点的数据复制了好几份),则就不能接受只有同步或者只有异步(只写一个点就返回)的状况了,就要把同步和异步结合起来,即每次同步写数个点后返回成功,剩下异步写剩下的点。
那在分布式数据库中如何才能保证读到的数据是一致或者最新的呢?
W+R>N,W表明同步写的个数,N表示总结点个数,即写的少,读的就越多,写的多,读的就越少。数据结构
mysql是有主库和备库的区别的,主备库数据是彻底同样的。内部的方案是读写都在主库,备库所有采用异步的方式备份。这是牺牲了备库的读性能来换取的,能够讲到同步的数据又不要同步的复制,来保证写的性能的提高。app
若是主库挂掉的话,怎么从剩下的备库中选举主库呢?大可能是用PAXOS算法或者衍生的算法来选举主库,主要原理是获取多数支持的那一个才能是主库。这样就是自动的的过程,内部每每简单粗暴的,当主库挂掉的时候人工选择一个备库当主库。
partition和replication在单机数据库中也是存在的,在单机数据库是也有分区表的概念,虽然是同一个机器,也会把表分红了好几个部分, 在单机数据库里面也会用磁盘陈列的方式作一些冗余,在单机数据库里也会这两种概念,那在分布式数据库要把这两点单独的拿出来讲呢?最重要的是由于分布式数据库多了网络,延迟变大了,在单机里cpu总线的延迟是纳秒级,走网络的话延迟多是毫秒级的。正是由于有延迟在在分布式数据库中才会把这两个东西单独拿出来讲。
以上主要是replication的讲解;
partition的讲解在tddl里;
TDDL产生背景:
单一数据库没法知足性能需求(数据切分 读写分离(只写主库只读备库))
容灾(主备切换)
配置变动(动态数据源(不须要数据库的帐号信息,哪天数据库ip地址换了也不要紧 只须要依赖appname tddl会自动地读取数据库帐号用户名密码 当这些发生变动的时候会自动的通知到tddl,对用户无感知))
切分有两种水平切分和垂直切分
垂直切分不能无限切分, 由于列是有限的,而水平切分是能够无限扩展的, 只要行数是无限的。tddl能作的就是水平拆分,由于垂直拆分就是sql中的join操做
主备切换也是tddl的功能,当主库挂掉的时候,会自动切换到备库上。
水平拆分不必定按照id字段来拆,也能够选择其余字段,好比某个字段全是字符串,能够先求出它的hash值,再对1024取模(假如拆了1024张表,此时也能够再对表进行分库,如0-155属于库1),总之要按照字段自己的特色。
这就是所谓的拆分算法。
分布式数据库都会有两个特色一个是partition,一个是replication.
那么hbase使用了一种B树的拆分方式,也就是说hbase各个存数据的节点(也就是分表的表)能够看做B树里面的一个节点,它在路由的时候就会根据树的算法来算出这条数据应该在哪个节点里面,由于它使用了树,因此它继承了树的一些特色,因此在hbase中能够很方便来作一些范围的查询, 好比rowkey大于一个值或者小于一个值。但tddl默认使用的是一种hash算法,就很难作一个范围的查询, 好比1-1000在hbase中可能也就跨了几个分区布局,可是若是在tddl中可能就跨了全部的分区,由于hash算法是一种零散的算法。但为何内部会默认使用hash算法来分表呢,由于内部常常作的操做是根据买家id\卖家id\羊肉串id\定单id来查,这样的话用hash算法就比较快。
拆分字段的选择跟选索引差很少,选拆分字段的时候基本会选查询都会带上的那种列,好比说买家id卖家id,还有就是区分度的概念,用这个字段会路由到比较少的表中,而不是会路由1000张表。但若是像定单这种东西,它既有买家id又有卖家id,同时买家和卖家都会去关注,那选个字段做为拆分字段呢? 答案是两个都选,数据复制一份,一份按买家id来路由分表,另外一份按卖家id来进行路由分表,也就是数据会存两份,相对应的咱们就能够把买家和卖家理解为两种索引,由于是两种映射嘛。而且这种索引也能够作成聚簇,也能够作成非聚簇的,好比说在冗余的时候按全部字段进行冗余一遍,这样在查的时候就会查的比较快。
规则路由—扩容
怎样扩容才能减小数据的移动,好比之前是模32,如今变成模40了,就要把之前的结果按40从新模一遍,怎样选择才能减小数据移动呢?
通常作法是一倍一倍的扩,好比之前是32,扩容后就变成64了。由于这种状况下只有一半的数据是须要移动的。
多维拆分实际是把数据按照两维度冗余了一份
组合索引就是先按一个列索引,再按另外一个列索引
多个字段同时做为拆分字段,实际上数据只存了一份
两个字段就是二维,只提供一个字段就会定位到好几个张分表
三个字段就是三维
tddl三层数据源
最底层是atom层,对应的是一个数据库,对应的就是一个ip用户名密码,一主一备就构成了第二层,就会变成一个group,同一个group会包含多个atom,但atom数据是同样的,也就是说group会完成读写分离这样一个操做,也能够完成主备切换,这两层就是绝大数应用使用的模型,由于绝大多数应用是小应用不须要作拆分,只要作读分离和主备切换这样的功能。多个group以后会组成一个matrix层,当咱们分库以后,多个库会组成一个matrix,matrix层负责来作切分这样一个操做,就会有规则计算这样一个功能,就会算出sql应该到哪一个group去。这就是tddl三层数据源的一个概念。
TDDL5分为三层数据源结构
Matrix,主要负责数据的水平切分
Group,主要负责主备与读写分离
Atom,主要负责物理数据源的管理
TDDL5中一个查询操做在三层数据源中的流程以下图:
使用者经过JDBC,将SQL传递到TDDL5中,会按照如下流程进行执行:
SQL Parser会将SQL解析并生成关系查询树;
Query Optimizer对关系查询树进行优化,生成由KV查询组成的执行计划;
在这个过程当中,还将根据切分规则,对条件id=2与id=3进行计算,得出数据所在的节点。如在此例中,根据id % 3,得出id=2的数据在Group1上,id=3的数据在Group0上;
Client会将执行计划发送给Matrix层,Matrix层会按照执行计划中指定的Group,将执行计划发送给对应的Group,在Group层,因为非强一致的读请求,会将读操做随机分配到数据节点Atom上;
最终数据在Matrix上进行合并以后,结果返回给Client。
matrix层:
Parser
Optimizer
语法解析
规则计算
查询优化
Executor
聚合/Join/函数计算
Repo(存储层)
Mysql
Group:主备切换,读写分离
Atom:动态数据源
Oceanbase/Hbase/Skiplist/Search(支持多存储)
TDDL的查询优化器有一个最为核心的理念:offload,也是单机优化器里面讲到的一个基本的概念主是下推,下推的意思就是说尽可能多的操做会让mysql本身去执行,好比说一些条件的过滤,一些join能让mysql去作的话就让它先来作,好比说索引选择,列的过滤,一些聚合函数的计算,一些排序,一些distinct去重这样的操做,能让mysql来作就让它来作,为何呢?和单机数据库同样的道理,尽可能减小中间数据,减小跨网络的延迟,offload的思想也是贯穿tddl基本的理念。也是tddl最佳实践最基本的思想。