**高并发、微服务 、性能调优实战案例100讲，所有案例均源于个人工作实战，均配合代码落地**

加我微信：itsoku，所有案例均提供在线答疑。



# 第79节 微服务跨库查询，如何解决？一次性搞懂

非常实用的一节，有点长，先收藏点赞，慢慢看。

<span style="font-weight:bold; color:red">目前整个课程59块钱，100个案例，含所有源码 & 文档 & 技术支持，可点击左下角小黄车了解</span>



# 1、背景

大家都比较熟悉微服务，通常每个服务都有自己单独的数据库。

## 1.1、电商系统

假如我们有个电商系统，里面有2个核心的服务

- 用户服务和订单服务
- 用户服务对应用户库
- 订单服务对应订单库
- 2个服务之间通过接口进行交互，比如要获取对方db中数据，也只能通过接口进行操作

![image-20240825093409596](img/image-20240825093409596.png)

## 1.2、用户库-有个用户表(t_user)

| 字段      | 说明         |
| --------- | ------------ |
| user_id   | 用户id，主键 |
| user_name | 用户名       |
| mobile    | 手机号       |

## 1.3、订单库-有个订单表(t_order)

只列出了本文需要的字段，其他字段省略了。

| 字段     | 说明                         |
| -------- | ---------------------------- |
| order_id | 订单号，主键                 |
| user_id  | 用户id，来源于用户表(t_user) |



# 2、需求1：如何通过手机号查询订单列表？

## 方案1：跨服务多次查询

1. step1：先调用用户服务接口，去用户表，根据手机号查询到用户id

   ```sql
   String userId = select user_id from t_user where mobile = '手机号'
   ```

2. step2：然后调用订单服务，去订单表，根据用户id便可查询用户的订单列表

   ```sql
   List<Order> orderList = select * from t_order where user_id = #{user_id}
   ```

3. step3：将结果返回

## 方案2：数据冗余的方式

1. step1：在订单表，冗余手机号字段：user_mobile，如下，生成订单的时候，将用户手机号写入到订单表

   | 字段        | 说明                         |
   | ----------- | ---------------------------- |
   | order_id    | 订单号，主键                 |
   | user_id     | 用户id，来源于用户表(t_user) |
   | user_mobile | 用户手机号                   |

   ```sql
   insert into t_order (order_id,user_id,user_mobile) values (订单id, 用户id，用户手机号)
   ```

2. step2：此时通过手机号查询订单列表，只需走订单服务，查询订单表便可得到结果

   ```sql
   List<Order> orderList = select * from t_order where user_mobile = '手机号'
   ```



# 3、需求2：如何通过手机号模糊查询订单列表？

## 3.1、方案1：跨服务多次查询

1. step1：先调用用户服务接口，去用户表，根据手机号模糊查询得到用户id列表：userIdList

   ```sql
   List userIdLIst = select user_id from t_user where mobile like '%手机号%';
   ```

2. step2：然后调用订单服务，去订单表，根据userIdList便可查询用户的订单列表

   ```sql
   List orderList = select * from t_order where user_id in (userIdList)
   ```

3. step3：将结果返回



## 3.2、方案1：可能存在问题

假如根据手机号模糊匹配的用户特别多，比如有上千个或者更多，那么这种方式性能上存在问题。

此时建议采用方案2：数据冗余的方式。



## 3.3、方案2：数据冗余的方式

1. step1：在订单表，冗余手机号字段：user_mobile，如下，生成订单的时候，将用户手机号写入到订单表

   | 字段        | 说明                         |
   | ----------- | ---------------------------- |
   | order_id    | 订单号，主键                 |
   | user_id     | 用户id，来源于用户表(t_user) |
   | user_mobile | 用户手机号                   |

2. step2：此时通过手机号查询订单列表，只需走订单服务，查询订单表便可得到结果

   ```sql
   List orderList = select * from t_order where user_mobile like '%手机号%';
   ```



# 4、数据冗余方式存在问题：数据一致性问题

订单表冗余了用户手机号

假如用户通过用户服务修改了手机号，此时用户表手机号是最新的，而订单表还是用户旧的手机号

