<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.9.3">Jekyll</generator><link href="http://neyzoter.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="http://neyzoter.github.io/" rel="alternate" type="text/html" /><updated>2023-12-25T14:35:48+08:00</updated><id>http://neyzoter.github.io/feed.xml</id><title type="html">宋超超 | Charles</title><subtitle>B.A. of Automation,M.S. of Control sciece and engineering</subtitle><author><name>songchaochao</name></author><entry><title type="html">TDengine技术内幕初探</title><link href="http://neyzoter.github.io/2023/01/22/Intro-TDengine/" rel="alternate" type="text/html" title="TDengine技术内幕初探" /><published>2023-01-22T00:00:00+08:00</published><updated>2023-01-22T00:00:00+08:00</updated><id>http://neyzoter.github.io/2023/01/22/Intro-TDengine</id><content type="html" xml:base="http://neyzoter.github.io/2023/01/22/Intro-TDengine/">&lt;blockquote&gt;
  &lt;p&gt;TDengine的架构、数据模型、存储引擎。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1 id=&quot;0名词解释&quot;&gt;0.名词解释&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;超级表&lt;/p&gt;

    &lt;p&gt;表的模板。&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;1架构&quot;&gt;1.架构&lt;/h1&gt;

&lt;p&gt;对云原生的支持是现有数据库的方向。在支持云原生的过程中，会将数据库的组建进行合理拆分，比如存算分离。TDengine引入了pnode、dnode、vnode、mnode等概念，将功能层层分离。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/posts/2023-01-22-Intro-TDengine/tdengine-cluster-structure.webp&quot; width=&quot;600&quot; alt=&quot;按照路径组织物理量&quot; /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;pnode&lt;/p&gt;

    &lt;p&gt;物理节点：物理机、虚拟机、容器等。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;dnode&lt;/p&gt;

    &lt;p&gt;数据节点，包括&amp;gt;=0个vnode，0-1个mnode，0-1个qnode，0-1个snode。&lt;/p&gt;

    &lt;p&gt;标识=Endpoint（FDQN+PORT）。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;vnode&lt;/p&gt;

    &lt;p&gt;虚拟节点，只属于一个DB，一个DB可以有多个vnode；用于支持数据分片、负载均衡，防止数据过热或倾斜。&lt;/p&gt;

    &lt;p&gt;图中dnode1、dnode4和dnode5中的3个V2（vnode）为一个VGroup，其中有1个Leader Vnode，2个Follower Vnode；VGroup会尽可能均衡分散在多个dnode上，比如V2在dnode1、dnode4和dnode5上，V6在dnode4、dnode5和dnode6上，V4在dnode4、dnode5和dnode6上….最终保持dnode1-dnode6的资源是平衡（多个数据库之间表的数量、写入频率、数据量等多个因素）的。&lt;/p&gt;

    &lt;p&gt;存储时序数据，表的schema、标签值等。&lt;/p&gt;

    &lt;p&gt;标识=Endpoint+VGroupID。&lt;/p&gt;

    &lt;p&gt;创建数据库的时候指定VGROUPS（VGroup个数），就会自动创建VGROUPS个vnode（后期通过hash一致性算法来分配数据）。vnode表现在文件系统中的一个个目录。&lt;/p&gt;

    &lt;p&gt;如果一个数据库有 N 个副本，那一个虚拟节点组就有 N 个虚拟节点，但是只有一个是 leader，其他都是 follower。当应用将新的记录写入系统时，只有 leader vnode 能接受写的请求。如果 follower vnode 收到写的请求，系统将通知 taosc 需要重新定向。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;mnode&lt;/p&gt;

    &lt;p&gt;管理节点，负责所有数据节点运行状态的监控和维护，节点之间的负载均衡，元数据（包括用户、数据库、超级表等）的存储和管理；集群内mnode个数1-3个，包括一个leader node，数据更新操作只能在leader node上进行。&lt;/p&gt;

    &lt;p&gt;标识=Endpoint。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;qnode&lt;/p&gt;

    &lt;p&gt;计算节点，虚拟的逻辑单元，运行查询计算任务，也包括基于系统表实现的show命令。在整个集群中共享使用，不和特定的db绑定，可以执行多个不同db的查询计算任务。每个dnode上最多一个qnode。&lt;/p&gt;

    &lt;p&gt;查询过程：客户端通过与 mnode 交互，获取可用的 qnode 列表，当没有可用的 qnode 时，计算任务在 vnode 中执行。当一个查询执行时，依赖执行计划，调度器会安排一个或多个 qnode 来一起执行。qnode 能从 vnode 获取数据，也可以将自己的计算结果发给其他 qnode 做进一步的处理。&lt;/p&gt;

    &lt;p&gt;通过引入独立的计算节点，TDengine 实现了&lt;strong&gt;存储和计算分离&lt;/strong&gt;。&lt;/p&gt;

    &lt;p&gt;标识=Endpoint。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;snode&lt;/p&gt;

    &lt;p&gt;流计算节点，只运行流计算任务，在整个集群内部共享使用；没有可用snode时，流计算任务在vnode中执行。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;VGroup&lt;/p&gt;

    &lt;p&gt;虚拟节点组，如果两个vnode的VGroupID相同，则数据互为备份。&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;TDengine的架构后期还需要好好研究，先有个大致的了解。&lt;/p&gt;

&lt;h1 id=&quot;2数据模型&quot;&gt;2.数据模型&lt;/h1&gt;

&lt;p&gt;TDengine可以通过定义表来创建一些数据的集合，类似于IoTDB的实体。不同的是，TDengine没有定义实体的路径，而是一个TABLE。&lt;/p&gt;

&lt;p&gt;TDengine支持通过定义超级表来实现快速创建表。用户可以在超级表定义列、标签名等信息。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/posts/2023-01-22-Intro-TDengine/data-model.png&quot; width=&quot;600&quot; alt=&quot;TDengine数据模型&quot; /&gt;&lt;/p&gt;

&lt;p&gt;TDengine的标签不同于InfluxDB，不是时间线的标记，而是落在表上。在创建表的时候设定标签。这一点类似于IoTDB。在某种程度上阻止用户为标签设定变化的值，造成大量的时间线创建（InfluxDB）。&lt;/p&gt;

&lt;h1 id=&quot;3存储&quot;&gt;3.存储&lt;/h1&gt;

&lt;p&gt;下方是一个典型的数据文件存储结构。&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;.
├── dnode
│   └── dnode.json
├── mnode
│   ├── data
│   │   └── sdb.data     // meta数据，见 dsbFile.C
│   ├── mnode.json
│   ├── sync
│   │   ├── raft_config.json
│   │   └── raft_store.json
│   ├── tmp
│   └── wal
│       ├── 00000000000000000000.idx
│       ├── 00000000000000000000.log
│       └── meta-ver0
└── vnode
    ├── vnode2
    │   ├── meta
    │   │   ├── invert
    │   │   ├── main.tdb       // tag索引；表名索引
    │   │   └── main.tdb-journal    // journal log，主要用于在修改main.tdb前备份，防止出现写入失败无法恢复的问题
    │   ├── sync
    │   │   ├── raft_config.json
    │   │   └── raft_store.json
    │   ├── tq
    │   │   ├── main.tdb
    │   │   └── stream
    │   │       ├── checkpoints
    │   │       ├── main.tdb
    │   │       └── main.tdb-journal
    │   ├── tsdb
    │   │   ├── CURRENT
    │   │   ├── v2f1736ver1.data  // 时序数据，v{vnode_id}f{fid}ver{commitID}
    │   │   ├── v2f1736ver1.sma  // 聚合缓存数据
    │   │   ├── v2f1736ver41.head // 时间主键索引，找表-&amp;gt;找时间范围的数据块及位置
    │   │   └── v2f1736ver41.stt // last文件，防止数据碎片化
    │   ├── vnode.json
    │   └── wal
    │       ├── 00000000000000009845.idx
    │       ├── 00000000000000009845.log
    │       ├── 00000000000000009966.idx
    │       ├── 00000000000000009966.log
    │       └── meta-ver123
    ├── vnode3
    │   ├── meta
    │   │   ├── invert
    │   │   ├── main.tdb
    │   │   └── main.tdb-journal
    │   ├── sync
    │   │   ├── raft_config.json
    │   │   └── raft_store.json
    │   ├── tq
    │   │   ├── main.tdb
    │   │   └── stream
    │   │       ├── checkpoints
    │   │       ├── main.tdb
    │   │       └── main.tdb-journal
    │   ├── tsdb
    │   │   ├── CURRENT
    │   │   ├── v3f1736ver1.data   // v{vgroup_id}f{fid}ver{commitID}
    │   │   ├── v3f1736ver1.sma
    │   │   ├── v3f1736ver41.head
    │   │   └── v3f1736ver41.stt
    │   ├── vnode.json
    │   └── wal
    │       ├── 00000000000000009837.idx
    │       ├── 00000000000000009837.log
    │       ├── 00000000000000009958.idx
    │       ├── 00000000000000009958.log
    │       └── meta-ver123
    └── vnodes.json
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;mnode&lt;/strong&gt;目录是mnode的相关信息，涉及到用户、数据库、超级表等。mnode在TDengine的早期还会有表的元数据。由于mnode的性能问题，已经被逐步剥离到vnode中。从文件名中也能看到TDengine通过RAFT协议来保证元信息在不同节点间的一致性。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;vnode&lt;/strong&gt;目录用于存储时序数据以及相关的元数据。下图是数据的存储层级：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/posts/2023-01-22-Intro-TDengine/data-model-level.png&quot; width=&quot;600&quot; alt=&quot;TDengine数据模型层级&quot; /&gt;&lt;/p&gt;

