Oceanus - 58同城数据库中间件

Related tags

Database Oceanus
Overview

Oceanus

Oceanus - 58同城数据库中间件

关于DB中间件

在DB存储需求中,尽管业务不同,技术难点还是类似的,开源世界有很多DB中间件,解决方案也以通用方案为主,满足业务需要为前提,支持各种类型的需求。

Oceanus致力于打造一个功能简单、可依赖、易于上手、易于扩展、易于集成的解决方案,甚至是平台化系统。拥抱开源,提供各类插件机制集成其他开源项目,新手可以在几分钟内上手编程,分库分表逻辑不再与业务紧密耦合,扩容有标准模式,减少意外错误的发生。

Oceanus内部名词定义

  • datanode:数据源节点。为一个数据源命名,配置链接属性、报警实现
  • namenode:数据源的簇。为一组数据源命名,指定这组数据源的负载方式、访问模式、权重
  • table:映射表。匹配解析sql中的table名称,命中table标签的name属性值后,会执行约定的路由逻辑
  • bean:实体。由其他标签引用,实体类必须有无参的构造函数
  • tracker:监控埋点。涉及到计算和IO的功能点都有监控点,自定义一个埋点实现类,当功能耗时超出预期时会执行其中的回调函数,便于监控和优化系统

为什么说Oceanus是非常易用的

Oceanus在设计时非常注重使用者的评价,配置结构近乎于和使用者交流约定业务规则,便于不同的人看同一套配置,互相理解流程。当配置文件编写完成后,编码就变得更加简单,只调用Oceanus客户端的几个方法就可以实现数据库操作,不再关心HA、报警、负载均衡、性能监控等问题。良好的用户视觉减少了分库分表在业务场景中的耦合度,对于编码者就像只对一个table操作,Oceanus负责进行sql解析、路由、sql重写。

如提交:	select * from user;

改写成:	select * from user0;
		   select * from user1;
		   select * from user2;
		   select * from user3;

分发给不同的库(或者同库)执行,用户视觉如图: github

开源集成

“接地气,拥抱开源” 是Oceanus的设计原则之一,可以很好的集成到mybatis和hibernate中,只要引用其中的插件,编写Oceanus配置文件,然后改写各自的DataSource实现或ConnectionProvider即可做到集成。这样既用到了熟悉的ORM,又借助Oceanus实现了分库分表等功能。

待开发

不得不说Oceanus在设计上非常灵活,使得每一个细小的功能点都有极高的切入价值,比如Cache机制、全局的ID生成规则、资源可视化监控等等,把其中某一个点做得足够好,都会为整体产品带来质的提升,简化实际生产环境中的配套系统研发维护成本。

wiki

https://github.com/58code/Oceanus/wiki

58同城开源技术交流群:427359544