就出现了数据不一致的问题：用户库用户表中的手机号和订单库订单表中的手机号不一致

如何解决？向下看。



# 5、冗余方案中，数据不一致如何解决？



## 5.1、订单库添加冗余表：t_user_copy

可以在订单库创建一个用户冗余表，专门用来冗余用户的数据，表名可以叫：t_user_copy

如下，这个表的字段需要根据需求来，需要看下订单库需要用到用户表那些字段，把需要用到的字段冗余过来就可以了，没必要将用户表所有的字段都给拿过来

| 字段      | 说明         |
| --------- | ------------ |
| user_id   | 用户id，主键 |
| user_name | 用户名       |
| mobile    | 手机号       |



## 5.2、将用户库中的用户表数据，同步到订单库的冗余表

1. 刚创建订单库的冗余表（t_user_copy）的时候，上线后，可以通过程序全量从用户库的用户表同步一份过来
2. 然后开启增量同步：后期用户表数据发生变化后，比如用户表发生了insert、delete、update，可以通过MQ广播一条用户数变更记录，订单服务监听这个消息，然后将变化的同步到t_user_copy这个冗余表就可以了，下面来看下增量同步的具体步骤。



## 5.3、增量同步具体步骤

1. 用户表只能通过用户服务进行操作，用户表发生了任何 `insert、delete、update`，用户服务通过mq广播一条消息，如下，消息中只需要用户的id就可以了，不需要知道这条消息具体是什么操作引起的

   ```json
   {"userId":"本次发生变化的用户id"}
   ```

2. 订单服务订阅这种消息，收到消息后，做如下处理

   ```java
   //1、从消息中拿到用户id
   String userId = msg.userId;
   
   //2、上分布式锁，锁的key：UserCopy:用户id；这里为了避免数据一致性，要确保同一个userId，若来了多条消息，需要排队消费，所以上了分布式锁；若不上锁，假如同一个userId，发生多次update操作，会产生多条消息，可能会出现并发消费，最终同步过来的数据可能和用户表中的数据不一致
   String lockKey = "UserCopy:"+userId;
   lock(lockKey);
   
   //3、携带userId,调用用户服务提供的接口，拿到用户信息
   User user = 根据userId调用用户服务提供的接口，获取用户信息;
   
   //4、从冗余表先删除用户信息
   delete from t_user_copy where user_id = #{userId};
   
   //5、若user不为空，则插入到订单库冗余表,user=null；说明这个用户被删了
   if(user!=null){
   	insert into (t_user_copy) values (#{user});
   }
   
   //6、释放分布式锁
   unlock(lockKey);
   ```
   
   > 增量同步这个方案，建议大家多看几遍，消化下



# 6、总结

1. 关于跨库查询，提供了2种方案
2. 方案1：通过跨服务多次查询，解决问题
3. 方案2：通过数据冗余的方式，解决问题；不过需要考虑数据同步的问题，关于数据同步的，大家可以将上面的数据同步方案多看几遍，如果大家工作中遇到数据同步的方案，也可以采用这种方式



# 高并发 & 微服务 & 性能调优实战案例100讲

## 已更新 79 节课

<span style="font-weight:bold; color:red">目前整个课程59块钱，含所有源码 & 文档 & 技术支持，一杯咖啡的价格，还没下手的朋友，赶紧了</span>。