&lt;p&gt;我们可以看到目录下有node{数字}这类目录名。在创建数据库的时候即可设定VGROUPS，也就是vnode组的个数。TDengine通过数字编号来区分不同的vnode。TDengine基于一致性Hash算法去分配数据。一致性Hash算法在集群增加节点时，能够使得数据尽可能少地搬迁。需要说明的是，目前没有看到TDengine创建数据库后更改VGROUPS的能力，也就是说一致性Hash“使得数据尽可能少地搬迁”的能力没有用到。一致性Hash在TDengine中只起到了Hash来对数据进行负载均衡的作用。&lt;/p&gt;

&lt;p&gt;每个vnode下还有元数据meta、流式计算tq、时序数据tsdb、WAL 等信息。meta目录中有标签的索引。用户根据标签查询数据时，TDengine会先从每个vnode中搜索标签对应的表信息，再根据这些表信息来进行数据查询。下图是TDengine根据TAG查询表的日志，可以看到在每个vnode中都进行了查询：&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;groupid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;location&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;meters&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;groupid&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;9&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;order&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;by&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ts&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;limit&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/images/posts/2023-01-22-Intro-TDengine/list-all-tables.png&quot; width=&quot;600&quot; alt=&quot;TDengine根据TAG查询&quot; /&gt;&lt;/p&gt;

&lt;p&gt;tsdb目录是重头戏，包括.head、.data、.stt、.sma。.head文件是主索引文件。TDengine根据表名、时间等信息可以快速索引到.data文件的数据块。数据块索引中记录了数据块存放的文件、偏移、数据时间的范围，TDengine可以通过BRIN索引方式来查询数据。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/posts/2023-01-22-Intro-TDengine/head-file.png&quot; width=&quot;600&quot; alt=&quot;TDengine的head文件&quot; /&gt;&lt;/p&gt;

&lt;p&gt;.data文件数据追加式写入（LSM树的理念）数据块。&lt;strong&gt;每个数据块只属于vnode的一张表&lt;/strong&gt;。数据块的数据按照时间排列，同一列的数据存放在一起。数据块某一列信息中包括类型、压缩算法、列数据偏移、列数据长度。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/posts/2023-01-22-Intro-TDengine/data-file.png&quot; width=&quot;600&quot; alt=&quot;TDengine的data文件&quot; /&gt;&lt;/p&gt;

&lt;p&gt;.stt文件也就是last文件，主要用于解决数据碎片化的问题。在多表低频场景下，产生数据落盘后，数据块内的列信息的数据少，压缩效果不好。为了解决这个问题，TDengine将同一个超级表下的列信息存放在一起，提高压缩率。在先前的文章“&lt;a href=&quot;https://neyzoter.cn/2023/01/21/Intro-IoTDB/&quot;&gt;IoTDB技术内幕初探&lt;/a&gt;”中提到过这个问题。IoTDB没有last文件的概念，而是将数据一股脑追加进文件。IoTDB会通过不断合并文件来提高Page中数据的个数。&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;我认为在初期也会出现的，只是随着合并的不断发生，会大大减轻这种情况。如果时间分区的时间窗口越小，越可能出现这种问题。&lt;/p&gt;

  &lt;p&gt;– &lt;a href=&quot;https://neyzoter.cn/2023/01/21/Intro-IoTDB/&quot;&gt;IoTDB技术内幕初探&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;为什么TDengine会出现这种问题呢？因为TDengine的存储层级是表（数据块）-列，索引单位是数据块；而IoTDB是实体（ChunkGroup，类似于表）-大时间线（Chunk）-小时间线（Page），索引单位是Chunk。从上边的结构来看，IoTDB更容易将时间相近的数据（其实是一条时间线）合并。而TDengine考虑到不同列（时间线）合并在一个数据块，所以就会造成不同时间线耦合在一起。有问题的话，请大家指正。&lt;/p&gt;

&lt;p&gt;sma全称Small Materialized Aggregates。.sma文件保存了每个数据块的聚合结果（max/min/sum/count），方便快速查询。&lt;/p&gt;

&lt;h1 id=&quot;4演进与展望&quot;&gt;4.演进与展望&lt;/h1&gt;

&lt;p&gt;TDengine有较多值得我们学习的地方，也有一些问题，值得我们去展望和改进。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;值得我们学习的是&lt;/strong&gt;：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;对云原生的支持&lt;/p&gt;

    &lt;p&gt;提出了mnode、vnode、snode等组件模型。我现在不知道这块是否设计足够合理。后期也是我要进一步学习和深挖的地方。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;表信息跟随vnode，而不是mnode&lt;/p&gt;

    &lt;p&gt;将压力分散到多个vnode中。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;最新值缓存&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;数据分级存储&lt;/p&gt;

    &lt;p&gt;将不同时间段的数据存储在挂载的不同介质的目录里，从而实现不同“热度”的数据存储在不同的存储介质上，充分利用存储。&lt;/p&gt;

    &lt;p&gt;&lt;img src=&quot;/images/posts/2023-01-22-Intro-TDengine/tdengine-level-storage.png&quot; width=&quot;600&quot; alt=&quot;TDengine的分级存储&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;基于WAL的订阅服务&lt;/p&gt;

    &lt;p&gt;利用WAL的时间有序特性，又提供了数据订阅这一刚需&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;高基数问题（时间线膨胀）的解决方法&lt;/p&gt;

    &lt;ul&gt;
      &lt;li&gt;将表分布到vnode中，实现负载均衡&lt;/li&gt;
      &lt;li&gt;元数据和时序数据分离，减少重复存储&lt;/li&gt;
      &lt;li&gt;在vnode中存储表的元数据&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;代码&lt;/p&gt;

    &lt;ul&gt;
      &lt;li&gt;
        &lt;p&gt;对信号的处理&lt;/p&gt;

        &lt;p&gt;忽略SIGPIPE，防止进程被结束&lt;/p&gt;

        &lt;p&gt;其他的信号处理，可以看dmSetSignalHandler（v3版本）函数&lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;有意义的参数不要占用默认值&lt;/p&gt;

        &lt;p&gt;比如字符串的空、整数类型的0都不要作为枚举类型的有意义的值，否则可能造成无法区分未配置和有意义的设置&lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;通过commitID（Version）来区分同一个数据点的先后顺序&lt;/p&gt;

        &lt;p&gt;可用于处理数据的删除、更新等操作&lt;/p&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;展望和改进&lt;/strong&gt;：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;同一个表的不同列存放在一个数据块，不灵活&lt;/li&gt;
  &lt;li&gt;模板不支持绑定、解绑，不灵活&lt;/li&gt;
  &lt;li&gt;数据存储时间为数据库级别&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;5参考&quot;&gt;5.参考&lt;/h1&gt;

&lt;p&gt;&lt;a href=&quot;https://docs.taosdata.com/intro/&quot;&gt;1. TDengine官方文档&lt;/a&gt;&lt;/p&gt;</content><author><name>songchaochao</name></author><category term="TSDB" /><summary type="html">TDengine的架构、数据模型、存储引擎。</summary></entry><entry><title type="html">IoTDB技术内幕初探</title><link href="http://neyzoter.github.io/2023/01/21/Intro-IoTDB/" rel="alternate" type="text/html" title="IoTDB技术内幕初探" /><published>2023-01-21T00:00:00+08:00</published><updated>2023-01-21T00:00:00+08:00</updated><id>http://neyzoter.github.io/2023/01/21/Intro-IoTDB</id><content type="html" xml:base="http://neyzoter.github.io/2023/01/21/Intro-IoTDB/">&lt;blockquote&gt;
  &lt;p&gt;IoTDB的数据模型、存储引擎。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1 id=&quot;0名词解释&quot;&gt;0.名词解释&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;物理量&lt;/p&gt;

    &lt;p&gt;Measurement，IoTDB的最基本的数据点，类似于InfluxDB的时间线概念。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;逻辑存储组&lt;/p&gt;

    &lt;p&gt;Storage Group，对应到数据库（Database）。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;实体&lt;/p&gt;

    &lt;p&gt;Entity，也叫设备，可以包含多个物理量。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;标签&lt;/p&gt;

    &lt;p&gt;Tag，用于对物理量进行标记，可用于快速批量搜索等场景。&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;1数据模型&quot;&gt;1.数据模型&lt;/h1&gt;

