mysql在线扩容和缩容一般涉及到的内容,主要包括三个方面,1在线也就意味着需要把增量的数据重新分布到新的拓扑结构中,我们一般称做增量复制,2原有的数据需要一条不漏的扫出来重新分布到新的拓扑结构中,这个一般叫做全量复制,3全量做完,增量正在同步,把应用的数据路由拓扑切到新的路由拓扑上来,并且做到无数据丢失,这个我们叫做停写切换。做好这三个方面的工作,能够达到的效果就是应用在最后切换数据分布拓扑的时刻,只要停写非常短的时间(秒级别)就能够做到无数据丢失的扩容和缩容。
增量同步一般有2种方式,一种是应用端或者数据库前端做trigger,记录变更数据的特征值log(比如pk,sharding key),然后异步复制到新的拓扑结构中。另外一种方式是通过分析mysql的binlog再进行不同数据拓扑的复制。两者本质上来说应该是一样的,后者可能更加简便,并且对应用无侵入,前者虽然也能够做到,实际实现或者推广和 *** 作上都有不少阻力,最起码解析binlog方式是mysql一上去,更新的log已经天然存在与binlog中了。
增量同步的两种方式如果要考虑到同步的可伸缩性(也就是多台机器可以同时消费相同的变更日志),需要在原数据中添加数据的版本信息防止更新乱序,或者通过唯一键进行复制机器的sharding,也就是不同进程(线程)同时消费相同的更新日志,必须让同一条记录的更新落在同一个线程里面,如果还需要保证复制的事务,那么实现会非常复杂,一般不会去支持多线程下复制的事务。
全量复制,也就是扫描需要复制的表的数据进行重新分布,主要存在的问题是复制速度和对数据库的写入压力的矛盾,其实能够做到整个拓扑连数据库都全部换掉,来达到对正在使用数据库的0影响,这个是一种可行的方案,另外是分时段调整复制线程数,一般单线程复制对于数据库的影响不会很大,在凌晨再转换成多线程方式达到提速的目标。
扩容或者缩容在最后阶段如何切换,这个涉及到的问题主要是如何避免新更新进来以至于增量没完没了,方式有很多,最简单的方法就是停掉应用,一般时间只有几分钟是可以接受的。另外一种是逻辑停写,因为我们迁移的时候是有一个规则去重新散列数据,也就是如果新的规则和旧的规则两者算出来的结果不一致,那么这个数据就是需要被迁移的,如果在停写的时刻,向前端抛错即可。逻辑停写最大的好处就是避免PE的介入,并且配合动态的数据路由数据推送,可以完全避免重新发布达到扩容或者缩容,这个就是真正的在线扩容,停写不可避免(等待延迟的增量同步完成),但是不影响读。
数据扩容或者缩容,我们觉得不应该排入业务的开发日程中,而是由数据管理团队对应用透明地进行这种 *** 作,最后介入的人员只是DBA而已。但是不像一些nosql一样按容量或者完全透明的split,数据库的sharding还是按照应用的数据特性(pk,user_id,gmt_create等等不同字段,自选策略)进行sharding,应用知道他们的某条数据具体存在哪个机器哪张表上,这个无论对于开发还是测试或者DBA都是一件不错的事情。
受群里小伙伴之邀,搞一个分库分表案例,这样让很多没用过分库分表的心里也有个底,不然永远看到的都是网上的各种概念和解决方案性的文章。
由于用户表过于庞大,采取相关SQL优化,还是不能满足,所以现对其进行做分库分表。
数据库: my-sharding
数据库表: t_user
建表语句如下:
关于数据库分库分表通常有两种方案:
下面我们来演示水平拆分,大致思路:
加入有2000万条数据,那么为了方便演示,我们就暂定分为五个库,每个数据库对应五个表。
五个数据库:
每个数据库有五张表:
建表语句如下:
使用技术栈: JDK8 + MySQL + Spring Boot + Mybatis + Shardingsphere + Druid
maven 相关依赖:
配置文件相关配置如下:
分库分表的两个分片类:
下面是业务部分代码,先看 UserMapperxml 内容:
UserMapper 接口:
为了更好地演示,我这里加入了 controller 层和 service 层,这也是大家平常开发套路。
service 层代码如下:
controller层代码如下:
最后是项目的启动类:
启动项目,启动成功:
下面我们来演示一下新增数据和查询。
先来添加数据到数据库中,这里使用的是IDEA中restful工具:
后台日志:
再查看数据库表中:
到此,我们的数据依旧落库,下面我们来演示一下数据查询。
浏览器里输入:
返回数据:
后台日志:
从日志和返回结果可以看出,已经为我们正确的选择到对应的数据库和表了,这样,一个分库分表的查询就成功了。
本文没有太多的概念,直接使用案例演示。相关概念性的文章,还有分库分表解决方案的文章,网上一堆堆的,感兴趣可以自行查阅。
1 proxy sharding,目前由cobar,mycat,drds,atlas修改,这几个产品的起源一般是mysqlproxy 或 ameoba,特点是mysql协议基本兼容,业务不需要做太多修改,缺点是分库分表的算法很烂,业务要自己做大堆配置
2 jdbc中间件sharding,这个和协议差不多,就是把服务实现为了一个中间件,好处是协议损失时间可以补回来,坏处是只有java可以使用,开源的有当当的jdbc sharding
3mysql的ndbcluster和fabric,这是mysql 引擎层面做的sharding,直接用mysql的协议层和计划生成,这个做的好处是原生的协议层都是百分百支持,事务用mysql xa支持也算马马虎虎,坏处是单机引擎,不能支持大数据的sql
这3种,被统称为“share-nothing”的数据库sharing模式,share-nothing很容易理解,就是每个被sharding的数据库之间没有共享任何数据。这种模式的缺点,主要就是:
1 sharding的时候必须带sharding key,或者不带sharding key就全表扫描
2 必须预设sharding key,但是sharding key设计是很考验架构师对体系了解的,比如这里的sharding key 会和事务息息相关
3 对事务的支持仅限于单库,跨库事务要走二阶段,不支持read repeated和串行事务
4 扩容意味着sharding key计算方式改变,需要停写
5 调度角度来看,proxy部分cpu是很浪费的,流量大的时候容易变成瓶颈,流量小的时候cpu都浪费了
当然share nothing的模式也是有谷歌参与一腿的,那就是youtube的vitess,他用设计和取舍很好规避了上述的很多问题:
1 proxy 转换两次协议的浪费,vitess直接用grpc来做sql转发,这样不用自己转mysql协议
2 扩容难题,vitess用的是范围sharding,也就是拟定每个分库key值上下限,扩容只涉及三个库,扩容一般是split模式,不需要伤经动骨
3 语句支持,这个基本上能上层做的优化大家都有做,不赘述
4 运维和调度模式简单,部署模式用了 kubernetes的模型,proxy被暴露为service,后面多个服务都变成pod,把部署扔给了一个好的调度模型,而且里面mysql单例是启动在docker上,再把存储挂载在普通硬盘上,实现了存储和进程基本分离
5 事务不赘述,基本上其他几个能不能做是意愿问题,不是设计问题
6 自建二级索引,方便查找,方便不用先定sharding key
所以没有把vitess归类在上面任意一个层里,它比较独立,看起来vitess是不是完美了?并不是,毕竟share nothing模式的思路一开始就是有问题的,等我接下来细细分析为何vitess这个设计这么牛逼的项目 在谷歌基本上属于边缘甚至被淘汰项目
mybatis不是hibernate
实体类和持久层没有直接的关联
数据库表名和对象名本来就没有需要命名一样的要求
其实就是执行的sql语句而已,就算没有对象,直接用Map传值
也是可以执行的
应该是其它的问题吧
2015年8月份内部release了Oracle 122 Beta版本(目前内部最新release的版本是2016年2月份发布的,windows和Linux都有了),目前根据122beta文档的介绍,Oracle推出了sharding的功能,跟其他NOSQL型的sharding结构相比,Oracle Sharding提供的是企业级的RDBMS的分片技术。
Oracle Sharding的优点:
Relational schemas
Database partitioning
ACID properties and read consistency
SQL and other programmatic interfaces
Complex data types
Online schema changes
Multi-core scalability
Advanced security
Compression
High Availability features
Enterprise-scale backup and recovery
在Oracle RDBMS 12201中最多支持1000个shards。
Oracle Sharding使用GDS(Global Data Services)架构来自动部署和管理sharding和复制技术。GDS(GDS是Oracle RDBMS 121的新特性)也提供负载均衡和SDB(sharded database)中的基于位置的路由功能。
Shard目录(Shard directors)使用GDS framework的全局服务管理组件(global service manager component)来提供应用层请求到shard的直接路由。shard目录(Shard directors)是一个单独的数据库,它用来保存SDB(Sharding database)配置数据和提供其他相关功能,比如shard的交叉查询和集中管理。可以使用GDS是GDSCTL工具可以用来配置SDB。
Oracle Sharding的分区架构(Partitioning Infrastructure)分区在表空间级别跨Shards分布,每个表空间关联一个特定的shard。一个shard表的每一个分区放单独的表空间,并且每个表空间关联到一个特定的shard。根据不同的sharding方法,这个关联可以自动建立或者根据定义创建。尽管一个shard表的多个分区放在多个单独主机的数据库上(这些数据库完全独立,不共享CPU、内存等软件和硬件),但是应用访问表时就如同访问一个单独数据库中的分区表一样。应用发出的SQL语句不需要依赖shard号和shard的物理配置。
Oracle Sharding 使用 familiar SQL 语法创建表分区,指定分区表的每行数据如何分片。
一个shard表的分区键叫做sharding key,例如,下面的语法是典型的用来创建sharding表的:
CREATE SHARDED TABLE customers
( cust_id NUMBER NOT NULL
, name VARCHAR2(50)
, address VARCHAR2(250)
, region VARCHAR2(20)
, class VARCHAR2(3)
, signup DATE
CONSTRAINT cust_pk PRIMARY KEY(cust_id)
)
PARTITION BY CONSISTENT HASH (cust_id)
TABLESPACE SET ts1
PARTITIONS AUTO;
这个数据分片(shard)就是基于键值cust_id,分区采用“CONSISTENT HASH”,这是一个特定的hash分区类型,通常用在分布式系统上。
Sharding a Table Family
一个表家族(Table Family)中没有任何父表的表叫做根表(root table),每个表家族中只能有一个根表。
表家族中所有的表按照根表的主键进行sharding,根据各级表的结构,相关数据可以被存储在同一个shard上。
在122,在一个SDB中只支持一个表家族。
以下面的例子说明,这里一共3张表组成的表家族(Table Family):客户表,订单表和订单明细表。
每个客户可以有多个订单,每个订单中可以有多个商品,因此订单明细中就记录了每个订单中的多个商品,他们的具体数据如下:
在这个表族中,客户编号为123的数据如下:
将一个表族(Sharded Table Family)分片通常使有下面两种方法创建:
方法1:不显示指定父子关系,而是通过表之间主外键关系创建表族。
这种方式创建的表族是一个多级的树形结构。
根表(root table)是客户表:
–客户表的主键是CustNo,分区方式是“CONSISTENT HASH (CustNo)”
–保存再表空间集ts1中
CREATE SHARDED TABLE Customers ( CustNo NUMBER NOT NULL , Name VARCHAR2(50) , Address VARCHAR2(250) , CONSTRAINT RootPK PRIMARY KEY(CustNo) ) PARTITION BY CONSISTENT HASH (CustNo) PARTITIONS AUTO TABLESPACE SET ts1 ;–订单表是客户表的字表,子表(订单表)根据CustNo关联父表(客户表):
–订单表的主键是(CustNo, OrderNo),外键(CustNo)引用了主表Customers(CustNo)–分区方式是按照订单表的外键约束(CustFK)CREATE SHARDED TABLE Orders ( OrderNo NUMBER NOT NULL , CustNo NUMBER NOT NULL , OrderDate DATE , CONSTRAINT OrderPK PRIMARY KEY (CustNo, OrderNo) , CONSTRAINT CustFK FOREIGN KEY (CustNo) REFERENCES Customers(CustNo) ) PARTITION BY REFERENCE (CustFK) ;–订单明细表是订单表的字表,子表(订单明细表)根据CustNo关联父表(订单表)–订单明细表的主键是(CustNo, OrderNo, LineNo),外键(CustNo, OrderNo)引用了父表Orders(OrderNo)和Orders(CustNo, OrderNo)–分区方式是按照订单明细表的外键约束(LineFK)CREATE SHARDED TABLE LineItems ( CustNo NUMBER NOT NULL , LineNo NUMBER(2) NOT NULL , OrderNo NUMBER(5) NOT NULL , StockNo NUMBER(4) , Quantity NUMBER(2) , CONSTRAINT LinePK PRIMARY KEY (CustNo, OrderNo, LineNo) , CONSTRAINT LineFK FOREIGN KEY (CustNo, OrderNo) REFERENCES Orders(OrderNo) REFERENCES Orders(CustNo, OrderNo) ) PARTITION BY REFERENCE (LineFK) ;因此,上面的例子中,这个表家族的所有数据都保存在同一个表空间集ts1中。当根表中增加一个分区的时候,那么相关联的表中都会自动增加相应的分区。
方法2:在分区表中显示指定父子关系的方法创建表家族这种分区方法只支持两级的表家族(two-level table families),所有的子表必须有相同的父表,父表的分区列在每个子表中都存在,例如下面的CustNo
–没有关键字“PARENT”(也没有上面引用约束关键字)的是根表,即客户表(Customers)CREATE SHARDED TABLE Customers ( CustNo NUMBER NOT NULL , Name VARCHAR2(50) , Address VARCHAR2(250) , region VARCHAR2(20) , class VARCHAR2(3) , signup DATE ) PARTITION BY CONSISTENT HASH (CustNo) TABLESPACE SET ts1 PARTITIONS AUTO ;–根据关键字“PARENT Customers”指定了订单表(Orders)的父表是客户表(Customers)CREATE SHARDED TABLE Orders ( OrderNo NUMBER , CustNo NUMBER , OrderDate DATE ) PARENT Customers PARTITION BY CONSISTENT HASH (CustNo) TABLESPACE SET ts1 PARTITIONS AUTO ;–根据关键字“PARENT Customers”指定了订单明细表(LineItems)的父表是客户表(Customers)CREATE SHARDED TABLE LineItems ( LineNo NUMBER , OrderNo NUMBER , CustNo NUMBER , StockNo NUMBER , Quantity NUMBER ) ) PARENT Customers PARTITION BY CONSISTENT HASH (CustNo) TABLESPACE SET ts1 PARTITIONS AUTO ;Creating a Duplicated Table Using CREATE TABLE复制表可以被复制到所有的shard上,这种在每个shard上有相同内容的表叫做复制表(Duplicated Table),需要经常跟shard表关联的小表适合于作为复制表(Duplicated Table),适用于:
(1)只读表
(2)大量跨shard的读 *** 作
Oracle Sharding使用Materialized View Replication来同步复制表(duplicated tables)的内容,每个shard上的duplicated tables的内容是一个只读物化视图(read-only materialized view)。
物化视图(materialized views)的主表保存在一个专门的数据库中,叫做Shard Catalog。
所有shard上的物化视图(materialized views)会根据配置的频率自动刷新。
创建复制表的语句“CREATE DUPLICATED TABLE”会自动创建master表,物化视图和其他物化视图复制所需要的对象。
还是以上面的客户订单关系为例,这里定义产品表(Products)为复制表:
CREATE DUPLICATED TABLE Products ( StockNo NUMBER PRIMARY KEY , Description VARCHAR2(20) , Price NUMBER(6,2)) ) ;根据sharding的机制,sharding的设计对后续系统性能影响是非常大的。一旦sharding创建完成,并已经有很多数据,相关的属性就不能再修改了,比如某个表是复制表,还是sharding表,sharding key等等,因此,SDB的设计是至关重要的,在设计sharding时需要考虑的有:
哪些表需要被设计为sharding表;
哪些表需要做复制表;
哪些shard表是根表;
使用什么方法来关联一个表到其他表或者根表;应该使用哪种sharding方法;
使用哪个作为sharding key;
数据库在现代企业管理中的应用:
特点:它们可以处理超大量的数据。
它们运行在便宜的PC服务器集群上。
PC集群扩充起来非常方便并且成本很低,避免了“sharding” *** 作的复杂性和成本。
它们击碎了性能瓶颈。
NoSQL的支持者称,通过NoSQL架构可以省去将Web或Java应用和数据转换成SQL友好格式的时间,执行速度变得更快。
“SQL并非适用于所有的程序代码,”
对于那些繁重的重复 *** 作的数据,SQL值得花钱。但是当数据库结构非常简单时,SQL可能没有太大用处。
对于第一个问题,设计一个schema->(messageID,likedCount),记录每条微博的点赞数。messageID是微博的编号,likedCount是该微博的点赞人数。但是这里有两个问题需要解决,第一是并发,第二是数据量。
每条微博都有可能有很多人同时点赞,为了保证点赞人数精确就需要保证likedCount++是原子 *** 作,这个可以由应用程序来实现,也可以用redis的事务来实现(如果redis有事务机制或者自增功能的话),但是我觉得为了性能考虑,也可以不用实现原子 *** 作,具体原因就不展开了。
每天都上亿可能更多的微博内容产生,这样就会有上亿个新的(messageID,likedCount)生成,这样的数据量是比较大的,单机数据库比较难提供高效的服务,所以需要采取sharding的功能(有时候也叫分表分库),可能根据messageID把这些schema分散到十个或者更多的shards上(据说,sina微博有600个节点,如何三个节点组成一个shard,就有200个shards),这样每个shard处理的请求就只有原来的十分之一,从而就能提高服务的性能。
关于点赞人列表的设计,一般来说,可能想到的schema是(messageID,userID),但是这样的设计有一个小问题,就是有些大发的微博可能会得到几十万的点赞,这样就会产生几十万个条数据,这样数据有点多,读取起来可能也慢。所以可以用这样一个schema(messageID,partID,userIDs),让一个messageID对于多个userID,同时比对应太多的userID,所以加入一个新的partID,一个part存1000个userID,这样几十万个点赞,只需要存几百条数据。这样做还有一个好处,用户点赞人时的,一般都不是完全显示所有点赞人,而是一批一批显示,这样可以一次只读一条数据,就可显示一批点赞用户信息。
以上就是关于如何进行mysql的动态扩容和缩容全部的内容,包括:如何进行mysql的动态扩容和缩容、给小白演示 分库分表案例、现在mysql的分布式数据访问层主流方案有哪些等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)