
同事抱着笔记本过来问我能不能让前端直接调 Oracle 里的几张报表别每次都让运维导 CSV。这需求放十年前答案基本是上中间件现在如果库是 11.2 之后的版本最省事的做法就是安装 ORDS把表和存储过程直接暴露成 REST 接口。ORDS 全称 Oracle REST Data Services是一个用 Java 写的中间层跑起来之后它自己就能当 Web 容器把 HTTP 请求翻译成 SQL 打到库里返回 JSON。整个链路不需要你写一行 Java 代码也不需要在数据库服务器上开额外的监听端口给外部。这篇就把我从解压安装包到用 curl 调通第一个接口的全过程摊开讲中间那些把 GitHub issue 翻烂才搞明白的坑也一并写出来。适合手上有一台能连数据库的机器、想快速给业务方开个数据接口的人看也适合只是想搞清楚 ORDS 到底是个什么东西的 DBA。1. ORDS 到底解决了什么问题1.1 直连数据库的三种老路子代价都不小在没有 ORDS 之前想让外部系统读到 Oracle 的数据无非三条路。第一条是让应用侧装 Oracle 客户端配 tnsnames.ora用 JDBC 或者 OCI 直连。这条路的问题在于每加一个消费方就要在对方机器上装一遍客户端、配一遍连接串、把数据库账号密码散出去账号一多权限审计基本就废了。第二条是自己写个中间层Java 也好 Python 也好把查询包一层 HTTP 接口等于凭空多出一个需要长期维护的服务。第三条最原始定期导出 CSV 或者 Excel 丢到共享目录实时性完全没有。这三条路我都踩过最难受的其实不是技术难度而是连接信息散落这件事。数据库密码一旦被复制到十几台机器上想改密码就得挨个通知漏掉一个就是一次生产事故。1.2 ORDS 把映射这件事做成了配置ORDS 的核心思路很朴素它就是一张 URL 到 SQL 的映射表只不过这张表存在数据库里。你在库里执行一段 PL/SQL声明表 EMPLOYEES 允许通过 AUTO REST 暴露ORDS 就会在内存里记住这个映射。之后收到GET /ords/hr/employees/它自动生成分页查询、自动把结果集序列化成 JSON再把响应吐回去。这就带来两个很实在的好处。一是权限收口所有外部访问都走 ORDS 这一个入口你只需要在 ORDS 层面管理用户和角色数据库密码不用外发。二是零代码常见的增删改查、分页、按字段过滤ORDS 全都内置了不用写 Controller不用配 ORM。1.3 有些场景我劝你别硬上 ORDSORDS 不是万能的。第一大批量导出别走它ORDS 的响应是在内存里拼好的几万行的结果集直接能把 JVM 堆打爆这种活还是老老实实走数据泵或者导出工具。第二跨多张表的复杂事务别指望它AUTO REST 只针对单表稍微复杂点的逻辑就得自己写 PL/SQL 然后映射成接口那不如直接走应用层。第三内网已经完全跑通的 JDBC 直连如果没有外部消费方、没有权限收口的需求为了用 ORDS 而重写一遍接入代码纯属给自己找活干。判断标准很简单消费方多、协议杂、需要审计、需要限流这四条占了两条以上ORDS 的收益就很明显了。2. 动手之前先把版本和目录这本账算清楚2.1 JDK 版本和 ORDS 版本是强绑定的这是最容易被忽略、也最容易卡住的一步。ORDS 是 Java 应用它要求的 JDK 版本跟着版本号走而且卡得挺死。经验上的对应关系大概是ORDS 21.x 及更早的版本JDK 8 就能跑22.x 开始最低要 JDK 11到了 23.x、24.x官方基本是奔着 JDK 17 去的。具体以你下载那个版本的官方 Release Notes 为准别凭印象。反过来也要注意不是 JDK 越新越好。有人图省事直接装了个 JDK 21 去跑 ORDS 21.4启动阶段就抛UnsupportedClassVersionError报错信息里那两个版本号编译版本和运行版本很多人第一次看根本反应不过来是 JDK 的事。我的建议是先把 JDK 装好、java -version确认输出再去看 ORDS 版本要求两边对齐了再往下走。还有一个隐藏点JAVA_HOME 和 PATH 里那个 java 可能不是同一个。有些系统上java -version显示的是 8但 ORDS 启动脚本读的是 JAVA_HOME 指向的 17或者反过来。安装之前一定用echo $JAVA_HOME和which java两个命令交叉确认一遍。2.2 数据库侧需要提前确认的四件事安装 ORDS 不是纯客户端行为它要往库里建元数据所以数据库这边得先过一遍。版本上ORDS 支持 11.2.0.4 以后的版本11g 是能用的但如果你手上是 11.2.0.1 这种老得不能再老的版本就别折腾了。表空间上要提前想好ORDS_METADATA和ORDS_PUBLIC_USER这两个模式用哪个表空间一般指到 SYSAUX 或者 USERS 就行别用 SYSTEM。字符集上强烈建议 AL32UTF8如果库是老字符集后面返回中文 JSON 大概率要面对乱码这个坑在第 6 节细说。最关键的一点是容器。12c 之后多租户架构普及如果你的库是 CDB 多个 PDBORDS 的元数据是装在具体的 PDB 里的。你连 CDB$ROOT 装了一遍业务数据在 PDB 里结果是接口怎么都找不到那张表日志也不报错就是 404。这个坑能让新手查一下午。2.3 目录规划和运行账户ORDS 需要一个配置目录这个目录里会存defaults.xml、连接池配置、日志以及后续可能放 APEX 静态资源。两个硬性要求一是这个目录安装时必须为空里面有文件的话安装程序会嫌脏直接退出二是这个目录要可写。目录位置我一般放在/u01/ords/config这种不带空格、不带中文的路径下。Windows 上尤其要注意放在C:\Program Files这种带空格的路径下某些版本的启动脚本传参时会出问题。运行账户这块Linux 上别用 root 跑。用一个专门的运维账号比如oracle或者新建一个ords用户给它配置目录的读写权限就够了。用 root 跑出来的日志文件属主是 root后面换个账号维护又要 chown纯属添乱。3. 安装 ORDS每一步都在问你什么3.1 解压与环境准备拿到安装包之后解压到一个独立目录比如/u01/ords解压出来你会看到bin、lib、examples这些目录bin下面就是ords这个可执行脚本。Linux 上给一下执行权限chmod x bin/ords。然后确认java -version和echo $JAVA_HOME都没问题。这一步还有个容易被忘的动作把bin目录加到 PATH 里或者每次都用全路径调用。我习惯后者因为服务器上经常同时存在好几套 ORDS全路径更不容易搞混版本。3.2 交互式安装的每一个提问带上配置目录参数执行安装/u01/ords/bin/ords --config /u01/ords/config install如果配置目录里已经有东西可以加--force覆盖但生产环境慎用。接下来它会连着问你一串问题我按顺序拆一下每一问到底在问什么。第一组是数据库连接方式。一般选1也就是 Basic然后依次填主机名、端口、Service Name。这里 Service Name 和 SID 是两个不同的东西12c 以后基本都是 Service Name如果填错会卡在连接测试上反复重试。如果你已经配好了 tnsnames.ora也可以选 TNS 方式直接写别名省得记 IP。接下来它会要一个能建用户的账号通常就是SYS AS SYSDBA。注意这里要的是 SYSDBA 权限不是普通 DBA 角色因为安装过程要创建ORDS_METADATA和ORDS_PUBLIC_USER两个模式普通账号权限不够。然后是表空间和密码的确认包括ORDS_METADATA用哪个表空间、ORDS_PUBLIC_USER用哪个、临时表空间给谁。默认值一般能用但生产上我会按库里已有的命名习惯改一下。接着会让你给ORDS_PUBLIC_USER设密码以及给 ORDS 自己的管理员账号设密码这两个密码记牢后面改配置还用得上。再往后是网关模式的选择问你是用 PL/SQL Gateway 还是 ORDS 自己的 REST 引擎选 ORDS 那项就行PL/SQL Gateway 是给非常老的遗留系统做兼容的。最后是HTTP 端口默认 8080还有 APEX 静态资源路径不装 APEX 直接回车跳过。装完它会打印出一行提示告诉你独立模式下的访问地址形如http://主机名:8080/ords/这行地址先复制存下来。3.3 独立模式和部署 WAR 包到底怎么选ORDS 有两种跑法。第一种是独立模式standalone安装完之后直接ords --config /u01/ords/config serve就起来了它内置了一个 Jetty 容器不用另外装 Tomcat。适合快速验证、内部小规模使用也适合做 PoC。第二种是部署 WAR 包到已有的 Tomcat、WebLogic 或者其它 Servlet 容器。做法是先用ords --config /u01/ords/config war生成ords.war再丢到容器的 webapps 目录里。怎么选我的经验是如果公司已经有一套统一的中间件运维体系那就老老实实出 WAR 包交给他们管日志、启停脚本、监控都走既有流程省得你一个人维护一个游离在体系外的进程。如果只是自己或者小团队内部用standalone 起步更快出问题也好排查因为它不依赖外部容器变量少。生产环境我见过不少直接跑 standalone 的只要把启停做成 systemd 服务其实也很稳。还有个进阶玩法是静默安装。所有交互问题都能用-p参数一次性给进去写成脚本之后可以反复执行做环境重建的时候特别香。这一块建议对着官方文档的参数列表来写因为不同版本的参数名偶尔会变。4. 让库里的对象变成能被访问的接口4.1 AUTO REST一条 PL/SQL 暴露一张表ORDS 装好之后默认是什么都不能访问的得逐个开。开的方式是在数据库里执行 PL/SQL 包调用。先把整个模式打开BEGIN ORDS.ENABLE_SCHEMA( p_enabled TRUE, p_schema HR, p_url_mapping_type BASE_PATH, p_url_mapping_pattern hr, p_auto_rest_auth FALSE ); COMMIT; END; /这里几个参数值得掰开说。p_url_mapping_pattern是 URL 里的路径片段填hr之后接口地址就是/ords/hr/这个别名可以跟真实模式名不一样等于顺手做了层脱敏别把生产库的模式名直接暴露在 URL 里。p_auto_rest_auth控制自动生成的接口是否需要认证PoC 阶段图省事填 FALSE上线前一定要改成 TRUE否则等于把整张表裸奔在网络上。模式打开之后再逐张表开BEGIN ORDS.ENABLE_OBJECT( p_enabled TRUE, p_schema HR, p_object EMPLOYEES, p_object_type TABLE, p_object_alias employees, p_auto_rest_auth FALSE ); COMMIT; END; /p_object_type除了 TABLE 还能填 VIEW所以视图一样能开。开完之后ORDS 会自动提供一整套接口GET 列表、GET 单条、POST 新增、PUT 更新、DELETE 删除全都不用你写。4.2 官方自动生成的接口有哪些查询能力自动生成的 GET 列表接口配合 URL 参数能干不少事。比如?limit10offset20做分页?q{dept_id:50}按字段过滤?fieldsfirst_name,last_name只取部分列。这些在官方文档里叫 filter 和 fields 语法实际用起来跟很多 NoSQL 的查询风格很像。表格对照一下常用的几个参数URL 参数作用示例limit每页返回行数?limit25offset跳过多少行?offset50q结构化过滤条件?q{dept_id:50}fields只返回指定列?fieldsid,nameorderby排序?orderbyname:desc这套东西的好处是前端不用等后端排期表开出来就能自己拼查询。坏处也很明显过滤条件太灵活容易被拖垮。所以第 7 节会讲怎么在连接池层面兜底。4.3 手工定义 RESTful 模块AUTO REST 只能覆盖单表稍微有点业务逻辑的场景就得自己写。比如想返回一个统计结果或者把两张表 join 之后返回这时候用ORDS.DEFINE_MODULE系列。三段式先定义模块再定义模板也就是路径最后定义处理器路径加 HTTP 方法。BEGIN ORDS.DEFINE_MODULE( p_module_name demo.module, p_base_path demo/, p_items_per_page 25, p_status PUBLISHED ); ORDS.DEFINE_TEMPLATE( p_module_name demo.module, p_pattern summary ); ORDS.DEFINE_HANDLER( p_module_name demo.module, p_pattern summary, p_method GET, p_source_type json/query, p_source SELECT COUNT(*) AS total FROM hr.employees ); COMMIT; END; /p_source_type决定了 SQL 怎么被处理json/query最常用直接把结果集转成 JSON 数组返回。如果要做写入类操作用plsql/block或者json/collection之类的类型具体选哪个看你的入参形式。我的经验是能用一个 SELECT 解决的就别写 PL/SQL因为映射一旦复杂起来调试难度会急剧上升。有个细节很多人不知道DEFINE_MODULE里的p_status一定要是PUBLISHED如果顺手写成别的值接口会一直返回 404而且日志里几乎看不出来是状态的问题。4.4 认证和授权怎么做才算合格ORDS 支持好几种认证方式最常用的是 OAuth2 客户端凭证模式和基本认证First Party。做内部系统对接我一般用OAuth2 客户端凭证模式因为它的 token 有有效期比长期挂着用户名密码要安全。流程是先在库里建用户和角色再给角色授权最后把 URL 路径绑定到权限上BEGIN ORDS.CREATE_ROLE(p_role_name emp_readonly); ORDS.CREATE_USER( p_username restuser, p_password 换成强密码 ); ORDS.GRANT_ROLE( p_role_name emp_readonly, p_username restuser ); ORDS.DEFINE_PRIVILEGE( p_privilege_name emp.priv, p_roles emp_readonly, p_pattern hr/employees/*, p_methods GET ); COMMIT; END; /注意p_methods只给了 GET意味着这个账号只能读不能写。按最小权限拆分角色是很值得做的一件事读的角色和写的角色分开前端用读角色后台任务用写角色真出问题的时候影响面能小一大截。5. 启动、验证把第一个请求跑通5.1 起服务与看日志独立模式下启动就一条命令/u01/ords/bin/ords --config /u01/ords/config serve看到日志里打印出端口和上下文路径就说明起来了。默认上下文路径是/ords。如果 8080 被占了可以在配置文件里改端口新版本可以直接用配置命令改比如把standalone.port设成别的值或者干脆在启动时加参数覆盖。日志位置跟版本有关独立模式一般打在标准输出上做成 systemd 服务之后直接进 journal。排查问题的时候优先看启动阶段的日志因为它会把连不上库、JDK 版本、端口占用这些致命问题一次性说清楚。5.2 用 curl 把接口验一遍服务起来之后先验证连接curl -i http://127.0.0.1:8080/ords/hr/employees/如果返回 200 加上一段 JSON 数组说明链路通了。如果返回 401说明权限配置生效了只是你没带认证信息。如果返回 404八成是模式或者对象没开或者 URL 里的别名跟p_url_mapping_pattern对不上。带上认证再试一次curl -i -u restuser:密码 http://127.0.0.1:8080/ords/hr/employees/?limit2顺手把分页参数也验一下确认limit生效。本地 127.0.0.1 通了之后一定要换一台机器用真实 IP 再试一次因为这一步能区分出服务本身有问题和网络或者防火墙有问题省得后面瞎猜。5.3 从浏览器和 Postman 交叉验证curl 通了不代表浏览器就通。浏览器会先发 OPTIONS 预检请求跨域场景如果你的前端是另一个域名在调需要处理 CORS。ORDS 支持配置跨域响应头但最省心的做法还是让前端和 ORDS 走同一个域名的反向代理同源之后 CORS 问题直接消失。Postman 上建议建一个集合把常用的几个接口都存下来用环境变量管理主机名和 token。这个集合留给后面接手的同事比写一份文字文档管用得多。6. 那些让我卡了半天的坑6.1 Java 版本报错UnsupportedClassVersionError现象是启动瞬间抛异常堆栈里全是类加载失败的痕迹。根因我在第 2 节提过就是 JDK 版本不匹配。但有个变种值得单说机器上装了多个 JDKPATH 里的和 JAVA_HOME 指向的不一致java -version看着没问题ORDS 启动脚本读到的却是另一个。解决办法很土但有效在启动脚本里显式设置 JAVA_HOME或者干脆改系统级的 JAVA_HOME别依赖 PATH。6.2 8080 端口被占不一定是端口的问题Address already in use这个报错很直白但踩过一次之后我养成了习惯先netstat -tlnp | grep 8080看清楚是谁占的而不是直接换端口。我有一次是因为测试环境的 Tomcat 没关干净换了端口之后过两天运维重启机器Tomcat 又起来了两个服务抢端口抢了好几天才发现。还有一种情况是端口没被占但从外部连不上。这时候排查顺序是本地 curl 通不通 → 防火墙规则 → 云主机的安全组 → SELinux。四层里任何一层没放行表现都是一样的连不上不按顺序查就是浪费时间。6.3 Service Name 和 SID 填反了这个坑的表现特别有迷惑性安装过程能过启动也正常但访问接口要么报连接错误要么干脆 404。根因是你安装时填的连接信息指向了一个逻辑上对但内容不对的地方。12c 以后连 PDB 必须用 PDB 的 Service Name填成 SID 或者 CDB 的 Service Name元数据就建到了别的地方。判断方法很简单在数据库侧执行SELECT name FROM v$services;看清楚 PDB 对应的 Service Name 到底是哪个然后拿这个值去比对 ORDS 配置里的连接串。6.4 重复安装ORA-01920 和 ORA-00955想重装一遍的时候安装程序会报用户已存在之类的错误。根因是ORDS_METADATA和ORDS_PUBLIC_USER这两个模式还在库里而 ORDS 的安装脚本默认不覆盖已有的。正确的做法是先用卸载命令清干净一般是通过 ords 命令带上卸载参数执行或者手工在库里把这两个模式连同数据一起删掉DROP USER ... CASCADE再重新装。我个人的习惯是做任何重装之前先在库里查一遍这两个用户是否存在确认了再动手别上来就执行安装。6.5 中文返回乱码这个坑的排查链路比较长。表现是接口返回的 JSON 里中文字段变成一串问号或者\uXXXX。分三层看数据库字符集是不是 AL32UTF8ORDS 进程所在的系统 locale 是不是 UTF-8以及客户端有没有正确声明Accept-Charset。实际经验是大部分情况下问题在数据库字符集。老库用 ZHS16GBK 的很多这种库上装 ORDS中文处理会一直别扭。如果条件允许从一开始就把库建成 AL32UTF8能省掉后面无数麻烦。6.6 长查询超时连接池的默认值不够用有些报表查询要跑十几秒甚至更久默认配置下接口会提前断开。这时候要调的是连接池相关的参数比如最大连接数、连接空闲回收时间、请求超时时间。新版本 ORDS 支持用配置命令改老版本的配置项写在defaults.xml里。但我要提醒一句先调 SQL再调参数。我见过有人把一个全表扫描的查询放到 ORDS 接口上然后拼命加jdbc.MaxLimit来解决问题最后 JVM 内存吃满 OOM。加连接数只是让它挂得晚一点不是解决之道。7. 上线前值得做的几项调整7.1 连接池和 JVM 参数连接池的核心参数就两个方向上限和回收。上限决定了 ORDS 能同时用多少数据库会话多了会挤占数据库资源少了并发上来会排队。一个粗略的起点是单机 ORDS 的最大连接数不要超过数据库允许的总会话数除以 ORDS 实例数再留点余量给其它应用。回收时间这块设得太短会导致连接频繁重建设得太长会让空闲连接一直挂着占会话中间值靠观察调整。JVM 这块主要是堆大小。默认值在测试环境够用生产上如果接口返回的数据集偏大需要适当调大。调完之后一定要用真实的接口压一轮光看启动成功不算数。7.2 把暴露面收窄上线前必做的两件事。第一关掉所有p_auto_rest_auth FALSE的自动接口把它们改成需要认证。第二确认没有整库级别的模式被打开只开真正需要对外的那几个。网络层面ORDS 的端口不应该对公网开放走内网或者反向代理。反向代理还能顺手做几件事统一域名、上 HTTPS、加限速、记录访问日志。限速这点尤其重要因为自动生成的查询接口太灵活不加限制的话一个前端 bug 就能把库压垮。7.3 备份和升级路径ORDS 的配置目录要纳入备份范围里面的defaults.xml和连接池配置丢了重建要花不少时间。数据库侧的ORDS_METADATA模式建议跟着库的备份策略走。升级这块ORDS 的版本升级相对平滑一般流程是先用新版本的安装包指向已有的配置目录执行安装让它自动完成元数据升级。升级前先在测试环境跑一遍重点验证 AUTO REST 的接口行为有没有变化因为不同版本对 filter 语法的处理偶尔会有细微差异。我在实际操作中的体会是ORDS 这类工具最耗时间的从来不是装而是想清楚要暴露什么。装一遍也就二十分钟但哪些表该开、以什么别名开、给谁开、开完之后怎么监控这些才是真正要花心思的地方。我现在的习惯是每开一张表就在文档里记一笔写上别名、负责人、用途半年之后回头看这份清单能省掉大量这张表谁开的能不能删的扯皮。