&lt;p&gt;IoTDB按照路径组织物理量，以下图为例，可以通过 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;root.ln.wf01.wt01.status&lt;/code&gt;唯一定位到一个物理量。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/posts/2023-01-21-Intro-IoTDB/data-model-example.png&quot; width=&quot;600&quot; alt=&quot;按照路径组织物理量&quot; /&gt;&lt;/p&gt;

&lt;p&gt;路径第一级为root，系统默认创建。路径第二级为数据库名，在存储概念中也称为逻辑存储组。存储路径倒数第二级表示实体，可以包含多个物理量。最后一级表示物理量。&lt;/p&gt;

&lt;p&gt;物理量的标签在物理量创建时设置，可以修改，但同一个物理量的标签是固定的。我们都知道InfluxDB中标签作为时间线的标识一部分。&lt;strong&gt;意味着改变标签后，InfluxDB将会新建时间线，而非原时间线。&lt;/strong&gt;很多人滥用标签，比如将TraceID放入到标签，造成了时间线快速膨胀，早晨性能问题。我认为TraceID这类不断变化的数据并不适合放到标签中。不管是InfluxDB还是IoTDB，这种做法必然破坏时序数据库的设计理念，造成创建大量的时间线，进而引发时间线膨胀问题。当然IoTDB本身就不支持为每个数据值设置标签。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;“为每个数据设置标签”和“高压缩率/高速写入/大数据量”这两种能力本身是矛盾的。&lt;/strong&gt;“为每个数据设置标签”往往是为了快速找到一些数据，必然需要为这些数据创建标签的索引。时序数据库的大数据量特点是一个根本的场景，也是从关系型数据库发展而来的基本需求。“为每个数据设置标签”的实现可能让时序数据回到关系型数据库的问题——索引极速膨胀，甚至超过数据本身。与此同时，时序数据高速写入的情况下，还需要管理标签的索引、标签的存储，大大加大CPU和IO资源压力。此外，时序数据库的“高压缩率”能力必将导致时序数据的编码/合并/压缩，伴随着数据大规模的搬迁。数据大规模的搬迁会让标签索引管理应接不暇。所以说，&lt;strong&gt;标签和物理量绑定的设计是正确的，也是妥协的产物&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;IoTDB支持创建模板，能够做到快速实例化实体。模板支持创建、挂载、激活、解绑、删除等方式。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/posts/2023-01-21-Intro-IoTDB/template-manage.png&quot; width=&quot;600&quot; alt=&quot;IoTDB的元数据模板&quot; /&gt;&lt;/p&gt;

&lt;p&gt;这里有一个问题，如果想要修改某个实体的模板，是做不到的。但&lt;strong&gt;实际的场景中，施工会出现频繁而持续的模板改动，甚至不同的实体会分化到不同的模板下。在系统维护节点，偶尔也会改动模板。&lt;/strong&gt;如果需要改动模板，则需要将原来的实体删除，造成所有数据丢失。用户不得已进行数据的备份和恢复工作。&lt;/p&gt;

&lt;h1 id=&quot;2存储&quot;&gt;2.存储&lt;/h1&gt;

&lt;p&gt;下方是一个典型的数据文件存储结构。&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;.
├── consensus
│   ├── data_region
│   │   └── 1_1
│   │       └── configuration.dat
│   └── schema_region
│       └── 47474747-4747-4747-4747-000200000000
│           ├── current
│           │   ├── log_inprogress_0
│           │   ├── raft-meta
│           │   └── raft-meta.conf
│           ├── in_use.lock
│           └── sm
├── data
│   ├── sequence
│   │   └── root.ln     // 数据库
│   │       └── 1      // DataRegionId，由实体path hash得到
│   │           └── 0   // 时间分区
│   │               ├── 1672106890628-1-0-0.tsfile    // 数据
│   │               ├── 1672106890628-1-0-0.tsfile.mods   // 数据修改记录
│   │               └── 1672106890628-1-0-0.tsfile.resource // 数据概述 
│   └── unsequence
│       └── root.ln
│           └── 1
│               └── 0
│                   ├── 1672107062990-2-0-0.tsfile
│                   ├── 1672107062990-2-0-0.tsfile.mods
│                   └── 1672107062990-2-0-0.tsfile.resource
├── system
│   ├── compression_ratio
│   │   └── Ratio-2.940341-2   // 压缩率信息
│   ├── databases
│   │   └── root.ln
│   │       └── 1
│   │           └── upgrade
│   │               └── Version-100
│   ├── roles
│   ├── schema
│   │   ├── root.ln
│   │   │   └── 0
│   │   │       └── tlog.txt    // tag信息
│   │   └── system.properties
│   ├── udf
│   │   └── tmp
│   ├── upgrade
│   │   └── upgrade.txt
│   └── users
│       └── root.profile
└── wal
    └── root.ln-1
        ├── _0.checkpoint
        └── _2-31-1.wal
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;consensus&lt;/strong&gt;目录是用于集群见保证数据一致性的。IoTDB底层基于RAFT实现数据一致性。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;data&lt;/strong&gt;目录是时序数据的存储目录，分为顺序sequence和乱序unsequence。IoTDB会为每个物理量维护顺序sequence的最新时间戳，如果新数据早于该时间戳，则认为是乱序的，需要将数据保存到乱序unsequence目录下。这么做的原因是IoTDB的特点（也是所有时序数据库的特点，具体可以看LSM树）决定的，即数据先缓存再批量刷盘。下图是IoTDB数据上报-缓存-刷盘-合并等过程的示意图。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/posts/2023-01-21-Intro-IoTDB/iotdb-lsm.png&quot; width=&quot;600&quot; alt=&quot;IoTDB存储引擎&quot; /&gt;&lt;/p&gt;

&lt;p&gt;为了提高数据写入速度，IoTDB会先数据缓存到内存也就是memtable。memtable达到一定的大小后，会将不同的时序数据组织成一条一条、按照时间排序的连续存储块（Chunk，每个Chunk下有多个存储页Page）批量刷入磁盘。在这个过程中会进行编码（比如二阶查分编码TS_2DIFF、游程编码RLE、字典编码DICTIONARY等）、压缩（比如SNAPPY压缩、LZ4压缩、GZIP压缩等）。Page的大小会限制在一定数据量，比如100个数据带你，是IoTDB编解码、压解缩的单位。回到“如果新数据早于该时间戳，则认为是乱序的，需要将数据保存到乱序unsequence目录下”的问题。如果允许乱序的数据立刻插入顺序sequence文件中，则需要定位到对应的Page，将其解压缩、解码、插入数据、编码、压缩并重新写入磁盘，搬迁所在的Chunk后其他数据并重新组织Chunk的索引。在上述过程中，会消耗较多的CPU和IO资源。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/posts/2023-01-21-Intro-IoTDB/tsfile.png&quot; width=&quot;600&quot; alt=&quot;IoTDB的TsFile&quot; /&gt;&lt;/p&gt;

&lt;p&gt;编码/压缩算法会根据历史数据进行学习，提高压缩率。所以为了进一步提高压缩率，IoTDB会将不同的文件进行合并，防止同一个物理量的数据无序/零散分布在不同的文件。与此同时，也会将乱序unsequence的数据合并到顺序sequence中。乱序和顺序合并后，也会在一定程度上提高查询效率。不然，我们可想而知，查询引擎需要在两个目录下都搜索一遍。&lt;/p&gt;

&lt;p&gt;在data的sequence或unsequnce目录下，按照数据库/DataRegion/时间分区的层级关系组织。DataRegion主要用于负载均衡，由实体的路径经过Hash得到。时间分区默认7天创建一个。时间分区的目录名表示自Unix时间0开始，7天的倍数。在每个时间分区下分为TsFile、Resource文件、Mods文件等。如果在合并期间刷盘，会增加携带“compaction”的文件，而不会立刻合并在其他的文件中。TsFile即上图，文件名按照{创建文件的时间}-{文件粒度版本号}-{内部合并次数}-{乱序合并次数}.tsfile的歌是。其中文件粒度版本号在每次新增一个TsFile都会增加1。Resource文件是TsFile的概述文件，包括时间索引信息、RAFT日志、墓碑文件路径等。&lt;/p&gt;

