本文将介绍PostgreSQL外部表的核心概念并基于SpringCloud实现外部表数据读取。
- 如文章中有明显错误或者用词不当的地方,欢迎大家在评论区批评指正,我看到后会及时修改。
如想要和博主进行技术栈方面的讨论和交流可私信我。
1. 前言
在微服务业务中,当需要获取别的业务模块的数据时,我们通常采用远程调用的形式,但远程调用一多,在拉高系统耦合度的同时,系统运维难度也在提升。其数据调用场景如下图所示:
由于我做的系统都是基于Postgres数据库的,在机缘巧合下我发现了外部表这个好东西。相比远程调用,外部表的优势如下:
-
性能和响应时间: 通过在微服务中使用外部表,可以在本地数据库中存储和缓存数据,从而减少远程调用的需求。本地数据的访问通常比远程调用更快,因此可以提高服务的性能和响应时间。
-
降低系统耦合度: 使用外部表可以降低微服务之间的直接依赖性。远程调用可能使得一个微服务依赖于另一个微服务的具体实现,而使用外部表,微服务只需关心自己本地的数据即可,降低了系统的耦合度。
-
减少网络开销: 远程调用会引入网络开销,而本地数据访问通常避免了这种开销。通过在本地使用外部表,可以减少对网络资源的依赖,从而提高整体系统的可靠性。
-
灵活性: 外部表允许更灵活地选择何时更新本地数据。可以定期同步数据,或者在需要时通过一些事件触发来更新本地数据。这种方式相对于实时远程调用更容易控制和管理。
然而,需要注意的是,使用外部表也可能带来一些挑战,例如数据一致性和同步的问题。如果外部数据源发生变化,需要确保本地数据及时更新,以避免脏数据或不一致的情况。因此,在选择使用外部表还是远程调用时,需要根据具体的业务需求和系统特点进行权衡。
2. 概念介绍
在 PostgreSQL 中,外部表是一项强大的功能,它允许我们在数据库中访问和查询来自不同数据源的数据,而无需实际地将数据复制到 PostgreSQL 数据库中。为了理解这一机制,我们将深入探讨一些核心概念。
1. 外部数据包装器 (FDW):
外部数据包装器是 PostgreSQL 提供的扩展,用于实现对外部数据源的连接和查询。不同的外部数据包装器支持不同的数据源,例如 postgres_fdw
用于连接到其他 PostgreSQL 数据库,而 file_fdw
则用于读取文件系统中的文件。
2. 外部服务器 (Foreign Server):
外部服务器是外部数据包装器的一个实例,它定义了如何连接到实际的外部数据源。创建外部服务器时,我们需要指定连接外部数据源所需的参数,如主机地址、端口号、数据库名称等。
CREATE SERVER my_server FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host 'external_host', dbname 'external_db');
3. 用户映射 (User Mapping)
用户映射建立了连接到外部服务器的 PostgreSQL 用户和外部数据源中的用户之间的映射关系。这包含了连接所使用的用户名、密码等信息。
CREATE USER MAPPING FOR local_user SERVER my_server OPTIONS (user 'external_user', password 'external_password');
4. 外部表 (Foreign Table)
外部表是通过外部数据包装器定义的虚拟表,它提供了对外部数据源中数据的访问。外部表的结构和数据来自于外部数据源。
CREATE FOREIGN TABLE my_external_table (
column1 datatype,
column2 datatype,
...
) SERVER my_server OPTIONS (table_name 'external_table');
5. 查询重写 (Query Rewriting)
查询重写是外部数据包装器的一部分,它负责将 PostgreSQL 查询转换为外部数据源能够理解和执行的形式。这包括将条件和投影推送到外部数据源,以减少在 PostgreSQL 中检索的数据量。
6. 外部表列映射 (Column Mapping)
外部表列映射定义了外部表的列与外部数据源中实际列的映射关系。这确保了在查询时,列的顺序和数据类型能够正确匹配。
IMPORT FOREIGN SCHEMA external_schema FROM SERVER my_server INTO public;
7. 服务器选项 (Server Options)
服务器选项是在创建外部服务器时指定的配置参数,用于指定连接到外部数据源的详细信息。
CREATE SERVER my_server FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host 'external_host', dbname 'external_db');
8. 访问权限控制 (Access Control)
外部表同样受到 PostgreSQL 的访问权限控制机制的影响。在创建外部表时,需要确保用户具有足够的权限来访问外部表。
GRANT SELECT ON TABLE my_external_table TO some_role;
3. 开发环境搭建
3.1. 所用版本工具
依赖 | 版本 |
---|---|
Spring Boot | 2.6.3 |
Spring Cloud Alibaba | 2021.0.1.0 |
Spring Cloud | 2021.0.1 |
java | 1.8 |
postgres | 13.12 |
3.2. pom文件编写
3.2.1. 父模块pom文件编写
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.xfc</groupId>
<artifactId>xfc-fdw-cloud</artifactId>
<version>1.0-SNAPSHOT</version>
<modules>
<module>xfc-gateway</module>
<module>xfc-foreign-table</module>
</modules>
<packaging>pom</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.3</version>
<relativePath/>
</parent>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<spring-cloud.version>2021.0.1</spring-cloud.version>
<spring-cloud-alibaba.version>2021.0.1.0</spring-cloud-alibaba.version>
<mybatis-plus.version>3.3.1</mybatis-plus.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<!-- springCloud -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring-cloud-alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<encoding>${project.build.sourceEncoding}</encoding>
</configuration>
</plugin>
<!--springboot 打包插件 ,不然报错 Unable to find main class
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
-->
</plugins>
<resources>
<resource>
<!--编译时,默认情况下不会将 mapper.xml文件编译进去,
src/main/java 资源文件的路径,
**/*.xml 需要编译打包的文件类型是xml文件,
-->
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
</resource>
</resources>
</build>
</project>
3.2.2. 网关模块pom文件编写
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>xfc-fdw-cloud</artifactId>
<groupId>com.xfc</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>xfc-gateway</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
<build>
<finalName>geoscene-back-gateway</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<skipTests>true</skipTests>
</configuration>
</plugin>
</plugins>
</build>
</project>
3.2.3. 外部表模块pom文件编写
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>xfc-fdw-cloud</artifactId>
<groupId>com.xfc</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>xfc-foreign-table</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
<build>
<finalName>geoscene-back-gateway</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<skipTests>true</skipTests>
</configuration>
</plugin>
</plugins>
</build>
</project>
4. 源码简介
我写了一个基于Spring Cloud读取外部表的demo,其链接如下:https://download.youkuaiyun.com/download/c18213590220/88653032?spm=1001.2014.3001.5503
4.1. 项目结构
本项目结构主要由父模块,网关微服务及外部表微服务构成,其接口访问流程如下图所示。
由上图看出,比起远程调用,外部表接口从请求到返回只经过了一个微服务。
4.2. 新增外部表
我简单说sql文件夹里面的目录结构,foreign里面是构建外部表的语句,original里面是原始表的语句(外部表基于原始表抽取而来)。
在navicat中执行外部表构建操作
4.3. 访问外部表对应接口
启动网关微服务及外部表微服务,访问接口。
6. 结语
以上就是本章的全部内容,多数据源系列现在更新了AbstractRoutingDataSource 及外部表两篇文章,想看多数据源其他技术栈的同学可以在评论区留言,我看到以后会(酌情)更新的~~。