```java
1. 分片上传实战
2. 通用并发处理工具类实战
3. 实现一个好用接口性能压测工具类
4. 超卖问题的4种解决方案，也是防止并发修改数据出错的通用方案
5. Semaphore实现接口限流实战
6. 并行查询，优化接口响应速度实战
7. 接口性能优化之大事务优化
8. 通用的Excel动态导出功能实战
9. 手写线程池管理器，管理&监控所有线程池
10. 动态线程池
11. SpringBoot实现动态Job实战
12. 并行查询，性能优化利器，可能有坑
13. 幂等的4种解决方案，吃透幂等性问题
14. 接口通用返回值设计与实现
15. 接口太多，各种dto、vo不计其数，如何命名？
16. 一个业务太复杂了，方法太多，如何传参？
17. 接口报错，如何快速定位日志？
18. 线程数据共享必学的3个工具类：ThreadLocal、InheritableThreadLocal、TransmittableThreadLocal
19. 通过AOP统一打印请求链路日志，排错效率飞升
20. 大批量任务处理常见的方案（模拟余额宝发放收益）
21. 并发环境下，如何验证代码是否正常？
22. MySql和Redis数据一致性
23. SpringBoot数据脱敏优雅设计与实现
24. 一行代码搞定系统操作日志
25. Aop简化MyBatis分页功能
26. ThreadLocal 遇到线程池有大坑 & 通用解决方案
27. SpringBoot读写分离实战（一个注解搞定读写分离 && 强制路由主库）
28. MQ专题-MQ典型的使用场景
29. MQ专题-如何确保消息的可靠性
30. MQ专题-SpringBoot中，手把手教你实现事务消息
31. 手写一个好用的延迟任务处理工具类
32. MQ专题-MQ延迟消息通用方案实战
33. MQ消息幂等消费 & 消费失败衰减式重试通用方案 & 代码 & 文档
34. MQ专题：顺序消息通用方案实战 & 代码落地 & 文档
35. MQ专题：消息积压相关问题及解决思路
36. 分布式事务-MQ最终一致性-实现跨库转账（案例+源码+文档）
37. 分布式事务-MQ最终一致性-实现电商账户余额提现到微信钱包（案例+源码+文档）
38. 分布式事务：通用的TCC分布式事务生产级代码落地实战
39. 分布式锁详解
40. 分享一个特别好用的Redissson分布式锁工具类
41. 一个注解轻松搞定分布式锁
42. 微服务中如何传递公共参数？
43. 接口幂等，通用方案 & 代码落地
44. 微服务链路日志追踪实战
45. 接口测试利器HTTP Client，不用Postman也可以
46. 封装MyBatis，实现通用无SQL版CRUD功能ORM框架
47. MyBatisPlus 轻松实现多租户数据隔离
48. 电商系统-资金账户表设计 及 应用实战
49. UML画图神器：PlantUML，画图效率飞升
50. 多线程事务，3秒插入百万数据
51. SpringBoot中自动初始化数据库功能，非常好用
52. SpringBoot优雅停机
53. 分享一个特好用的集合工具类，开发效率轻松翻倍
54. 性能调优：线程死锁相关问题
55. 如何排查OOM？
56. cpu飙升，如何快速排查？
57. cpu飙升，使用Arthas，3秒定位问题
58. 接口响应慢，使用Arthas，3秒定位问题代码
59. 策略模式，轻松消除ifelse代码
60. 生产上，代码未生效，如何排查？
61. 使用MySQL，实现一个高性能，分布式id生成器
62. 方法执行异常，使用arthas，快速定位问题
63. 扫码登录详解
64. 使用hutool生成&解析二维码，太方便了
65. SpringBoot中，redis中实现排行榜
66. SpringBoot中，Redis如何实现查找附近的人功能？
67. SpringBoot中，接口签名，通用方案，一次性搞懂
68. SpringBoot中，接口加解密，通用方案实战
69. 分库、分表、分库分表，如何选择？
70. 分库分表：分表字段如何选择？
71. 分库分表：分表数量为什么建议是2的n次方？
72. 分库分表：如何平滑迁移数据？
73. 并发编程有多难？值得反复研究的一个案例
74. 使用Redis Pipeline，接口性能提升10倍
75. 电商中，重复支付如何解决？
76. 千万级数据，全表update的正确姿势
77. 优雅实现树形菜单，适用于所有树，太好用了
78. 接口调用利器：RestTemplate，吃透它
79. 微服务跨库查询，如何解决？一次性搞懂
```




## 课程部分大纲，连载中。。。。

以下课程均来源于个人多年的实战，均提供原理讲解 && 源码落地