&lt;p&gt;data目录是存储存储引擎的核心，需要考虑&lt;strong&gt;时间线膨胀、高压缩率、高速写入、高速读取（比如通过统计信息）、删除、更新、顺序/乱序、保留策略&lt;/strong&gt;等诸多问题。从IoTDB的功能对比和性能测试结果看，压缩率、读写速度都很优秀。压缩率通过编码和压缩两个过程提升。用户可以根据自行选择每个物理量的编码和压缩算法。其中读取数据的速度通过Page块的统计信息、Bloom过滤器等方式提升。&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;TSDB&lt;/th&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;IoTDB&lt;/th&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;InfluxDB&lt;/th&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;OpenTSDB&lt;/th&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;KairosDB&lt;/th&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;TimescaleDB&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;em&gt;Align by time&lt;/em&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;&lt;strong&gt;++&lt;/strong&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;+&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;-&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;-&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;+&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;em&gt;Compression&lt;/em&gt;（压缩率）&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;&lt;strong&gt;++&lt;/strong&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;+-&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;+-&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;+-&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;+-&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;em&gt;MQTT support&lt;/em&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;&lt;strong&gt;++&lt;/strong&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;+&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;-&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;-&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;+-&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;em&gt;Run on Edge-side Device&lt;/em&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;&lt;strong&gt;++&lt;/strong&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;+&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;-&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;+-&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;+&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;em&gt;Multi-instance Sync&lt;/em&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;&lt;strong&gt;++&lt;/strong&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;-&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;-&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;-&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;-&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;em&gt;JDBC Driver&lt;/em&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;&lt;strong&gt;+&lt;/strong&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;-&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;-&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;-&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;++&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;em&gt;Standard SQL&lt;/em&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;+&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;-&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;-&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;-&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;&lt;strong&gt;++&lt;/strong&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;em&gt;Spark integration&lt;/em&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;&lt;strong&gt;++&lt;/strong&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;-&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;-&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;-&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;-&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;em&gt;Hive integration&lt;/em&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;&lt;strong&gt;++&lt;/strong&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;-&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;-&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;-&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;-&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;em&gt;Writing data to NFS (HDFS)&lt;/em&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;&lt;strong&gt;++&lt;/strong&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;-&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;+&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;-&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;-&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;em&gt;Flink integration&lt;/em&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;&lt;strong&gt;++&lt;/strong&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;-&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;-&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;-&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;-&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;TSDB&lt;/th&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;IoTDB&lt;/th&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;InfluxDB&lt;/th&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;KairosDB&lt;/th&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;TimescaleDB&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;em&gt;Scalable Writes&lt;/em&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;++&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;+&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;+&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;+&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;em&gt;Raw Data Query&lt;/em&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;++&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;+&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;+&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;+&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;em&gt;Aggregation Query&lt;/em&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;++&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;+&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;+&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;+&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;em&gt;Downsampling Query&lt;/em&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;++&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;+&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;+-&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;+-&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;em&gt;Latest Query&lt;/em&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;++&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;+&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;+-&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;+&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;删除、更新、顺序/乱序问题的处理过程和数据顺序刷盘过程解耦，看着很清晰。如果用过TDengine的话，会了解到有一个last文件的概念。主要用于在多表少频（点位多，点位数据产生慢）的情况下，将同一个超级表下的同类型数据放在一起，提高压缩率。IoTDB是否会出现多表低频下压缩率低的情况呢？我认为在初期也会出现的，只是随着合并的不断发生，会大大减轻这种情况。如果时间分区的时间窗口越小，越可能出现这种问题。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;system&lt;/strong&gt;目录是系统配置、标签、压缩率等信息。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;wal&lt;/strong&gt;目录是write-ahead-log。由于LSM内存先行的特点，可能出现数据丢失。wal目录用于保存所有的操作日志，方便在启动时恢复数据。这也是LSM树的基本操作。&lt;/p&gt;

&lt;h1 id=&quot;3演进与展望&quot;&gt;3.演进与展望&lt;/h1&gt;

&lt;p&gt;IoTDB有较多值得我们学习的地方，也有一些问题，值得我们去展望和改进。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;值得我们学习的是&lt;/strong&gt;（前文可能没有提到，可以前往IoTDB官网查看详细描述）：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;统计信息提升查询效率&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;统计memtable的大小，通过丢弃的方式防止OOM，提升稳定性&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;顺序/乱序数据分开处理，插入/修改数据过程分开处理，逻辑清晰，支撑高速写入&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;实体模板绑定和解绑，提升实例化效率&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;标签不作为时间线的标记，而是元数据的标记，防止标签被滥用&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;不同物理量可以共用时间线，提高压缩率&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;正在考虑和评估将索引和数据分开，索引支持B+树&lt;/p&gt;

    &lt;table&gt;
      &lt;thead&gt;
        &lt;tr&gt;
          &lt;th&gt;&lt;strong&gt;模块&lt;/strong&gt;&lt;/th&gt;
          &lt;th&gt;&lt;strong&gt;子模块及作用&lt;/strong&gt;&lt;/th&gt;
          &lt;th&gt;&lt;strong&gt;在TsFile外部缺点&lt;/strong&gt;&lt;/th&gt;
          &lt;th&gt;&lt;strong&gt;在TsFile外部优点&lt;/strong&gt;&lt;/th&gt;
        &lt;/tr&gt;
      &lt;/thead&gt;
      &lt;tbody&gt;
        &lt;tr&gt;
          &lt;td&gt;IndexOfTimeseriesIndex&lt;/td&gt;
          &lt;td&gt;时间序列索引的索引（二级索引）&lt;/td&gt;
          &lt;td&gt;系统管理文件数会翻倍读写打开文件数会翻倍&lt;/td&gt;
          &lt;td&gt;索引文件可以灵活修改，不影响数据文件；支持一个数据文件对应多索引文件；读取索引内容无需跳过数据区，磁盘定位快&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td&gt;BloomFilter&lt;/td&gt;
          &lt;td&gt;针对序列的布隆过滤&lt;/td&gt;
          &lt;td&gt; &lt;/td&gt;
          &lt;td&gt; &lt;/td&gt;
        &lt;/tr&gt;
      &lt;/tbody&gt;
    &lt;/table&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Chunk中的Page Header连续存储，而不是和Data混合&lt;/p&gt;

    &lt;p&gt;&lt;img src=&quot;/images/posts/2023-01-21-Intro-IoTDB/chunk-page-header-opt.png&quot; width=&quot;600&quot; alt=&quot;IoTDB的TsFile&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;区分编码和压缩&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;展望和改进&lt;/strong&gt;：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;实体模板不支持对一个实体重复绑定、修改和解绑&lt;/p&gt;

    &lt;p&gt;如果能够支持模板绑定后自动识别和原模型的区别，并处理差异，将会大大方便某些场景。比如在工业场景下，用户为很多设备（实体）定义了模板，但是需要对其中一些设备进行修改，其余的不变。用户可以直接创建一个新的模板，绑定到对应的设备上，自动识别差异。在这个过程中能保留未删除的数据，标记删除的数据，同时创建新增的数据。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;数据保留时间为数据库级别&lt;/p&gt;

    &lt;p&gt;不能像InfluxDB一样做到时间线级别的数据保留管理。这个和InfluxDB的存储结构有关——RP为顶层的目录，RP下数据的保留时间相同，所以InfluxDB可以快速删除文件，而不需要提取文件中的某些数据进行删除。这也引入了问题，数据的RP改变后，数据就查不到了。用户需要自行实现数据迁移。&lt;/p&gt;

    &lt;p&gt;IoTDB采用独立后台进程维护数据保留任务，在发现一个文件的数据超过期限，即可将文件直接删除。因为只支持到数据库级别的数据保留策略，所以也不需要判断哪些物理量可以删除。&lt;/p&gt;

    &lt;p&gt;两个方案的选择要看具体的场景，不可一概而论哪个更好。&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;4参考&quot;&gt;4.参考&lt;/h1&gt;

