最近花了点时间研究了下OceanBase,非常有意思,写点东西记录一下学到的东西。
参考文档:https://github.com/alibaba/oceanbase/wiki/OceanBase%E6%9E%B6%E6%9E%84%E4%BB%8B%E7%BB%8D
OceanBase的产生背景OceanBase最初是为了解决淘宝网的大规模数据而产生的(数百亿条的记录、数十TB的数据、数万TPS、数十万QPS),传统的如Oracle单机数据库肯定无法支撑(加再多硬件也不行,RAC的可扩展性太差),于是乎,有段时间,淘宝就开始用MySQL取代Oracle,然后就是疯狂分库(通常的做法是根据某个业务字段,通常取用户编号,哈希后取模,根据取模的结果将数据分布到不同的数据库服务器上,客户端请求通过数据库中间层路由到不同的数据库服务器上),这种方式有如下弊端:
1)添加节点比较复杂,往往需要人工介入;
2)有些范围查询需要访问几乎所有的分区数据库,性能变的更差;
接着Hadoop如火如荼,于是这些技术人员又在考虑是否可以使用HBase,但HBase有个致命缺陷:只能支持单行事务(HBase的体系架构可参考我的另一篇博文:http://blog.csdn.net/u010415792/article/details/8902746,因为写在HBase里是针对某个HRegion,而HRegion是分布在各个结点中,所以至多只能保证单行事务的原子性),而淘宝的业务必须支持跨行跨表事务。
因此,需要开发出一个新的数据库,即有良好的可扩展性,又能支持跨行跨表事务,于是OceanBase应运而生!
OceanBase的架构设计
通过分析发现,虽然淘宝在线业务的数据量十分庞大,但最近一段时间(例如一天)的修改量往往不多,因此,OceanBase决定采用单台更新服务器来记录最近一段时间的修改增量,而以前的数据保持不变,称为基准数据。基准数据以类似分布式文件系统的方式存储于多台基准数据服务器中,每次查询都需要把基准数据和增量数据融合后返回给客户端。这样,写事务都集中在单台更新服务器上,避免了复杂的分布式事务,高效地实现了跨行跨表事务;另外,更新服务器上的修改增量能够定期分发到多台基准数据服务器中,避免成为瓶颈,实现了良好的扩展性。
- RootServer:(存放元数据)管理集群中的服务器,tablat数据分布及副本管理。
- UpdateServer:存储增量更新数据,往往和RootServer公用一台物理服务器。
- ChunkServer:存储基准数据,基准数据有多个副本(类似Hadoop)
- MergeServer:接受客户端请求,合并UpdataServer和ChunkServer的数据返回给客户端,并定期把UpdateServer上的数据合并到ChunkServer上。
从上面可以看出,OceanBase融合了分布式存储系统和关系数据库这两种技术。通过分布式存储技术将基准数据分布到多台ChunkServer,实现数据复制、负载均衡、服务器故障检测与自动容错,等等;UpdateServer相当于一个高性能的内存数据库,底层采用关系数据库技术实现。OceanBase相当于GFS + MemSQL,ChunkServer的实现类似GFS,UpdateServer的实现类似MemSQL,目标是成为可扩展的、支持每秒百万级跨行跨表事务操作的分布式数据库。
OceanBase最大的亮点在于把写集中到一个单点UpdateServer,这样的好处是可以让一致性和可用性兼得,实现跨行跨表事务,坏处是UpdateServer单点性能有可能成为瓶颈,因此它的配置要非常非高(大内存+SSD+存储Cache)。
动态或者静态地为一个对象附加一个职责或者功能,称为装饰模式。它是属于结构设计模式之一,比较常用。
使用场合和优势:
我们将要定义一个Shape接口和图的实现类。之后,我们创建抽象装饰类ShapeDecorator,它实现了Shape接口。RedShapeDecorator 是 ShapeDecorator的扩展类,DecoratorPatternDemo则是对这个模式的测试类. UML类图如下所示。
定义Shape接口.
public interface Shape { void draw(); }Shape的子类Circle、Rectangle.
public class Rectangle implements Shape { @Override public void draw() { System.out.println("Shape: Rectangle"); } }
public class Circle implements Shape { @Override public void draw() { System.out.println("Shape: Circle"); } }
抽象装饰类ShapeDecorator。
public abstract class ShapeDecorator implements Shape { protected Shape decoratedShape; public ShapeDecorator(Shape decoratedShape){ this.decoratedShape = decoratedShape; } public void draw(){ decoratedShape.draw(); } }
ShapeDecorator的扩展类。
public class RedShapeDecorator extends ShapeDecorator { public RedShapeDecorator(Shape decoratedShape) { super(decoratedShape); } @Override public void draw() { decoratedShape.draw(); setRedBorder(decoratedShape); } private void setRedBorder(Shape decoratedShape){ System.out.println("Border Color: Red"); } }
测试类:
public class DecoratorPatternDemo { public static void main(String[] args) { Shape circle = new Circle(); Shape redCircle = new RedShapeDecorator(new Circle()); Shape redRectangle = new RedShapeDecorator(new Rectangle()); System.out.println("Circle with normal border"); circle.draw(); System.out.println("\nCircle of red border"); redCircle.draw(); System.out.println("\nRectangle of red border"); redRectangle.draw(); } }
你会发现装饰模式与适配器模式有点像,但是适配器模式强调的是协调两个对象,而装饰模式则是添加新的功能给某个对象。
上一篇( Sybase 海量数据分页瞬间完成)是不支持排序的,这次又写了一个支持排序的,不过要在数据库排序列加上索引,并在程序中指定索引名字。(注:sybase的jdbc API不是很智能不指定就没用使用索引搞的速度很慢)
带排序分页代码下载
测试代码如下:
/** * @param args * @throws InterruptedException */ public static void main(String[] args) throws InterruptedException { StringBuilder sql = new StringBuilder("select t.keyId,t.pvcode" + ",t.spType,t.factory,t.spareType,t.spareNum," + "t.spareFuncNum,t.spareState,t.spareSerialNum,t.spareHardVersion,t.spareSoftVersion," + "t.spareEnName,t.spareZhName,t.spareAddress " + " from province_Spare_Info_ALL t where 1=1 order by factory");// PagingService service = new PagingService(); int pageSize = 15; int startNum = pageSize * 0;//第一页 //首次加载效率 long b = System.currentTimeMillis(); getPage(service ,sql.toString(),startNum,pageSize); long e = System.currentTimeMillis(); System.out.println("首次加载时间"+(e-b)); Thread.currentThread().sleep(10000);//缓存5.5秒后可查询 我得电脑是4.7秒 //缓存之后效率 startNum = pageSize * 8000;//第8000页 b = System.currentTimeMillis(); getPage(service ,sql.toString(),startNum,pageSize); e = System.currentTimeMillis(); System.out.println("翻到第8000时间"+(e-b));
startNum = pageSize * 40000;//第40000页 b = System.currentTimeMillis(); getPage(service ,sql.toString(),startNum,pageSize); e = System.currentTimeMillis(); System.out.println("翻到第40000时间"+(e-b)); } public static void getPage(PagingService service,String sql, int startNum, int pageSize ){ /* * 我这里主键的索引是 province_pk_1 * factory 字段加的索引名叫province_index_1 * 如果就一个主键那写null就可以。如果几个索引那用哪个索引写哪个索引的名字 */ Page page = service.findPageBySql(sql, ProvinceSpareInfoAll.class, "keyId", startNum, pageSize,"province_index_1");//province_pk_1 List list = page.getData(); System.out.println(page.getTotalCount()); for (Object object : list) { ProvinceSpareInfoAll p = (ProvinceSpareInfoAll)object; System.out.print(p.getKeyId()+" "); } System.out.println(); }