1. 分片上传实战
2. 通用并发处理工具类实战
3. 实现一个好用接口性能压测工具类
4. 超卖问题的4种解决方案，也是防止并发修改数据出错的通用方案
5. Semaphore实现接口限流实战
6. 并行查询，优化接口响应速度实战
7. 接口性能优化之大事务优化
8. 通用的Excel动态导出功能实战
9. 手写线程池管理器，管理&监控所有线程池
10. 动态线程池
11. SpringBoot实现动态Job实战
12. 并行查询，性能优化利器，可能有坑
13. 幂等的4种解决方案，吃透幂等性问题
14. 接口通用返回值设计与实现
15. 接口太多，各种dto、vo不计其数，如何命名？
16. 一个业务太复杂了，方法太多，如何传参？
17. 接口报错，如何快速定位日志？
18. 线程数据共享必学的3个工具类：ThreadLocal、InheritableThreadLocal、TransmittableThreadLocal
19. 通过AOP统一打印请求链路日志，排错效率飞升
20. 大批量任务处理常见的方案（模拟余额宝发放收益）
21. 并发环境下，如何验证代码是否正常？
22. MySql和Redis数据一致性
23. SpringBoot数据脱敏优雅设计与实现
24. 一行代码搞定系统操作日志
25. Aop简化MyBatis分页功能
26. ThreadLocal 遇到线程池有大坑 & 通用解决方案
27. SpringBoot读写分离实战（一个注解搞定读写分离 && 强制路由主库）
28. MQ专题：MQ典型的7种使用场景
29. MQ专题：如何确保消息的可靠性
30. MQ专题：SpringBoot中，手把手教你实现事务消息
31. 手写一个好用的延迟任务处理工具类
32. MQ专题：延迟消息通用方案实战
33. MQ专题：消息幂等消费 & 消费失败自动重试通用方案 & 代码落地
34. MQ专题：顺序消息通用方案实战
35. MQ专题：消息积压问题
36. 分布式事务-MQ最终一致性-实现跨库转账（案例+源码+文档）
37. 分布式事务-MQ最终一致性-实现电商账户余额提现到微信钱包（案例+源码+文档）
38. 分布式事务：通用的TCC分布式事务生产级代码落地实战
39. 分布式锁详解
40. 分享一个特别好用的Redissson分布式锁工具类
41. 分布式锁：一个注解轻松实现布式锁
42. 微服务中如何传递上下文？实战
43. 接口幂等，通用方案 & 代码落地
44. 微服务链路日志追踪实战
45. 接口测试利器HTTP Client，不用Postman也可以
46. 封装MyBatis，实现通用无SQL版CRUD功能
47. MyBatisPlus 轻松实现 多租户数据隔离
48. 电商系统-资金账户表设计 及 应用实战
49. 开发者必须掌握的一款UML画图工具，画图效率飞升
50. 多线程事务，3秒插入百万数据
51. SpringBoot自动初始化数据库功能，太好用了
52. SpringBoot优雅停机
53. 分享一个特别好用的集合工具类，开发效率大幅提升
54. 性能调优：如何排查死锁？
55. 如何排查OOM？
56. cpu飙升，如何快速排查？
57. cpu飙升，使用Arthas，3秒定位问题
58. 接口响应慢，使用Arthas，3秒定位问题代码
59. 策略模式，轻松消除ifelse代码
60. 生产上，代码未生效，如何排查？
61. 使用MySQL，实现一个高性能，分布式id生成器
62. 方法执行异常，使用arthas，快速定位问题
63. 扫码登录详解
64. 使用hutool生成&解析二维码，太方便了
65. SpringBoot中，Redis如何实现排行榜功能？
66. SpringBoot中，Redis如何实现查找附近的人功能？
67. SpringBoot中，接口签名，通用方案，一次性搞懂
68. SpringBoot中，接口加解密，通用方案实战
69. 分库、分表、分库分表，如何选择？
70. 分库分表：分表字段如何选择？
71. 分库分表：分表数量为什么建议是2的n次方？
72. 分库分表：如何平滑迁移数据？
73. 并发编程有多难？值得反复研究的一个案例
74. 使用Redis Pipeline，接口性能提升10倍
75. 电商系统中，如何解决重复支付？
76. 千万级数据，全表update的正确姿势
77. 优雅实现树形菜单，适用于所有树，太好用了
78. 接口调用利器：RestTemplate，吃透它
79. 微服务跨库查询，如何解决？
80. 更多实战案例详解