&lt;p&gt;&lt;a href=&quot;https://iotdb.apache.org/zh/UserGuide/V1.0.x/IoTDB-Introduction/Architecture.html&quot;&gt;1. IoTDB官方文档&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://cwiki.apache.org/confluence/pages/viewpage.action?pageId=177051872&quot;&gt;2. IoTDB设计文档&lt;/a&gt;&lt;/p&gt;</content><author><name>songchaochao</name></author><category term="TSDB" /><summary type="html">IoTDB的数据模型、存储引擎。</summary></entry><entry><title type="html">Mac网络访问Win的文件</title><link href="http://neyzoter.github.io/2020/12/04/Mac-Conn-To-Win/" rel="alternate" type="text/html" title="Mac网络访问Win的文件" /><published>2020-12-04T00:00:00+08:00</published><updated>2020-12-04T00:00:00+08:00</updated><id>http://neyzoter.github.io/2020/12/04/Mac-Conn-To-Win</id><content type="html" xml:base="http://neyzoter.github.io/2020/12/04/Mac-Conn-To-Win/">&lt;blockquote&gt;
  &lt;p&gt;Mac如何实现网络访问Win文件&lt;/p&gt;

&lt;/blockquote&gt;

&lt;h1 id=&quot;1需求&quot;&gt;1.需求&lt;/h1&gt;

&lt;p&gt;通过U盘传输文件不太方便，而且QQ、微信等即时通信软件只能在一台PC上登陆，无法实现快速的文件共享。Mac和Win可以通过smb来实现文件共享。&lt;/p&gt;

&lt;h1 id=&quot;2解决过程&quot;&gt;2.解决过程&lt;/h1&gt;

&lt;h2 id=&quot;21-准备工作&quot;&gt;2.1 准备工作&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;（1）保证Mac和Win在一个局域网下&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;可以通过ifconfig（Mac）或者ipconfig（Win）来查询自身IP地址，通过ping 对方的IP确保在一个局域网且可以联通&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;（2）Win配置——关闭防火墙&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;控制面板 -&amp;gt; Windows防火墙 -&amp;gt; 打开或关闭Windows防火墙 -&amp;gt; 将家庭或工作（专用）网络和公用网络的防火墙都关闭
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;是否可以选择只关闭一个？待考证。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;（3）Win配置——设置高级共享&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;控制面板 -&amp;gt; 选择家庭组和共享选项 -&amp;gt; 更改高级共享设置 -&amp;gt; 网络发现、文件和打印机共享、公用文件夹共享都设置为启用...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;（4）Win配置——添加用户&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;管理 -&amp;gt; 本地用户与组 -&amp;gt; 给“用户”添加一个用户
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;记住用户名和密码&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;（4）Win配置——安全选项&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;1、运行里面输入”secpol.msc”来启动本地安全设置&lt;/p&gt;

&lt;p&gt;2、网络安全LAN 管理器身份验证级别修改成“发送 LM 和 NTMLM响应(&amp;amp;)”或者“发送 LM 和 NTMLM - 如果已协商，则使用NTLMv2 会话安全”&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;本地策略 -&amp;gt; 安全选项 -&amp;gt; 网络安全LAN 管理器身份验证级别
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;3、打开来宾账户状态&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;本地策略 -&amp;gt; 安全选项 -&amp;gt; 来宾账户状态
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;4、网络访问：本地账户的共享和安全模型设置为“经典”&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;本地策略 -&amp;gt; 安全选项 -&amp;gt; 网络访问：本地账户的共享和安全模型
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;（5）Win配置——共享文件&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;文件夹 -&amp;gt; 右键选择共享 -&amp;gt; 特定用户 -&amp;gt; 添加之前创建的用户
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;22-mac访问&quot;&gt;2.2 Mac访问&lt;/h2&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;桌面 -&amp;gt; Go -&amp;gt; Connect to Server -&amp;gt; 输入smb://[Win的计算机名（可以通过属性得到）] -&amp;gt; 输入用户名和密码 -&amp;gt; 连接
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;</content><author><name>songchaochao</name></author><category term="Softwares" /><summary type="html">Mac如何实现网络访问Win文件</summary></entry><entry><title type="html">ZooKeeper报错Last transaction was partial</title><link href="http://neyzoter.github.io/2020/11/28/Zookeeper-Last-Transaction-Was-Partial/" rel="alternate" type="text/html" title="ZooKeeper报错Last transaction was partial" /><published>2020-11-28T00:00:00+08:00</published><updated>2020-11-28T00:00:00+08:00</updated><id>http://neyzoter.github.io/2020/11/28/Zookeeper-Last-Transaction-Was-Partial</id><content type="html" xml:base="http://neyzoter.github.io/2020/11/28/Zookeeper-Last-Transaction-Was-Partial/">&lt;blockquote&gt;
  &lt;p&gt;Zookeeper无法正常启动，报错Last transaction was partial&lt;/p&gt;

&lt;/blockquote&gt;

&lt;h1 id=&quot;1错误日志&quot;&gt;1.错误日志&lt;/h1&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[2020-11-28 10:28:59,552] INFO Using org.apache.zookeeper.server.NIOServerCnxnFactory as server connection factory (org.apache.zookeeper.server.ServerCnxnFactory)
[2020-11-28 10:28:59,556] INFO binding to port 0.0.0.0/0.0.0.0:2181 (org.apache.zookeeper.server.NIOServerCnxnFactory)
[2020-11-28 10:28:59,588] ERROR Last transaction was partial. (org.apache.zookeeper.server.persistence.Util)
[2020-11-28 10:28:59,589] ERROR Unexpected exception, exiting abnormally (org.apache.zookeeper.server.ZooKeeperServerMain)
java.io.EOFException
	at java.io.DataInputStream.readInt(DataInputStream.java:392)
	at org.apache.jute.BinaryInputArchive.readInt(BinaryInputArchive.java:63)
	at org.apache.zookeeper.server.persistence.FileHeader.deserialize(FileHeader.java:66)
	at org.apache.zookeeper.server.persistence.FileTxnLog$FileTxnIterator.inStreamCreated(FileTxnLog.java:588)
	at org.apache.zookeeper.server.persistence.FileTxnLog$FileTxnIterator.createInputArchive(FileTxnLog.java:607)
	at org.apache.zookeeper.server.persistence.FileTxnLog$FileTxnIterator.goToNextLog(FileTxnLog.java:573)
	at org.apache.zookeeper.server.persistence.FileTxnLog$FileTxnIterator.next(FileTxnLog.java:653)
	at org.apache.zookeeper.server.persistence.FileTxnSnapLog.fastForwardFromEdits(FileTxnSnapLog.java:219)
	at org.apache.zookeeper.server.persistence.FileTxnSnapLog.restore(FileTxnSnapLog.java:176)
	at org.apache.zookeeper.server.ZKDatabase.loadDataBase(ZKDatabase.java:217)
	at org.apache.zookeeper.server.ZooKeeperServer.loadData(ZooKeeperServer.java:284)
	at org.apache.zookeeper.server.ZooKeeperServer.startdata(ZooKeeperServer.java:407)
	at org.apache.zookeeper.server.NIOServerCnxnFactory.startup(NIOServerCnxnFactory.java:118)
	at org.apache.zookeeper.server.ZooKeeperServerMain.runFromConfig(ZooKeeperServerMain.java:122)
	at org.apache.zookeeper.server.ZooKeeperServerMain.initializeAndRun(ZooKeeperServerMain.java:89)
	at org.apache.zookeeper.server.ZooKeeperServerMain.main(ZooKeeperServerMain.java:55)
	at org.apache.zookeeper.server.quorum.QuorumPeerMain.initializeAndRun(QuorumPeerMain.java:119)
	at org.apache.zookeeper.server.quorum.QuorumPeerMain.main(QuorumPeerMain.java:81)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;2解决过程&quot;&gt;2.解决过程&lt;/h1&gt;

&lt;p&gt;（1）删除ZooKeeper安装目录下的logs&lt;/p&gt;

&lt;p&gt;无效果&lt;/p&gt;

&lt;p&gt;（2）删除ZooKeeper的数据文件夹下大小为0的文件&lt;/p&gt;

&lt;p&gt;数据文件夹位置可以通过zookeeper的配置文件得到。&lt;/p&gt;

&lt;div class=&quot;language-properties highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# the directory where the snapshot is stored.
&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;dataDir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;/tmp/zookeeper&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;删除该&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[dataDir]/version-2/&lt;/code&gt;下大小为0的快照文件，即可正常启动。&lt;/p&gt;

&lt;h1 id=&quot;3原因探究&quot;&gt;3.原因探究&lt;/h1&gt;

&lt;p&gt;（1）磁盘满了&lt;/p&gt;

&lt;p&gt;首先使用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;du -h --max-depth=1&lt;/code&gt;来逐层分析磁盘满的根本原因，比如我最终定位到docker文件夹，是因为Docker容器过多，需要将停止的容器清除，即可释放磁盘&lt;/p&gt;

&lt;p&gt;（2）ZooKeeper快照无法写入（待考证）&lt;/p&gt;

