很长一段时间,我从未遇到过任何使用FEDERATED表的系统。但最近,人们对FEDERATED表似乎更感兴趣。这与微服务的增长有关吗? 在微服务体系结构中,服务通常设计为小型、松散耦合且可独立部署。每个服务可能都有自己的数据库,这使访问和集成来自多个服务的数据变得困难。 FEDERATED引擎可以提供一种虚拟表,这种表可以从远程 MySQL 服务器访问数据。这允许您将来自不同服务的数据集成到单个数据库中,而无需复杂的复制方案或手工数据管理。 让我们来看看如何将FEDERATED存储引擎与MySQL 8.0一起使用。
01
—
架构
我们有 2 个 MySQL 服务器:
- production
- sales
production服务器有一个product表,其中包含所有产品的成本(购买价格)、销售的价格和库存数量,它还有一个保存供应商信息的supplier表。 sales服务器有order表,保存订单和客户的相关信息。 在production服务器中还有一个product_view视图,该视图仅允许访问我们要与销售人员共享的product表中的数据。 在sales服务器中还有一个product表,它是一个FEDERATED表,指向production服务器中的product_view视图:
02
—
Production中的表
这是production服务器中表的定义:
product表
CREATE TABLE `product` ( `id` int unsigned NOT NULL AUTO_INCREMENT, `name` varchar(20) DEFAULT NULL, `supplier_id` int unsigned DEFAULT NULL, `purchase_cost` decimal(8,2) DEFAULT NULL, `price` decimal(8,2) DEFAULT NULL, `quantity` int NOT NULL DEFAULT '0', PRIMARY KEY (`id`), KEY `supplier_id` (`supplier_id`), CONSTRAINT `product_ibfk_1` FOREIGN KEY (`supplier_id`) REFERENCES `supplier` (`id`)) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
supplier表
CREATE TABLE `supplier` ( `id` int unsigned NOT NULL AUTO_INCREMENT, `name` varchar(30) DEFAULT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
product_view视图
这不是一张表,这是一个视图
CREATE ALGORITHM=UNDEFINED DEFINER=`msandbox`@`localhost` SQL SECURITY DEFINER VIEW `product_view` ASselect `product`.`id` AS `id`,`product`.`name` AS `name`, `product`.`price` AS `price`, if((`product`.`quantity` > 0),'yes','no') AS `stock`from `product`
下面是视图中的字段类型:
desc product_view;+-------+--------------+------+-----+---------+-------+| Field | Type | Null | Key | Default | Extra |+-------+--------------+------+-----+---------+-------+| id | int unsigned | NO | | 0 | || name | varchar(20) | YES | | NULL | || price | decimal(8,2) | YES | | NULL | || stock | varchar(3) | NO | | | |+-------+--------------+------+-----+---------+-------+4 rows in set (0.00 sec)
这意味着从production服务器中,我们只想共享字段id, name, price和是否有存货,这是我们将使用FEDERATED表提供的信息。因此,FEDERATED表可以指向远程表,也可以指向视图。
03
—
Sales中的表
customer表
CREATE TABLE `customer` ( `id` int unsigned NOT NULL AUTO_INCREMENT, `name` varchar(20) DEFAULT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
orders表
CREATE TABLE `orders` ( `id` int unsigned NOT NULL AUTO_INCREMENT, `customer_id` int unsigned DEFAULT NULL, `date` timestamp NULL DEFAULT CURRENT_TIMESTAMP, `status` enum('pending','confirmed','shipped','delivered','paid') DEFAULT NULL, PRIMARY KEY (`id`), KEY `customer_id` (`customer_id`), CONSTRAINT `orders_ibfk_1` FOREIGN KEY (`customer_id`) REFERENCES `customer` (`id`)) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
order_lines表
CREATE TABLE `order_lines` ( `id` int unsigned NOT NULL AUTO_INCREMENT, `order_id` int unsigned NOT NULL, `product_id` int unsigned NOT NULL, `price` decimal(8,2) NOT NULL, `quantity` int NOT NULL, PRIMARY KEY (`id`), KEY `order_id` (`order_id`), CONSTRAINT `order_lines_ibfk_1` FOREIGN KEY (`order_id`) REFERENCES `orders` (`id`)) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
product表
CREATE TABLE `product` ( `id` int unsigned NOT NULL AUTO_INCREMENT, `name` varchar(20) DEFAULT NULL, `price` decimal(8,2) DEFAULT NULL, `stock` varchar(3) DEFAULT NULL, PRIMARY KEY (`id`)) ENGINE=FEDERATED DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci CONNECTION='remote/product_view'
上面的product表使用FEDERATED作为存储引擎,这里的字段类型和product_view的定义一致。 我们接着要创建创建远程服务器。
04
—
远程访问
从sales服务器访问production服务器上的product_view视图,我们首先需要在production服务器上创建一个专用用户:
create user remote_user identified by 'remoteme';grant select on production.product_view to remote_user;
这意味着用户 remote_user 将能够使用密码 remoteme 连接到production服务器,并且对product_view视图只有SELECT权限。 在sales服务器上,我们需要创建一个用于FEDERATED表的SERVER。我们已经在FEDERATED表的CONNECTION参数中订阅这个SERVER为remote。
create server 'remote' foreign data wrapper mysql options(user 'remote_user', host '192.168.1.56', port 3306, password 'remoteme', database 'production');
05
—
测试
现在,让我们在production服务器上造一些数据,然后查看product和product_view中的记录:
select * from product;+----+------------------+-------------+---------------+-------+----------+| id | name | supplier_id | purchase_cost | price | quantity |+----+------------------+-------------+---------------+-------+----------+| 1 | dolphin keychain | 1 | 0.50 | 1.50 | 50 || 2 | dolphin sickers | 1 | 0.20 | 1.00 | 10 || 3 | dolphin pens | 2 | 1.00 | 2.00 | 10 |+----+------------------+-------------+---------------+-------+----------+3 rows in set (0.00 sec) select * from product_view;+----+------------------+-------+-------+| id | name | price | stock |+----+------------------+-------+-------+| 1 | dolphin keychain | 1.50 | yes || 2 | dolphin sickers | 1.00 | yes || 3 | dolphin pens | 2.00 | yes |+----+------------------+-------+-------+3 rows in set (0.00 sec)
现在我们也可以尝试在sales服务器中查看product FEDERATED表中的记录:
select * from product;+----+------------------+-------+-------+| id | name | price | stock |+----+------------------+-------+-------+| 1 | dolphin keychain | 1.50 | yes || 2 | dolphin sickers | 1.00 | yes || 3 | dolphin pens | 2.00 | yes |+----+------------------+-------+-------+
如您所见,我们可以访问production服务器中product_view视图中的数据。例如,我们可以创建这样的订单:
start transaction;Query OK, 0 rows affected (0.00 sec) insert into orders(customer_id, status) values (1,'pending');Query OK, 1 row affected (0.00 sec) select * from orders;+----+-------------+---------------------+---------+| id | customer_id | date | status |+----+-------------+---------------------+---------+| 1 | 1 | 2023-03-07 13:04:05 | pending |+----+-------------+---------------------+---------+1 row in set (0.00 sec) select last_insert_id() into @order_id;Query OK, 1 row affected (0.00 sec) insert into order_lines (order_id, product_id, price, quantity) values (@order_id,1,(select price from product where id=1),2);Query OK, 1 row affected (0.00 sec) insert into order_lines (order_id, product_id, price, quantity) values (@order_id,2,(select price from product where id=2),5);Query OK, 1 row affected (0.01 sec) commit;Query OK, 0 rows affected (0.02 sec) select * from order_lines;+----+----------+------------+-------+----------+| id | order_id | product_id | price | quantity |+----+----------+------------+-------+----------+| 1 | 1 | 1 | 1.50 | 2 || 2 | 1 | 2 | 1.00 | 5 |+----+----------+------------+-------+----------+2 rows in set (0.00 sec) select o.id, c.name as 'customer name', date, status, sum(price*quantity) tot from orders o join order_lines on order_lines.order_id=o.id join customer c on c.id=customer_id group by o.id;+----+---------------+---------------------+---------+------+| id | customer name | date | status | tot |+----+---------------+---------------------+---------+------+| 1 | Lenka corp | 2023-03-07 13:04:05 | pending | 8.00 |+----+---------------+---------------------+---------+------+
06
—
结论
如您所见,使用FEDERATED引擎在MySQL服务器之间创建链接非常容易,当您需要从另一台服务器访问有限数量的数据并且不希望使用基于复制的解决方案时,这尤其有用。 我看到了FEDERATED引擎在微服务架构中的新机遇。MySQL的官方手册中有一整节专门介绍FEDERATED引擎:dev.mysql.com/doc/refman/8.0/en/federated-storage-engine.html