<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="zh-Hans-CN">
	<id>https://wiki.riguz.com/index.php?action=history&amp;feed=atom&amp;title=Blog%3A%E4%B8%80%E4%B8%AA%E6%95%B0%E6%8D%AE%E5%AF%BC%E5%85%A5%E7%9A%84%E6%9C%89%E8%B6%A3%E9%97%AE%E9%A2%98</id>
	<title>Blog:一个数据导入的有趣问题 - 版本历史</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.riguz.com/index.php?action=history&amp;feed=atom&amp;title=Blog%3A%E4%B8%80%E4%B8%AA%E6%95%B0%E6%8D%AE%E5%AF%BC%E5%85%A5%E7%9A%84%E6%9C%89%E8%B6%A3%E9%97%AE%E9%A2%98"/>
	<link rel="alternate" type="text/html" href="https://wiki.riguz.com/index.php?title=Blog:%E4%B8%80%E4%B8%AA%E6%95%B0%E6%8D%AE%E5%AF%BC%E5%85%A5%E7%9A%84%E6%9C%89%E8%B6%A3%E9%97%AE%E9%A2%98&amp;action=history"/>
	<updated>2026-06-02T21:43:17Z</updated>
	<subtitle>本wiki上该页面的版本历史</subtitle>
	<generator>MediaWiki 1.42.3</generator>
	<entry>
		<id>https://wiki.riguz.com/index.php?title=Blog:%E4%B8%80%E4%B8%AA%E6%95%B0%E6%8D%AE%E5%AF%BC%E5%85%A5%E7%9A%84%E6%9C%89%E8%B6%A3%E9%97%AE%E9%A2%98&amp;diff=2650&amp;oldid=prev</id>
		<title>imported&gt;Riguz：​相比于传统的单体应用，在基于微服务架构的系统中进行数据导入的操作显得更加复杂一点。通常而言，微服务的架构中包含了多个服务，服务的技术架构也可能大相径庭，同时考虑到拓展的需要，每个服务都有可能会拓展成多个instance。最近遇到一个有趣的问题，进行了一些思考。</title>
		<link rel="alternate" type="text/html" href="https://wiki.riguz.com/index.php?title=Blog:%E4%B8%80%E4%B8%AA%E6%95%B0%E6%8D%AE%E5%AF%BC%E5%85%A5%E7%9A%84%E6%9C%89%E8%B6%A3%E9%97%AE%E9%A2%98&amp;diff=2650&amp;oldid=prev"/>
		<updated>2019-01-14T00:00:00Z</updated>

		<summary type="html">&lt;p&gt;相比于传统的单体应用，在基于微服务架构的系统中进行数据导入的操作显得更加复杂一点。通常而言，微服务的架构中包含了多个服务，服务的技术架构也可能大相径庭，同时考虑到拓展的需要，每个服务都有可能会拓展成多个instance。最近遇到一个有趣的问题，进行了一些思考。&lt;/p&gt;
&lt;p&gt;&lt;b&gt;新页面&lt;/b&gt;&lt;/p&gt;&lt;div&gt;&lt;br /&gt;
相比于传统的单体应用，在基于微服务架构的系统中进行数据导入的操作显得更加复杂一点。通常而言，微服务的架构中包含了多个服务，服务的技术架构也可能大相径庭，同时考虑到拓展的需要，每个服务都有可能会拓展成多个instance。最近遇到一个有趣的问题，进行了一些思考。&lt;br /&gt;
场景大致是这样的：&lt;br /&gt;
&lt;br /&gt;
* 我们有一个单独的service（以下简称SM），每天定时从一个目录读取文件（一个压缩包）。其中这个包中包括多个文件，分别对应到不同的业务数据，这些数据又影响到多个不同的service（以下简称SA，SB，SC）&lt;br /&gt;
* 于是SM读取到文件之后，解析文件，并通过消息发送给SA，SB，SC。收到消息大小的限制，文件中的内容不能一次性发送完成，需要拆分成N个消息（比如每200条数据一个消息）&lt;br /&gt;
* SA，SB都是增量更新，因此收到数据后，要么新增，要么更新，就可以了。很完美。&lt;br /&gt;
* 但是SC确每次都是全量更新。&lt;br /&gt;
&lt;br /&gt;
问题来了，如果按照SA，SB的做法，SC面临的问题有两个：&lt;br /&gt;
&lt;br /&gt;
* 如果没有办法区分一条数据是新增还是更新，那直接有问题&lt;br /&gt;
* 即便可以，如何能删除多余的数据？ 譬如原来有200条，现在过来150条，这150条更新了，多余的50条则没有办法删除&lt;br /&gt;
&lt;br /&gt;
一个直接的办法是在SM开始的时候，先去SC删除所有的数据，然后再将数据发送过去就可以了。但是这样带来的问题就是，万一后面导入失败了，而删除已经做了，会造成以前的数据也不可用。这在我们的业务场景里面是比较致命的，应该至少保证同步失败的时候，保持上一次的数据。基于这个场景，想了一个解决方案:&lt;br /&gt;
&lt;br /&gt;
* 在数据表中加一个version字段标识，表示是哪一批次的数据&lt;br /&gt;
* 创建一个视图，根据version来查询出最新的数据&lt;br /&gt;
* 同步完成之后，更新视图&lt;br /&gt;
&lt;br /&gt;
下面详细来说。假定我们是一个K-V类似的配置的导入，首先创建表和视图:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
create table t_raw_config(&lt;br /&gt;
id int not null primary key auto_increment,&lt;br /&gt;
`version` char(6) not null,&lt;br /&gt;
name varchar(50) not null,&lt;br /&gt;
value varchar(100)&lt;br /&gt;
);&lt;br /&gt;
&lt;br /&gt;
create view t_config&lt;br /&gt;
as&lt;br /&gt;
select id, name, value from t_raw_config;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
在SM中，我们需要做的事情是：&lt;br /&gt;
&lt;br /&gt;
* 在导入开始的时候，生成一个唯一的version，简单一点，我们根据日期来，比如${\displaystyle version=20190101}$&lt;br /&gt;
* 假定有1001条数据，每200个拆分成1个message，则有6个${\displaystyle message=\{1, 2, 3, 4, 5, 6\}}$&lt;br /&gt;
* SC中，每消费完成一个message，则记录下消费的message到一个列表中，例如${\displaystyle  messages20190101 = \{1, 2\}}$（例如存储在redis中）&lt;br /&gt;
* SM中解析完成后，告知SC所有的message，即${\displaystyle message=\{1, 2, 3, 4, 5, 6\}}$&lt;br /&gt;
* SC收到告知后，比对${\displaystyle  messages20190101}$是否与${\displaystyle message=\{1, 2, 3, 4, 5, 6\}}$匹配，如果不匹配则等待（可以利用redis的BLPOP实现，如果不匹配则不断去BLPOP）&lt;br /&gt;
* 所有的消息都消费完成后，更新视图，将version设置为${\displaystyle version=20190101}$。如果考虑到性能问题，可以再version字段上建索引；考虑空间问题，可以在这一步以前version的数据&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;/div&gt;</summary>
		<author><name>imported&gt;Riguz</name></author>
	</entry>
</feed>