&lt;p&gt;由于磁盘满，所以ZooKeeper无法将快照写入到磁盘，导致了大小为0的快照文件&lt;/p&gt;

&lt;p&gt;下次启动时，如果从快照中读取数据，就会出现&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Last transaction was partial&lt;/code&gt;&lt;/p&gt;</content><author><name>songchaochao</name></author><category term="Backend" /><summary type="html">Zookeeper无法正常启动，报错Last transaction was partial</summary></entry><entry><title type="html">物流公司远程信息管理系统</title><link href="http://neyzoter.github.io/2020/06/20/Remote-Information-System-Express-Company/" rel="alternate" type="text/html" title="物流公司远程信息管理系统" /><published>2020-06-20T00:00:00+08:00</published><updated>2020-06-20T00:00:00+08:00</updated><id>http://neyzoter.github.io/2020/06/20/Remote-Information-System-Express-Company</id><content type="html" xml:base="http://neyzoter.github.io/2020/06/20/Remote-Information-System-Express-Company/">&lt;blockquote&gt;
  &lt;p&gt;物流公司远程信息管理系统，实现远程车辆监控和控制。&lt;/p&gt;

&lt;/blockquote&gt;

&lt;h1 id=&quot;1项目介绍&quot;&gt;1.项目介绍&lt;/h1&gt;

&lt;p&gt;本项目的应用场景是物流公司的远程信息管理。管理员可以远程查看汽车的状态，并设置汽车的上锁和开锁。&lt;/p&gt;

&lt;h1 id=&quot;2项目难点&quot;&gt;2.项目难点&lt;/h1&gt;

&lt;h2 id=&quot;21-设备接入点设计&quot;&gt;2.1 设备接入点设计&lt;/h2&gt;

&lt;p&gt;设备接入方式采用交通部的JT/T808标准协议。具体而言，设备和JT/T808服务器之间基于UDP/TCP来建立连接，并基于特定的消息体格式进行通信。本项目的其中一个目标是设计一个具备较好的抗并发能力的JT/T808接入点，类似MQTT协议的EMQ节点。我们将它取名为&lt;a href=&quot;https://jet.nescar.icu&quot;&gt;jet&lt;/a&gt;，属于我们的&lt;a href=&quot;https://armee.nescar.icu&quot;&gt;armee项目&lt;/a&gt;的子项目之一。目前而言，准备采用Java来进行开发，后期是否可以采用更加适合于消息中间件的语言开发？&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;设备消息解析&lt;/strong&gt;&lt;/p&gt;

    &lt;p&gt;设备的消息是完全遵循JT/T808标准的。jet节点需要按照JT/T808标准进行消息的解析。&lt;/p&gt;

    &lt;p&gt;对于一些信息需要保存到数据库中，比如终端注册信息，而有些消息只需要分发到消息中间件即可&lt;/p&gt;

    &lt;p&gt;周边：（1）数据库创建脚本&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;设备鉴权&lt;/strong&gt;&lt;/p&gt;

    &lt;p&gt;设备的信息（包括密码）源可配置，一般将设备信息源配置到MySQL、MongoDB等数据库。&lt;/p&gt;

    &lt;p&gt;周边：（1）设备信息录入脚本，实现从文档中读取设备信息，并存放到MySQL、MongoDB等数据库中。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;消息分发&lt;/strong&gt;&lt;/p&gt;

    &lt;p&gt;jet节点可以通过配置的方式将数据分发到Kafka、RabbitMQ等消息中间件。&lt;/p&gt;

    &lt;p&gt;所有消息都分发到一个Topic中，并根据一致性哈希算法来指定到某一个分区（对于Kafka而言，RabbitMQ目前还未使用过）。具体而言，一致性哈希算法是对消息中的手机号（JT/T808标准中的说法，也就是IMSI）&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;消息回复&lt;/strong&gt;&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;</content><author><name>songchaochao</name></author><category term="Proj" /><summary type="html">物流公司远程信息管理系统，实现远程车辆监控和控制。</summary></entry><entry><title type="html">霍乱时期的爱情？我不懂</title><link href="http://neyzoter.github.io/2020/06/06/Love-In-The-Time-Of-Cholera/" rel="alternate" type="text/html" title="霍乱时期的爱情？我不懂" /><published>2020-06-06T00:00:00+08:00</published><updated>2020-06-06T00:00:00+08:00</updated><id>http://neyzoter.github.io/2020/06/06/Love-In-The-Time-Of-Cholera</id><content type="html" xml:base="http://neyzoter.github.io/2020/06/06/Love-In-The-Time-Of-Cholera/">&lt;blockquote&gt;
  &lt;p&gt;弗洛伦蒂诺和费尔明娜谈的是什么爱情？&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;这一个月来一直在阅读&lt;a href=&quot;https://github.com/lemonade117&quot;&gt;Diary&lt;/a&gt;多年前送的《霍乱时期的爱情》。读书的时间，正如小说中的男主弗洛伦蒂诺和女主费尔明娜的爱情，断断续续，忽明忽暗。&lt;/p&gt;

&lt;p&gt;刚看到书名，还以为讲的是霍乱横行期间，男主女主如何地冲破病魔枷锁，如何地演绎轰轰烈烈的爱情。但事实并不是这样。&lt;/p&gt;

&lt;p&gt;故事开始之时，费尔明娜和弗洛伦蒂诺都是青葱少年，往来信件中都是满满的荷尔蒙。但出于家庭原因，费尔明娜嫁给了父亲心中的好丈夫。而后故事的情节走向了一个万万没想到的方向，弗洛伦蒂诺得不到费尔明娜，心中一直挂念，但是身体却又很诚实地游走在各色女性之间。&lt;/p&gt;

&lt;p&gt;在我看来，这不就是现代人所说的“渣男”吗？&lt;/p&gt;

&lt;p&gt;出乎意料的是，大家都是老太太和老爷爷时，费尔明娜丈夫去世，弗洛伦蒂诺竟然还惦记着她。小说硬生生将两个人给写到了一起，宛如几十年前，双方还是少年那样。故事中，费尔明娜和弗洛伦蒂诺都还是介意这段感情公布于众，也曾想方设法掩盖这段隐晦的黄昏恋。但是最终，双方还是勇敢地面对世人，让这段感情在大家的口中任意发酵。&lt;/p&gt;

&lt;p&gt;如果说这段爱情伟大在何处，我想应该是两者能够共同面对世人的冷眼，能够携手珍惜爱情时光。&lt;/p&gt;

&lt;p&gt;但是这段感情又让我觉得难以理解。为什么弗洛伦蒂诺能几十年一直惦记心中的情人？为什么弗洛伦蒂诺又让自己这样任意妄为地玩弄女人？为什么弗洛伦蒂诺还能在老年接受弗洛伦蒂诺？&lt;/p&gt;

&lt;p&gt;这些问题我一直没有想明白。如果要说原因，我只能想起陈奕迅的那句歌词——“得不到的永远在骚动”。&lt;/p&gt;

&lt;p&gt;费尔明娜这几十年来和丈夫在一起，有互相“厌倦”，又互相离不开彼此。有出轨的，有离家出走的，也有破镜重圆。费尔明娜“憎恨”对方，又在多年后觉得这段生活时期很美好。这不就是我们现在真实的生活吗？夫妻间总有磕磕绊绊，但是生活的美好也总是多于困苦。&lt;/p&gt;

&lt;p&gt;爱情是什么？在我看来，更像是费尔明娜和丈夫那样，相伴多年，彼此依存。有些人也许觉得炽热的爱情才是真爱，就像弗洛伦蒂诺对费尔明娜那样——几十年的惦念。也许我还年轻，也许我老了，也许我和弗洛伦蒂诺不是一类人，总之我无法认同，无法理解。&lt;/p&gt;

&lt;p&gt;—- 2020年6月6日   于求是园&lt;/p&gt;</content><author><name>songchaochao</name></author><category term="Life" /><summary type="html">弗洛伦蒂诺和费尔明娜谈的是什么爱情？</summary></entry><entry><title type="html">大型项目中的数据分层</title><link href="http://neyzoter.github.io/2020/05/15/Web-Layer/" rel="alternate" type="text/html" title="大型项目中的数据分层" /><published>2020-05-15T00:00:00+08:00</published><updated>2020-05-15T00:00:00+08:00</updated><id>http://neyzoter.github.io/2020/05/15/Web-Layer</id><content type="html" xml:base="http://neyzoter.github.io/2020/05/15/Web-Layer/">&lt;blockquote&gt;
  &lt;p&gt;PO、VO、DTO、DAO、BO等&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;假设我们有一个面试系统，数据库中存储了很多面试题，通过 WEB和 API 提供服务。可能会做如下的设计：&lt;/p&gt;