Comments
  • ScriptFunction构造的ShardException

    ScriptFunction构造的ShardException

    类: com.bj58.oceanus.core.shard.ScriptFunction

        @Override
        public int execute(int size, Map<String, Object> parameters) {
    
            try {
                // ...
                context.put("$NODE_SIZE", size);
                Double value = scriptExecutor.execute(null,context);
                if (value != null) {
                    return value.intValue();
                } else {
                    throw new ShardException("function error,could not find namenodes index="+value+"!parameters=" + parameters); //
                }
            } catch (ScriptException e) {
                throw new ShardException("script error!parameters=" + parameters, e);
            }
    
        }
    

    应该是写错了,上下文没有namenodes的信息,而且这个else分支value是null

    opened by hailinzeng 2
  • dataNodeAlive特殊情况处理

    dataNodeAlive特殊情况处理

    类: com.bj58.oceanus.core.loadbalance.ha.DataNodeChecker

        /**
         * 从状态映射map中查看DataNode是否可用
         * @param dataNode
         * @return
         */
        public static boolean dataNodeAlive(DataNode dataNode){
            if(dataNode==null || dataNode.getId()==null)
                return true;
    
            if(!dataNodeAliveMap.containsKey(dataNode.getId())){
                logger.error("DataNode ["+dataNode.getId()+"] is not in the check list !!");
                return true;
            }
    
            return dataNodeAliveMap.get(dataNode.getId()).get();
        }
    

    前面的两个if特殊情况应该不会发生, 而且为什么返回的是true?

    opened by hailinzeng 2
  • 查询语句对HAVING的支持不够

    查询语句对HAVING的支持不够

    环境: 一个MySQL实例,两个schema[ ocuanus、ocuanus2 ],四张表 ocuanus t_user

    ocuanus2 t_user t_user1 t_user2 表结构与oceanus-test/t_user.sql里面一致

    问题1描述: 执行SQL,会出现异常 SELECT AVG(age) FROM t_user u GROUP BY age HAVING age > 20

    异常信息: java.sql.SQLException: Column 'age' not found. at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:996) at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:935) at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:924) at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:870) at com.mysql.jdbc.ResultSetImpl.findColumn(ResultSetImpl.java:1064) at org.apache.tomcat.dbcp.dbcp.DelegatingResultSet.findColumn(DelegatingResultSet.java:331) at org.apache.tomcat.dbcp.dbcp.DelegatingResultSet.findColumn(DelegatingResultSet.java:331) at com.bj58.oceanus.result.merger.DefaultResultSetMerger$ResultSetColumnFinder.findIndex(DefaultResultSetMerger.java:407) at com.bj58.oceanus.result.merger.DefaultResultSetMerger.merge(DefaultResultSetMerger.java:121) at com.bj58.oceanus.exchange.executors.jdbc.SimpleExecutor.doQuery(SimpleExecutor.java:184) at com.bj58.oceanus.exchange.executors.jdbc.SimpleExecutor.doQuery(SimpleExecutor.java:148) at com.bj58.oceanus.exchange.executors.jdbc.SimpleExecutor.execute(SimpleExecutor.java:66) at com.bj58.oceanus.exchange.jdbc.PreparedStatementWrapper.executeQuery(PreparedStatementWrapper.java:102) at com.bj58.oceanus.demo.UserRwDemo.selectTest(UserRwDemo.java:89) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) .....

    问题2描述: 执行SQL,会出现异常 SELECT AVG(age), MIN(age), MAX(age), age FROM t_user u GROUP BY u.age HAVING u.age > 20 ORDER BY u.age DESC LIMIT 0, 15

    异常信息: 18:10:47.328 [main] ERROR com.bj58.oceanus.result.merger.DefaultResultSetMerger.processHaving(DefaultResultSetMerger.java:254) - script execute error! javax.script.ScriptException: sun.org.mozilla.javascript.internal.EcmaError: ReferenceError: "U" is not defined. (#1) in at line number 1 at com.sun.script.javascript.RhinoScriptEngine.eval(RhinoScriptEngine.java:156) ~[na:1.6.0_45] at com.sun.script.javascript.RhinoScriptEngine.eval(RhinoScriptEngine.java:170) ~[na:1.6.0_45] at javax.script.AbstractScriptEngine.eval(AbstractScriptEngine.java:216) ~[na:1.6.0_45] at com.bj58.oceanus.core.script.InterpretedScriptExecutor.execute(InterpretedScriptExecutor.java:47) ~[oceanus-core-0.0.2-SNAPSHOT.jar:na] at com.bj58.oceanus.result.merger.DefaultResultSetMerger.processHaving(DefaultResultSetMerger.java:250) [oceanus-results-merger-0.0.2-SNAPSHOT.jar:na] at com.bj58.oceanus.result.merger.DefaultResultSetMerger.merge(DefaultResultSetMerger.java:192) [oceanus-results-merger-0.0.2-SNAPSHOT.jar:na] at com.bj58.oceanus.exchange.executors.jdbc.SimpleExecutor.doQuery(SimpleExecutor.java:184) [oceanus-exchange-executors-0.0.2-SNAPSHOT.jar:na] at com.bj58.oceanus.exchange.executors.jdbc.SimpleExecutor.doQuery(SimpleExecutor.java:148) [oceanus-exchange-executors-0.0.2-SNAPSHOT.jar:na] at com.bj58.oceanus.exchange.executors.jdbc.SimpleExecutor.execute(SimpleExecutor.java:66) [oceanus-exchange-executors-0.0.2-SNAPSHOT.jar:na] at com.bj58.oceanus.exchange.jdbc.PreparedStatementWrapper.executeQuery(PreparedStatementWrapper.java:102) [oceanus-exchange-jdbc-0.0.2-SNAPSHOT.jar:na] at com.bj58.oceanus.demo.UserRwDemo.selectTest(UserRwDemo.java:89) [classes/:na] at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.6.0_45] at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) ~[na:1.6.0_45] at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) ~[na:1.6.0_45] at java.lang.reflect.Method.invoke(Method.java:597) ~[na:1.6.0_45] .....

    opened by fengwuze 1
  • loadConfigFile特殊情况处理

    loadConfigFile特殊情况处理

    类: com.bj58.oceanus.config.parser.ConfigurationLoader

        public void load(String path) throws Exception {
            // ... 
    
            loadConfigFile(new File(path));
    
            // ...
        }
    
        void loadConfigFile(File configFile) throws Exception {
            // ...
            if(configFile==null || !configFile.exists())
                throw new Exception("oceanus config file:"+configFile.getName()+" not found !");
    
            // ......
    
            List<Node> includeNodes = ParseUtils.getNodeList(rootNodeList, INCLUDE_TAG);
            for(Node includeNode : includeNodes) {
                IncludeConfig includeConfig = (IncludeConfig) ParseUtils.getParser(includeNode).parse((Element)includeNode);
    
                loadConfigFile(new File(configFile.getParent() + SEPERATOR + includeConfig.getFile()));
            }
        }
    

    如果传入的configFile可能为null, 那configFile.getName就有问题了.

    opened by hailinzeng 1
  • 无用日志

    无用日志

    类: com.bj58.oceanus.core.loadbalance.ha.DataNodeChecker.FirstChecker

        /**
         * 该线程用于轮询检测全部DataSource状态,初步判断数据源是否可用,如果不可用,交给ConfirmChecker(确认线程)延时处理
         */
        private static class FirstChecker implements Runnable{
    
            @Override
            public void run() {
                logger.debug("dataNodeMap:"+DataNodeChecker.dataNodeMap);
    
                // .......
            }
        }
    

    输出的是DataNodeChecker.dataNodeMap这个HashMap的地址

    opened by hailinzeng 1
Owner
Wuba
Wuba Open Source
Wuba