&lt;p&gt;数据表：表中的面试题包括编号、题目、选项、答案、创建时间、修改时间；&lt;/p&gt;

&lt;p&gt;PO：包括题目、选项、答案、创建时间、修改时间；&lt;/p&gt;

&lt;p&gt;VO：题目、选项、答案、上一题URL、下一题URL；&lt;/p&gt;

&lt;p&gt;DTO：编号、题目、选项、答案、上一题编号、下一题编号；&lt;/p&gt;

&lt;p&gt;DAO：数据库增删改查方法；&lt;/p&gt;

&lt;p&gt;BO：业务基本操作。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/posts/2020-05-15-Web-Layers/layers.jpg&quot; width=&quot;500&quot; alt=&quot;web分层&quot; /&gt;&lt;/p&gt;</content><author><name>songchaochao</name></author><category term="Backend" /><summary type="html">PO、VO、DTO、DAO、BO等</summary></entry><entry><title type="html">活着，是为了什么</title><link href="http://neyzoter.github.io/2020/05/06/For-Living-Yuhua/" rel="alternate" type="text/html" title="活着，是为了什么" /><published>2020-05-06T00:00:00+08:00</published><updated>2020-05-06T00:00:00+08:00</updated><id>http://neyzoter.github.io/2020/05/06/For-Living-Yuhua</id><content type="html" xml:base="http://neyzoter.github.io/2020/05/06/For-Living-Yuhua/">&lt;blockquote&gt;
  &lt;p&gt;《活着（余华）》读书笔记&lt;/p&gt;
&lt;/blockquote&gt;</content><author><name>songchaochao</name></author><category term="Life" /><summary type="html">《活着（余华）》读书笔记</summary></entry><entry><title type="html">利用Java反射机制实现从外部文件配置一个对象</title><link href="http://neyzoter.github.io/2020/03/12/Read-Properties-From-File-With-Java-Reflect/" rel="alternate" type="text/html" title="利用Java反射机制实现从外部文件配置一个对象" /><published>2020-03-12T00:00:00+08:00</published><updated>2020-03-12T00:00:00+08:00</updated><id>http://neyzoter.github.io/2020/03/12/Read-Properties-From-File-With-Java-Reflect</id><content type="html" xml:base="http://neyzoter.github.io/2020/03/12/Read-Properties-From-File-With-Java-Reflect/">&lt;blockquote&gt;
  &lt;p&gt;通过Java反射机制，实现从外部文件读取JSON属性，并配置对象的变量。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1 id=&quot;1-java反射及应用&quot;&gt;1. Java反射及应用&lt;/h1&gt;

&lt;p&gt;反射可以在运行时根据指定的类名、变量名、方法名获得对应的信息，可以实现较高可复用性。&lt;/p&gt;

&lt;p&gt;比如我现在有一个需求：我在Java中实现归一化操作，此时需要知道特征的最大最小值。而最大最小值是在用Python训练模型时确定的，我需要在文本中将最大最小值存储下来，Java可以随时读取。为了提高系统的可复用性，文本是properties文件，以json的格式来保存数据，Java读取数据后以Java反射的方式给对象赋值，比如&lt;/p&gt;

&lt;div class=&quot;language-properties highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;py&quot;&gt;max-value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;{val1:0.23,val2:1.34}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;其中&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;max-value&lt;/code&gt;指示配置最大值，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;{val1:0.23,val2:1.34}&lt;/code&gt;以json的方式来指定保存最大数据对象的变量。下面是一个保存特征的类，&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nd&quot;&gt;@Setter&lt;/span&gt;
&lt;span class=&quot;nd&quot;&gt;@Getter&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;RuntimeData&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Double&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;val1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;val2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;valFromStr&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;val&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;valName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;){&lt;/span&gt;
		&lt;span class=&quot;c1&quot;&gt;// 实现通过valName指定变量名，来给对应的变量赋值，&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 具体为val，val需要转化为对应的类型&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;那么，Java在读取properties文件中的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;max-value&lt;/code&gt;后，解析出变量名和其对应的数值（都是字符串格式），通过函数&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;valFromStr&lt;/code&gt;实现给对象赋值。&lt;/p&gt;

&lt;h1 id=&quot;2-具体实现&quot;&gt;2. 具体实现&lt;/h1&gt;

&lt;p&gt;具体实现主要是properties文件的读取、解析和&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RuntimeData&lt;/code&gt;的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;valFromStr&lt;/code&gt;方法。&lt;/p&gt;

&lt;p&gt;下面是&lt;a href=&quot;https://github.com/Neyzoter/aiot/blob/master/common/src/main/java/cn/neyzoter/aiot/common/util/PropertiesUtil.java&quot;&gt;properties管理类&lt;/a&gt;，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;getPropertiesMap&lt;/code&gt;函数实现从properties文件中解析出JSON格式的数据，并以Map保存并返回。&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cm&quot;&gt;/**
 * properties Util
 * @author neyzoter song
 * @date 2019-12-11
 */&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;PropertiesUtil&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Serializable&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;long&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;serialVersionUID&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3031705469789764720L&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;MAX_PROPERTIES_MAP_CAP&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;PROPERTIES_SEPERATOR&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;,&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;KEY_VAL_SEPERATOR&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;:&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;cm&quot;&gt;/**
     * properties
     */&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Properties&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;cm&quot;&gt;/**
     * file path
     */&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;cm&quot;&gt;/**
     * load properties file
     */&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PropertiesUtil&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;){&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;updateProps&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;cm&quot;&gt;/**
     * read property
     * @param key key
     * @return value
     */&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;readValue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;  &lt;span class=&quot;n&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getProperty&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;cm&quot;&gt;/**
     * 更新属性
     * @param path properties file
     */&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;updateProps&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;path&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;updateProps&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;

    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;cm&quot;&gt;/**
     * 获取属性
     * @return
     */&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;updateProps&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(){&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nc&quot;&gt;BufferedReader&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bufferedReader&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;BufferedReader&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;FileReader&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;));&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;props&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Properties&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;load&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bufferedReader&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;cm&quot;&gt;/**
     * 获取props
     * @return
     */&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Properties&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;getProps&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;cm&quot;&gt;/**
     * trans json format properties to map
     * @param properties json format properties &amp;lt;br/&amp;gt; such as {val1:0.12,val2:2.2}
     * @return properties Map
     */&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;getPropertiesMap&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;properties&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;map&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;HashMap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;MAX_PROPERTIES_MAP_CAP&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;properties&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;properties&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;trim&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// rm &quot;{&quot; and &quot;}&quot; and &quot; &quot;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;properties&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;properties&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;replaceAll&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;[\\{|\\}| ]&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// split&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;propertisArray&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;properties&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;PROPERTIES_SEPERATOR&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;kv&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;propertisArray&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;kvArray&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;kv&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;KEY_VAL_SEPERATOR&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;kvArray&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;],&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;kvArray&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]);&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;下面是&lt;a href=&quot;https://github.com/Neyzoter/aiot/blob/master/dal/src/main/java/cn/neyzoter/aiot/dal/domain/vehicle/RuntimeData.java&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RuntimeData&lt;/code&gt;的简单实现&lt;/a&gt;，重点是&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;valFromStr&lt;/code&gt;函数，&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nd&quot;&gt;@Setter&lt;/span&gt;
&lt;span class=&quot;nd&quot;&gt;@Getter&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;RuntimeData&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Double&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;val1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;val2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;valFromStr&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;val&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;valName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;){&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 获取变量valName（valName可以是&quot;val1&quot;或者&quot;val2&quot;）的Field&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;Field&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;field&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getClass&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getDeclaredField&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;valName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 查看field是否可到达&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;isAccessible&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;isAccessible&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// 设置field为可到达&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;setAccessible&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// 将String类型转化为对应类型&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// field.getType()表示获取变量的类型，比如本类中val1是Double，val2是Float&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// isAssignableFrom表示判断是否是某一个类型&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getType&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;isAssignableFrom&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Double&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Double&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;parseDouble&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;val&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;));&lt;/span&gt;
            &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getType&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;isAssignableFrom&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Float&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Float&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;parseFloat&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;val&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;));&lt;/span&gt;
            &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getType&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;isAssignableFrom&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Integer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Integer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;parseInt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;val&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;));&lt;/span&gt;
            &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getType&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;isAssignableFrom&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Long&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Long&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;parseLong&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;val&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;));&lt;/span&gt;
            &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getType&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;isAssignableFrom&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;val&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;finally&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// 将该field设置为原来的状态&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;setAccessible&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;isAccessible&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;具体过程包括properties文件的读取、解析，返回Map后，使用Map遍历各个变量的名称和数值（都是String类型），通过&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;valFromStr&lt;/code&gt;函数赋值。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;需要注意的是，Java反射效率并不高，所以尽可能在初始化时就确定配置值，不要反复更新。否则，会严重影响系统性能。&lt;/strong&gt;&lt;/p&gt;</content><author><name>songchaochao</name></author><category term="Java" /><summary type="html">通过Java反射机制，实现从外部文件读取JSON属性，并配置对象的变量。</summary></entry><entry><title type="html">AUTOSAR的MemMap介绍</title><link href="http://neyzoter.github.io/2020/03/09/AUTOSAR-MemMap-Intro/" rel="alternate" type="text/html" title="AUTOSAR的MemMap介绍" /><published>2020-03-09T00:00:00+08:00</published><updated>2020-03-09T00:00:00+08:00</updated><id>http://neyzoter.github.io/2020/03/09/AUTOSAR-MemMap-Intro</id><content type="html" xml:base="http://neyzoter.github.io/2020/03/09/AUTOSAR-MemMap-Intro/">&lt;blockquote&gt;
  &lt;p&gt;AUTOSAR框架下的MemMap介绍&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1 id=&quot;1memmap介绍&quot;&gt;1.MemMap介绍&lt;/h1&gt;

&lt;h2 id=&quot;21-memmap的作用&quot;&gt;2.1 MemMap的作用&lt;/h2&gt;

&lt;p&gt;对于许多ECU而言，&lt;strong&gt;内存映射（MemMap）&lt;/strong&gt;是非常重要的，具体包括以下优点：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;避免RAM的浪费&lt;/p&gt;

    &lt;p&gt;在同一个模块下定义了不同类型的变量（比如8bit、16bit、32bit），则为了对齐，会在不同类型变量间留间隙。比如8bit变量和32bit变量存放在一起，在两个变量都要对齐到32bit。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;特殊RAM用途&lt;/p&gt;

    &lt;p&gt;比如一些变量通过位掩码来获取，如果map到RAM可以通过编译器的位掩码功能来加快执行效率&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;特殊ROM用途&lt;/p&gt;

    &lt;p&gt;一些频繁执行的函数可以映射到RAM中，而不经常执行的函数可以映射到外部ROM。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;同一段代码用在bootloader和应用&lt;/p&gt;

    &lt;p&gt;如果同一个模块同时需要被bootloader和应用使用，则需要同时映射到不同的段中&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;支持内存保护&lt;/p&gt;

    &lt;p&gt;使用硬件存储器保护需要将模块变量分离到不同的存储器区域。内部变量映射到受保护的内存中，用于数据交换的缓冲区映射到不受保护的内存中。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;支持分区&lt;/p&gt;

    &lt;p&gt;可以将一个模块的变量映射到不同的分区中&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h1 id=&quot;2memmap使用&quot;&gt;2.MemMap使用&lt;/h1&gt;

&lt;h2 id=&quot;21-memmaph文件配置&quot;&gt;2.1 MemMap.h文件配置&lt;/h2&gt;

&lt;p&gt;在模块使用MemMap之前，需要对应配置MemMap.h文件。可以为每个模块或者每个SWC都配置一个MemMap.h文件。&lt;/p&gt;

&lt;p&gt;比如我们为SWC1配置一个&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Swc1_MemMap.h&lt;/code&gt;。&lt;/p&gt;

&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cm&quot;&gt;/*段开始*/&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;#define MEMMAP_ERROR
&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;// ....&lt;/span&gt;
&lt;span class=&quot;cm&quot;&gt;/* COMMON_CODE代码段开始 */&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;#if defined SWC1_START_SECTION_COMMON_CODE
#pragma section ftext
#undef SWC1_START_SECTION_COMMON_CODE
#undef MEMMAP_ERROR
&lt;/span&gt;&lt;span class=&quot;cm&quot;&gt;/* UNBANKED_CODE代码段开始 */&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;#elif defined SWC1_START_SECTION_UNBANKED_CODE
#pragma section code text
#undef SWC1_START_SECTION_UNBANKED_CODE
#undef MEMMAP_ERROR
&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;// ....&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;#endif
&lt;/span&gt;
&lt;span class=&quot;cm&quot;&gt;/*段结束*/&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// ....&lt;/span&gt;
&lt;span class=&quot;cm&quot;&gt;/* module and ECU specific section mappings */&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;#if defined SWC1_STOP_SECTION_COMMON_CODE
#pragma section ftext
#undef SWC1_STOP_SECTION_COMMON_CODE
#undef MEMMAP_ERROR
#elif defined SWC2_STOP_SECTION_UNBANKED_CODE
#pragma section code text
#undef SWC2_STOP_SECTION_UNBANKED_CODE
#undef MEMMAP_ERROR
&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;// ....&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;#endif
&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;22-在模块中使用memmap&quot;&gt;2.2 在模块中使用MemMap&lt;/h2&gt;

&lt;p&gt;下面是一个使用MemMap的范式，&lt;/p&gt;

&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// 定义对应的段名称，在上述MemMap.h文件中会使用#if define来找到对应的段信息&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// &amp;lt;PREFIX&amp;gt;一般可以是SWC名称&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// &amp;lt;MEMORY_ALLOCATION_KEYWORDS&amp;gt;表示对应的代码段名册和那个&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;#define &amp;lt;PREFIX&amp;gt;_START_SEC_&amp;lt;MEMORY_ALLOCATION_KEYWORDS&amp;gt;
&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;// 引用MemMap.h，进而找到对应的段&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;#include &amp;lt;MemMap.h&amp;gt;
&lt;/span&gt;&lt;span class=&quot;cm&quot;&gt;/* 此处定义代码、变量、常数等 */&lt;/span&gt;
&lt;span class=&quot;cm&quot;&gt;/* … */&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;#define &amp;lt;PREFIX&amp;gt;_STOP_SEC_&amp;lt;MEMORY_ALLOCATION_KEYWORDS&amp;gt;
#include &amp;lt;MemMap.h&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;比如我们要在&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Rte_SWC1.c&lt;/code&gt;文件中创建一个函数，则将该代码放到对应的代码段中。&lt;/p&gt;

&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;#define SWC1_START_SECTION_COMMON_CODE
#include &quot;Swc1_MemMap.h&quot;
&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//....&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;#define SWC1_STOP_SECTION_COMMON_CODE
#include &quot;Swc1_MemMap.h&quot;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;具体作用过程如下，&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Rte_SWC1.c文件
1. #define SWC1_START_SECTION_COMMON_CODE -&amp;gt; 定义了 SWC1_START_SECTION_COMMON_CODE
2. #include &quot;Swc1_MemMap.h&quot; -&amp;gt; 调用 Swc1_MemMap.h

Swc1_MemMap.h文件
1. #define MEMMAP_ERROR -&amp;gt; 定义MEMMAP_ERROR
2. #if defined SWC1_START_SECTION_COMMON_CODE -&amp;gt; 发现定义了SWC1_START_SECTION_COMMON_CODE
3. #pragma section ftext -&amp;gt; 设置存储到对应的段
4. #undef SWC1_START_SECTION_COMMON_CODE -&amp;gt; 取消SWC1_START_SECTION_COMMON_CODE定义
5. #undef MEMMAP_ERROR -&amp;gt; 取消MEMMAP_ERROR定义，在本文件中如果发现MEMMAP_ERROR定义则会报错

然后回到Rte_SWC1.c文件
1. 函数定义
void run(void) {
    //....
}
2. #define SWC1_STOP_SECTION_COMMON_CODE -&amp;gt; 定义了 SWC1_STOP_SECTION_COMMON_CODE
3. #include &quot;Swc1_MemMap.h&quot; -&amp;gt; 调用 Swc1_MemMap.h

Swc1_MemMap.h
1. #define MEMMAP_ERROR -&amp;gt; 定义MEMMAP_ERROR
2. #if defined SWC1_STOP_SECTION_COMMON_CODE -&amp;gt; 发现定义了SWC1_STOP_SECTION_COMMON_CODE
3. #pragma section ftext -&amp;gt; 设置存储到对应的段
4. #undef SWC1_STOP_SECTION_COMMON_CODE -&amp;gt; 取消SWC1_STOP_SECTION_COMMON_CODE定义
5. #undef MEMMAP_ERROR -&amp;gt; 取消MEMMAP_ERROR定义，在本文件中如果发现MEMMAP_ERROR定义则会报错
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;</content><author><name>songchaochao</name></author><category term="AUTOSAR" /><summary type="html">AUTOSAR框架下的MemMap介绍</summary></entry></feed>