<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>FinTx</title>
  
  <subtitle>Enable the finance</subtitle>
  <link href="/atom.xml" rel="self"/>
  
  <link href="http://www.fintx.org/"/>
  <updated>2018-05-19T17:05:50.443Z</updated>
  <id>http://www.fintx.org/</id>
  
  <author>
    <name>fintxorg</name>
    
  </author>
  
  <generator uri="http://hexo.io/">Hexo</generator>
  
  <entry>
    <title>计息方法及金融计算总结</title>
    <link href="http://www.fintx.org/20180512-interest-accrual-and-financial-calculation-summary.html"/>
    <id>http://www.fintx.org/20180512-interest-accrual-and-financial-calculation-summary.html</id>
    <published>2018-05-12T14:47:52.000Z</published>
    <updated>2018-05-19T17:05:50.443Z</updated>
    
    <content type="html"><![CDATA[<div class="row">    <embed src="./uploads/posts/interest-accrual-and-financial-calculation-summary/计息方法及金融计算总结.pdf" width="100%" height="550" type="application/pdf"></div><p>欢迎在GitHub上关注FinTx的开源账务系统：<a href="https://github.com/fintx/fintx-accounting" target="_blank" rel="noopener">fintx-accounting</a></p>]]></content>
    
    <summary type="html">
    
      计息方法和金融计算注意事项的总结
    
    </summary>
    
      <category term="Business" scheme="http://www.fintx.org/categories/Business/"/>
    
    
      <category term="accrual" scheme="http://www.fintx.org/tags/accrual/"/>
    
      <category term="calculation" scheme="http://www.fintx.org/tags/calculation/"/>
    
  </entry>
  
  <entry>
    <title>银行核心系统及研发转型</title>
    <link href="http://www.fintx.org/20180430-core-banking-system-and-it-development-transition.html"/>
    <id>http://www.fintx.org/20180430-core-banking-system-and-it-development-transition.html</id>
    <published>2018-04-30T15:47:52.000Z</published>
    <updated>2018-05-13T16:32:50.784Z</updated>
    
    <content type="html"><![CDATA[<div class="row">    <embed src="./uploads/posts/core-banking-system-and-it-development-transition/银行核心系统及研发转型.pdf" width="100%" height="550" type="application/pdf"></div><p>欢迎在GitHub上关注FinTx的开源账务系统：<a href="https://github.com/fintx/fintx-accounting" target="_blank" rel="noopener">fintx-accounting</a></p>]]></content>
    
    <summary type="html">
    
      银行核心业务系统是银行处理最核心的存款，贷款，结算业务，中间业务的IT系统。
    
    </summary>
    
      <category term="Thinking" scheme="http://www.fintx.org/categories/Thinking/"/>
    
    
      <category term="banking" scheme="http://www.fintx.org/tags/banking/"/>
    
      <category term="technology" scheme="http://www.fintx.org/tags/technology/"/>
    
  </entry>
  
  <entry>
    <title>金融核心系统设计原则</title>
    <link href="http://www.fintx.org/20170827-finacial-system-design-principle.html"/>
    <id>http://www.fintx.org/20170827-finacial-system-design-principle.html</id>
    <published>2017-08-27T02:13:20.000Z</published>
    <updated>2018-05-27T15:12:51.462Z</updated>
    
    <content type="html"><![CDATA[<h3 id="1-正确"><a href="#1-正确" class="headerlink" title="1 正确"></a>1 正确</h3><p>正确时金融系统设计的第一原则，不能保证正确其他原则都无从谈起。</p><h3 id="2-安全"><a href="#2-安全" class="headerlink" title="2 安全"></a>2 安全</h3><p>对金融数据做到防篡改，防解析，防获取。默认不信任任何外来请求，数据…没有用户会愿意使用一个不安全的金融系统。</p><h3 id="3-合规"><a href="#3-合规" class="headerlink" title="3 合规"></a>3 合规</h3><p>在国内的大环境下，对于有一定规模或有一定追求的金融公司，监管是不得不面对的问题。尤其是对互联网金融企业，针对一些国家，地方的互联网监管规范要求，如《互联网金融统计制度》和其他一些报送要求。</p><h3 id="4-可追溯"><a href="#4-可追溯" class="headerlink" title="4 可追溯"></a>4 可追溯</h3><p>对所有的登陆，退出，请求，响应，业务处理，状态变更都要记录历史信息。方便风控，审计，追溯，定位。</p><h3 id="4-一致"><a href="#4-一致" class="headerlink" title="4 一致"></a>4 一致</h3><p>领域（服务）内强一致性与领域（服务）间最终一致性相结合使用。系统间完善对账机制与业务监控。</p><h3 id="6-补偿"><a href="#6-补偿" class="headerlink" title="6 补偿"></a>6 补偿</h3><p>无论服务内还是服务间，带超时的同步接口，查询/异步通知接口，撤销接口，配合定时补偿机制，结合使用。</p><h3 id="7-幂等"><a href="#7-幂等" class="headerlink" title="7 幂等"></a>7 幂等</h3><p>保证服务调用的幂等性，业务处理的幂等性，不能保证幂等的地方需要请求去重，反馈去重，业务去重，对每一次变化，才能保证结果正确。</p><h3 id="5-识别"><a href="#5-识别" class="headerlink" title="5 识别"></a>5 识别</h3><p>对每一次引起变化的事件，都要可识别，request要有RequestId，responseId，业务要有businessId…对有状态的业务要识别尽可能多的状态（有人提到MECE分析法，深以为然），并记录详细的状态迁移信息。作到一切事件，状态皆识别。</p><h3 id="9-内外控"><a href="#9-内外控" class="headerlink" title="9 内外控"></a>9 内外控</h3><p>风控针对所有用户。不仅包括所有客户，也包括所有内部用户。</p><p>欢迎在GitHub上关注FinTx开源金融系统：<a href="https://github.com/fintx" target="_blank" rel="noopener">FinTx</a></p>]]></content>
    
    <summary type="html">
    
      准确，安全，合规，可追溯，一致，补偿，幂等，识别，内外控
    
    </summary>
    
      <category term="Design" scheme="http://www.fintx.org/categories/Design/"/>
    
    
      <category term="financial system" scheme="http://www.fintx.org/tags/financial-system/"/>
    
  </entry>
  
  <entry>
    <title>谈谈ID生成方案</title>
    <link href="http://www.fintx.org/20170815-id-generation.html"/>
    <id>http://www.fintx.org/20170815-id-generation.html</id>
    <published>2017-08-15T11:11:23.000Z</published>
    <updated>2018-05-27T15:12:10.753Z</updated>
    
    <content type="html"><![CDATA[<h3 id="ID生成一般要符合如下几个原则"><a href="#ID生成一般要符合如下几个原则" class="headerlink" title="ID生成一般要符合如下几个原则"></a>ID生成一般要符合如下几个原则</h3><ul><li>1 全局唯一<br>系统设计中对ID的唯一性需求非常普遍。尤其在分布式系统中，要辨别每一次请求是否重复，每一笔业务是否重复，都要求有唯一的ID来识别。对待系统问题查找，审计等有唯一的ID都可以大大简化工作难度和工作强度。</li><li>2 分布式：<br>能在本地分布式生成，不用经过网络获取。没有中心单点多点，不依赖于第三方，无限制扩展。这样才能保证ID最大的可靠性和便利性。</li><li>3 有序递增<br>ID有序对数据检索极为重要，现代数据库一般都采用B+树索引进行数据检索，有序的ID可以极大提升数据库的效率。递增则可满足排序需求。</li><li>4 时间相关<br>ID与时间相关不仅能保证有序递增的，也便于冷热数据的分离。时间相关的ID生成都会面临时间回调问题，要注意处理（保留上次的时间戳，和本次的时间戳比较）</li><li>5 长度短<br>ID长度越短其读写效率越高占用空间小，并且ID都会作为索引列，ID越短期生成的索引占用空间也越小。不考虑其他情况的前提下64bit的long型是最佳选择。</li><li>6 高性能<br>可能的条件下性能尽可能高，以满足大规模超大规模交易需求。单点每秒百万以上的性能才能称为高性能。</li><li>7 有内涵<br>ID的每一部分都有含义，比如包含时间 机器甚至业务信息，便于跟踪定位。尤其在分布式系统下，对线上问题查找原因有极大的帮助。</li></ul><h3 id="目前有如下一些主流的ID生成方案"><a href="#目前有如下一些主流的ID生成方案" class="headerlink" title="目前有如下一些主流的ID生成方案"></a>目前有如下一些主流的ID生成方案</h3><h4 id="1-MySQL自增主键-autoincreasement-id"><a href="#1-MySQL自增主键-autoincreasement-id" class="headerlink" title="1 MySQL自增主键  autoincreasement id"></a>1 MySQL自增主键  autoincreasement id</h4><p>在MySQL中单表自增主键是最高效的数据库索引，通过单库顺序自增或者多库设定不同的offset和increament来保证ID唯一性。但要在数据存入之后再从数据库取回，不便且面临网络可靠性的风险。默认性能不高单库很难突破千/s级别，多库情况下扩展困难。<br>优缺点：</p><ul><li>[X] 全局唯一</li><li>[] 分布式</li><li>[X] 有序递增（仅单库）</li><li>[] 时间相关</li><li>[X] 长度短</li><li>[] 高性能</li><li>[] 有内涵</li></ul><h4 id="2-数据库Sequence"><a href="#2-数据库Sequence" class="headerlink" title="2 数据库Sequence"></a>2 数据库Sequence</h4><p>MySQL并不像DB2 oracle有现成的Sequence特性。它是通过数据表+数据库函数方式实现的Sequence。同样也可以通过设定“current_value”和“increment”的方式多库生成。具体方法网上很多但是大部分都是有问题的，并发条件下会有重复ID。这里提供一个完整的方案 <a href="./uploads/posts/id-generation/t_sequence.sql">t_sequence.sql</a>。默认性能不高也是很难突破千/s级别，多库扩展困难。但是可以使用获得的ID做基础，在本地附加本地顺序号进行二次分配提高性能。如获取1则在本地可分配[1000,1999]以此提高性能，实现会麻烦一些。<br>优缺点：</p><ul><li>[X] 全局唯一</li><li>[] 分布式</li><li>[X] 有序递增（仅单库）</li><li>[] 时间相关</li><li>[X] 长度短</li><li>[] 高性能</li><li>[] 有内涵</li></ul><h4 id="3-Redis"><a href="#3-Redis" class="headerlink" title="3 Redis"></a>3 Redis</h4><p>利用Redis单线程模型，获取自增ID。与数据库Sequence类似，也可通过使用不同初始offset和increment来扩展。不过引入了对Redis的依赖。默认但节点性能万/s-十万/s，同样可进行本地二次分配。<br>优缺点：</p><ul><li>[X] 全局唯一</li><li>[] 分布式</li><li>[X] 有序递增（仅单库）</li><li>[] 时间相关</li><li>[X] 长度短</li><li>[] 高性能</li><li>[] 有内涵</li></ul><h4 id="4-UUID"><a href="#4-UUID" class="headerlink" title="4 UUID"></a>4 UUID</h4><p>UUID有不同的版本，常见version 1 和 version 4。参见 <a href="http://www.ietf.org/rfc/rfc4122.txt" target="_blank" rel="noopener">RFC4122</a>。UUID长度32字符。<br>UUID version 1 是基于时间 机器地址 本地序列号构造。<br>UUID version 4 是基于安全随机数算法构造的，是jdk默认的UUID实现。<br>优缺点：</p><ul><li>[X] 全局唯一</li><li>[X] 分布式</li><li>[X] 有序递增（仅version 1，单节点）</li><li>[X] 时间相关（仅version 1）</li><li>[] 长度短</li><li>[X] 高性能</li><li>[] 有内涵（仅version 1）</li></ul><h4 id="5-snowflake"><a href="#5-snowflake" class="headerlink" title="5 snowflake"></a>5 snowflake</h4><p>64bit的long型分布式ID生成。ID中包含时间戳和自增序列。但是依赖于中心分配DataCenterID 和 Worker ID<br>优缺点：</p><ul><li>[X] 全局唯一</li><li>[] 分布式</li><li>[X] 有序递增（单节点）</li><li>[X] 时间相关</li><li>[X] 长度短</li><li>[] 高性能</li><li>[X] 有内涵（仅时间戳有意义）</li></ul><h4 id="6-ObjectId"><a href="#6-ObjectId" class="headerlink" title="6 ObjectId"></a>6 ObjectId</h4><p>MongoDB使用12byte数据结构，16进制编码24个字符。结构上与UUID version 1类似，但是做了一定的改进。</p><table border="1"><br><caption>ObjectID layout</caption><br><tr><br><td>0</td><td>1</td><td>2</td><td>3</td><td>4</td><td>5</td><td>6</td><td>7</td><td>8</td><td>9</td><td>10</td><td>11</td><br></tr><br><tr><br><td colspan="4">time</td><td colspan="3">machine</td> <td colspan="2">pid</td><td colspan="3">inc</td><br></tr><br></table><p>虽然由于inc采用随机数初始值大大降低了重复的可能性，但是由于machine采用hash值，理论上还是有可能产生重复ID。可能是考虑到inc随机数初始值的前提下可能性不大，且inc递增使时间回调有一定容忍性（回调n秒时峰值不超过2^24/(n+1)的前提下依能保证唯一），ObjectID也没有考虑时间回调的问题。一切系统放大到一定规模的时候，不太可能出现的问题就一定会出现。<br>优缺点：</p><ul><li>[] 全局唯一</li><li>[X] 分布式</li><li>[X] 有序递增（单节点）</li><li>[X] 时间相关</li><li>[] 长度短</li><li>[X] 高性能</li><li>[X] 有内涵</li></ul><h3 id="FinTx的分布式唯一ID生成方案"><a href="#FinTx的分布式唯一ID生成方案" class="headerlink" title="FinTx的分布式唯一ID生成方案"></a>FinTx的分布式唯一ID生成方案</h3><p>fintx-identifer采用了类似ObjectId的方案，使用15byte数据结构，将machine扩展为48bit使网卡MAC地址作为机器唯一标识保证绝对唯一。处理了时间回调问题。并且采用64进制（Base64）编码字符串，将长度控制在20个字符。由于Base64的字符在UTF-8编码下只占1byte。UTF-8编码的20字符（20byte）唯一ID比UUID的32字符(32byte)减少37.5%。</p><table border="1"><br><caption>UniqueId layout</caption><br><tr><br><td>0</td><td>1</td><td>2</td><td>3</td><td>4</td><td>5</td><td>6</td><td>7</td><td>8</td><td>9</td><td>10</td><td>11</td><td>12</td><td>13</td><td>14</td><br></tr><br><tr><br><td colspan="4">time</td><td colspan="6">machine</td><td colspan="2">pid</td><td colspan="3">counter</td><br></tr><br></table><p>优缺点：    </p><ul><li>[X] 全局唯一（全球唯一）</li><li>[X] 分布式</li><li>[X] 有序递增（单节点）</li><li>[X] 时间相关</li><li>[] 长度短</li><li>[X] 高性能</li><li>[X] 有内涵</li></ul><p>欢迎在GitHub上关注FinTx开源的唯一ID生成方案：<a href="https://github.com/fintx/fintx-identifer" target="_blank" rel="noopener">fintx-identifer</a></p>]]></content>
    
    <summary type="html">
    
      分布式唯一ID是金融系统绕不开的问题
    
    </summary>
    
      <category term="Design" scheme="http://www.fintx.org/categories/Design/"/>
    
    
      <category term="id" scheme="http://www.fintx.org/tags/id/"/>
    
      <category term="design" scheme="http://www.fintx.org/tags/design/"/>
    
  </entry>
  
  <entry>
    <title>互联网金融整体业务架构</title>
    <link href="http://www.fintx.org/20170804-online-financial-business-architecture.html"/>
    <id>http://www.fintx.org/20170804-online-financial-business-architecture.html</id>
    <published>2017-08-04T13:25:50.000Z</published>
    <updated>2018-05-13T13:36:45.385Z</updated>
    
    <content type="html"><![CDATA[<p><img src="./uploads/posts/online-financial-business-architecture/online-financial-business-architecture.png" alt="互联网金融整体业务架构"></p><p>欢迎在GitHub上关注FinTx开源金融系统：<a href="https://github.com/fintx" target="_blank" rel="noopener">FinTx</a></p>]]></content>
    
    <summary type="html">
    
      互联网金融公司的完整业务体系
    
    </summary>
    
      <category term="Architecture" scheme="http://www.fintx.org/categories/Architecture/"/>
    
    
      <category term="architecture" scheme="http://www.fintx.org/tags/architecture/"/>
    
      <category term="business" scheme="http://www.fintx.org/tags/business/"/>
    
  </entry>
  
  <entry>
    <title>账务核心系统介绍</title>
    <link href="http://www.fintx.org/20170730-introduction-of-accounting-system.html"/>
    <id>http://www.fintx.org/20170730-introduction-of-accounting-system.html</id>
    <published>2017-07-30T15:47:52.000Z</published>
    <updated>2018-05-19T17:06:16.033Z</updated>
    
    <content type="html"><![CDATA[<div class="row">    <embed src="./uploads/posts/introduction-of-accounting-system/账务核心系统介绍.pdf" width="100%" height="550" type="application/pdf"></div><p>欢迎在GitHub上关注FinTx的开源账务系统：<a href="https://github.com/fintx/fintx-accounting" target="_blank" rel="noopener">fintx-accounting</a></p>]]></content>
    
    <summary type="html">
    
      账务系统是金融公司核心的核心
    
    </summary>
    
      <category term="Business" scheme="http://www.fintx.org/categories/Business/"/>
    
    
      <category term="accounting" scheme="http://www.fintx.org/tags/accounting/"/>
    
  </entry>
  
  <entry>
    <title>无日切，动账更新的7X24小时的账务系统设计</title>
    <link href="http://www.fintx.org/20170725-7x24-continuous-accounting-system.html"/>
    <id>http://www.fintx.org/20170725-7x24-continuous-accounting-system.html</id>
    <published>2017-07-25T11:11:13.000Z</published>
    <updated>2018-05-27T15:11:04.475Z</updated>
    
    <content type="html"><![CDATA[<p>7X24服务，国外常称为24X7，是不间断服务的别称。由于金融系统的特殊性，经常需要对某日切（日期切换的简称）点的数据进行批量处理或者分析，而客户的金融需求随时都会有，业务时时刻刻都在发生，所以7X24服务对金融企业尤其重要。7X24的概念最初由国外软件服务商介绍引入，国内银行业基本全面实现7*24服务。但在国内互联网金融领域仍未普及。<br>实现不间断的金融服务，基本思想就是一个字段不可能承载两份数据，所以要用多个字段去存储数据，一份存储实时数据，一份存日切点数据。7X24服务基本有两套主流方式——双余额法（单表双余额法）和影子账户法（双表单余额法）。比较这两种方式，双余额法逻辑稍复杂，但是操作简单，对业务的影响及资源占用低。影子账户法虽逻辑简单一些但是，需要追账等复杂操作，且在原占用多，对业务也有影响（需要追账的业务过多会影响日夜模式切换，部分业务受限）。综合比较之下双余额法是目前实现7X24服务的最优方法。<br>双余额法的一般原理是账户（双余额法不是专用与账务系统，这里账户不专指分户账，有需求的业务账户也可以使用）中设置余额，上期余额，最终交易日期三个字段。</p><table><thead><tr><th>账号</th><th>上期余额</th><th>余额</th><th>最终交易日期</th></tr></thead><tbody><tr><td> account_no</td><td>last_balance</td><td>balance</td><td>latest_trans_date</td></tr></tbody></table><p> 在获取账户余额时，比较最终更新日期与当前日期，当前日期&gt;=更新日期，则取余额，否则取上期余额。<br>在更新账户余额时，比较最终更新日期与当前日期。当前日期&lt;更新日期（后详），则更新上期余额和余额。当前日期=更新日期，则更新余额。当前日期&gt;更新日期,则设置上期余额=余额再更新余额，最后设置更新日期=当前日期。<br>由上可见，在双余额的条件下不太关心系统什么时候日切，只要按照交易系统传入的当前日期与更新日期做比较即可。<br>对更新余额的情况分两种–动账更新和批量更新。<br>动账更新时在每个账户需要进行账务处理时才更新账户余额，批量更新是每天日切之后对交易日期为昨日的账户统一更余额。动账更新的方式对系统资源占用更少但是对依赖于余额数据的批量来说会相对复杂，批量新会做大量无意义的处理，并且在日切后处理也无法完全保证账户持续可用（更新时要锁定虽然时间可能很短），但是对有依赖于余额数据的其他批量来说会更简单。FinTx开源的账务系统中选择了动账更新的方式，并且对余额获取提供方式提供了封装好的函数供全局调用。<br>当然账务系统由于其特定的领域特征，7X24的实现更复杂一些，但是原理相同。</p><table><thead><tr><th>账号</th><th>上期余额</th><th>上期借发生额</th><th>上期贷发生额</th></tr></thead><tbody><tr><td> account_no</td><td>last_balance</td><td>last_dr_trans_amt</td><td>last_cr_trans_amt</td></tr></tbody></table><table><thead><tr><th>余额</th><th>借发生额</th><th>贷发生额</th><th>最终交易日期</th></tr></thead><tbody><tr><td> balance</td><td>dr_trans_amt</td><td>cr_trans_amt</td><td>latest_trans_date</td></tr></tbody></table><p>这里也是一个简化模型引入了借贷发生额有时间再详细写，完整实现请先参考FinTx的开源账务系统。<a href="https://github.com/fintx/fintx-accounting" target="_blank" rel="noopener">fintx-accounting</a></p><p>补充一份关于7X24服务实现的文档<a href="./uploads/posts/7x24-continuous-accounting-system/银行核心系统7x24方案.pdf">银行核心系统7x24方案</a>参考，来源于网络，如有侵权请联系。<br>欢迎在GitHub上关注FinTx的开源账务系统：<a href="https://github.com/fintx/fintx-accounting" target="_blank" rel="noopener">fintx-accounting</a></p>]]></content>
    
    <summary type="html">
    
      7X24服务是金融系统应具备的基本能力
    
    </summary>
    
      <category term="Design" scheme="http://www.fintx.org/categories/Design/"/>
    
    
      <category term="accounting" scheme="http://www.fintx.org/tags/accounting/"/>
    
  </entry>
  
  <entry>
    <title>MySQL线上死锁问题分析一例</title>
    <link href="http://www.fintx.org/20170723-deadlock-analysis-of-mysql-1.html"/>
    <id>http://www.fintx.org/20170723-deadlock-analysis-of-mysql-1.html</id>
    <published>2017-07-23T08:18:04.000Z</published>
    <updated>2018-05-27T15:11:35.019Z</updated>
    
    <content type="html"><![CDATA[<h3 id="1-问题描述"><a href="#1-问题描述" class="headerlink" title="1 问题描述"></a>1 问题描述</h3><h4 id="0-表结构"><a href="#0-表结构" class="headerlink" title="0)表结构:"></a>0)表结构:</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">CREATE TABLE `t_acctsn_custno_pkid` (</span><br><span class="line">  `CustNo` char(20) COLLATE utf8_bin NOT NULL,</span><br><span class="line">  `CustType` char(2) COLLATE utf8_bin NOT NULL,</span><br><span class="line">  `AcctSN` char(8) COLLATE utf8_bin NOT NULL,</span><br><span class="line">  `id` bigint(20) NOT NULL AUTO_INCREMENT,</span><br><span class="line">  PRIMARY KEY (`id`)</span><br><span class="line">) ENGINE=InnoDB;</span><br></pre></td></tr></table></figure><h4 id="1-表数据"><a href="#1-表数据" class="headerlink" title="1)表数据"></a>1)表数据</h4><table><thead><tr><th>CustNo</th><th>CustType</th><th>AcctSN</th><th>id</th></tr></thead><tbody><tr><td>0000001</td><td>00</td><td>0</td><td>1</td></tr><tr><td>0000002</td><td>00</td><td>0</td><td>2</td></tr></tbody></table><h4 id="2-问题程序示意"><a href="#2-问题程序示意" class="headerlink" title="2)问题程序示意"></a>2)问题程序示意</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">int</span> concurrency = <span class="number">5</span>;</span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">int</span> c = <span class="number">0</span>; c &lt; concurrency; c++) &#123;</span><br><span class="line"><span class="keyword">new</span> Thread(<span class="keyword">new</span> Runnable() &#123;</span><br><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line"><span class="keyword">while</span> (<span class="keyword">true</span>) &#123;</span><br><span class="line">Connection conn = <span class="keyword">null</span>;</span><br><span class="line"><span class="keyword">try</span> &#123;</span><br><span class="line">conn = DBUtil.getTxnConnection();</span><br><span class="line">PreparedStatement ps = DBUtil.getPreparedStatement(conn,</span><br><span class="line"><span class="string">"select acctSN+1 from t_acctsn_custno_pkid where custNo = ? for update"</span>);</span><br><span class="line">ps.setString(<span class="number">1</span>, <span class="string">"0000001"</span>);</span><br><span class="line">ResultSet rs = ps.executeQuery();</span><br><span class="line">DBUtil.closePreparedStatement(ps);</span><br><span class="line"></span><br><span class="line">ps = DBUtil.getPreparedStatement(conn,</span><br><span class="line"><span class="string">"update  t_acctsn_custno_pkid  set acctSN=?  where custNo = ? "</span>);</span><br><span class="line">ps.setString(<span class="number">1</span>, acctSN);</span><br><span class="line">ps.setString(<span class="number">2</span>, <span class="string">"0000001"</span>);</span><br><span class="line">ps.executeUpdate();</span><br><span class="line">DBUtil.closePreparedStatement(ps);</span><br><span class="line"></span><br><span class="line">DBUtil.closeTxnConnection(conn, <span class="keyword">true</span>);</span><br><span class="line">&#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">e.printStackTrace();</span><br><span class="line"><span class="keyword">if</span> (<span class="keyword">null</span> != conn) &#123;</span><br><span class="line">DBUtil.closeTxnConnection(conn, <span class="keyword">false</span>);</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">&#125;).start();</span><br><span class="line"></span><br><span class="line"><span class="keyword">new</span> Thread(<span class="keyword">new</span> Runnable() &#123;</span><br><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line"><span class="keyword">while</span> (<span class="keyword">true</span>) &#123;</span><br><span class="line">Connection conn = <span class="keyword">null</span>;</span><br><span class="line"><span class="keyword">try</span> &#123;</span><br><span class="line">conn = DBUtil.getTxnConnection();</span><br><span class="line">PreparedStatement ps = DBUtil.getPreparedStatement(conn,</span><br><span class="line"><span class="string">"select acctSN+1 from t_acctsn_custno_pkid where custNo = ? for update"</span>);</span><br><span class="line">ps.setString(<span class="number">1</span>, <span class="string">"0000002"</span>);</span><br><span class="line">ResultSet rs = ps.executeQuery();</span><br><span class="line">DBUtil.closePreparedStatement(ps);</span><br><span class="line"></span><br><span class="line">ps = DBUtil.getPreparedStatement(conn,</span><br><span class="line"><span class="string">" update  t_acctsn_custno_pkid  set acctSN=?  where custNo = ? "</span>);</span><br><span class="line">ps.setString(<span class="number">1</span>, acctSN);</span><br><span class="line">ps.setString(<span class="number">2</span>, <span class="string">"0000002"</span>);</span><br><span class="line">ps.executeUpdate();</span><br><span class="line">DBUtil.closePreparedStatement(ps);</span><br><span class="line"></span><br><span class="line">DBUtil.closeTxnConnection(conn, <span class="keyword">true</span>);</span><br><span class="line">&#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">e.printStackTrace();</span><br><span class="line"><span class="keyword">if</span> (<span class="keyword">null</span> != conn) &#123;</span><br><span class="line">DBUtil.closeTxnConnection(conn, <span class="keyword">false</span>);</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">&#125;).start();</span><br></pre></td></tr></table></figure><h4 id="3-MySQL死锁日志"><a href="#3-MySQL死锁日志" class="headerlink" title="3) MySQL死锁日志"></a>3) MySQL死锁日志</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><span class="line">------------------------</span><br><span class="line">LATEST DETECTED DEADLOCK</span><br><span class="line">------------------------</span><br><span class="line">2015-12-31 15:34:23 7fb7cdabb700</span><br><span class="line">*** (1) TRANSACTION:</span><br><span class="line">TRANSACTION 336633, ACTIVE 0 sec fetching rows</span><br><span class="line">mysql tables in use 1, locked 1</span><br><span class="line">LOCK WAIT 3 lock struct(s), heap size 360, 2 row lock(s)</span><br><span class="line">MySQL thread id 102345, OS thread handle 0x7fb7cdc41700, query id 1133481 172.16.5.15 root Sending data</span><br><span class="line">select acctSN+1 from t_acctsn_custno_pkid where custNo = &apos;0000001&apos; for update</span><br><span class="line">*** (1) WAITING FOR THIS LOCK TO BE GRANTED:</span><br><span class="line">RECORD LOCKS space id 810 page no 3 n bits 72 index `PRIMARY` of table `checkaccount`.`t_acctsn_custno_pkid` trx id 336633 lock_mode X locks rec but not gap waiting</span><br><span class="line">Record lock, heap no 3 PHYSICAL RECORD: n_fields 6; compact format; info bits 0</span><br><span class="line"> 0: len 8; hex 8000000000000002; asc         ;;</span><br><span class="line"> 1: len 6; hex 0000000522f0; asc     &quot; ;;</span><br><span class="line"> 2: len 7; hex 31000001721bc8; asc 1   r  ;;</span><br><span class="line"> 3: len 20; hex 3030303030303220202020202020202020202020; asc 0000002             ;;</span><br><span class="line"> 4: len 2; hex 3030; asc 00;;</span><br><span class="line"> 5: len 8; hex 3135343434202020; asc 15444   ;;</span><br><span class="line"></span><br><span class="line">*** (2) TRANSACTION:</span><br><span class="line">TRANSACTION 336632, ACTIVE 0 sec starting index read</span><br><span class="line">mysql tables in use 1, locked 1</span><br><span class="line">3 lock struct(s), heap size 360, 2 row lock(s)</span><br><span class="line">MySQL thread id 102340, OS thread handle 0x7fb7cdabb700, query id 1133491 172.16.5.15 root updating</span><br><span class="line">update  t_acctsn_custno_pkid  set acctSN=&apos;15445&apos;  where custNo = &apos;0000002&apos;</span><br><span class="line">*** (2) HOLDS THE LOCK(S):</span><br><span class="line">RECORD LOCKS space id 810 page no 3 n bits 72 index `PRIMARY` of table `checkaccount`.`t_acctsn_custno_pkid` trx id 336632 lock_mode X locks rec but not gap</span><br><span class="line">Record lock, heap no 3 PHYSICAL RECORD: n_fields 6; compact format; info bits 0</span><br><span class="line"> 0: len 8; hex 8000000000000002; asc         ;;</span><br><span class="line"> 1: len 6; hex 0000000522f0; asc     &quot; ;;</span><br><span class="line"> 2: len 7; hex 31000001721bc8; asc 1   r  ;;</span><br><span class="line"> 3: len 20; hex 3030303030303220202020202020202020202020; asc 0000002             ;;</span><br><span class="line"> 4: len 2; hex 3030; asc 00;;</span><br><span class="line"> 5: len 8; hex 3135343434202020; asc 15444   ;;</span><br><span class="line"></span><br><span class="line">*** (2) WAITING FOR THIS LOCK TO BE GRANTED:</span><br><span class="line">RECORD LOCKS space id 810 page no 3 n bits 72 index `PRIMARY` of table `checkaccount`.`t_acctsn_custno_pkid` trx id 336632 lock_mode X locks rec but not gap waiting</span><br><span class="line">Record lock, heap no 2 PHYSICAL RECORD: n_fields 6; compact format; info bits 0</span><br><span class="line"> 0: len 8; hex 8000000000000001; asc         ;;</span><br><span class="line"> 1: len 6; hex 0000000522f1; asc     &quot; ;;</span><br><span class="line"> 2: len 7; hex 32000001b423cc; asc 2    # ;;</span><br><span class="line"> 3: len 20; hex 3030303030303120202020202020202020202020; asc 0000001             ;;</span><br><span class="line"> 4: len 2; hex 3030; asc 00;;</span><br><span class="line"> 5: len 8; hex 3537323520202020; asc 5725    ;;</span><br><span class="line"></span><br><span class="line">*** WE ROLL BACK TRANSACTION (2)</span><br></pre></td></tr></table></figure><h4 id="4-MySQL数据库隔离级别"><a href="#4-MySQL数据库隔离级别" class="headerlink" title="4) MySQL数据库隔离级别"></a>4) MySQL数据库隔离级别</h4><p>RC(READ COMMITTED)</p><h3 id="2-知识准备"><a href="#2-知识准备" class="headerlink" title="2 知识准备"></a>2 知识准备</h3><ul><li>1 一条当前读的SQL语句，InnoDB与MySQL Server的交互，是一条一条进行的，因此，加锁也是一条一条进行的。先对一条满足条件的记录加锁，返回给MySQL Server，做一些DML操作；然后在读取下一条加锁，直至读取完毕。</li><li>2 RC级别下，由于custNo列上没有索引，因此只能走主键索引，进行全部扫描。主键索引上所有的记录，都被加上了X锁。无论记录是否满足条件，全部被加上X锁。既不是加表锁，也不是在满足条件的记录上加行锁。<br>在实际的实现中，MySQL有一些改进，在MySQL Server过滤条件，发现不满足后，会调用unlock_row方法，把不满足条件的记录放锁 (违背了2PL的约束)。这样做，保证了最后只会持有满足条件记录上的锁，但是每条记录的加锁操作还是不能省略的。</li></ul><h3 id="3-死锁分析"><a href="#3-死锁分析" class="headerlink" title="3 死锁分析"></a>3 死锁分析</h3><h4 id="0-死锁日志分析"><a href="#0-死锁日志分析" class="headerlink" title="0)死锁日志分析"></a>0)死锁日志分析</h4><ul><li>(1) TRANSACTION (select acctSN+1 from t_acctsn_custno_pkid where custNo = ‘0000001’ for update) 共需3个锁(LOCK WAIT 3 lock struct(s)),其中1个表级意向写锁 IX ,2个行锁(2 row lock(s))。发生死锁时在尝试获取主键索引上(index <code>PRIMARY</code>) 某一行的X锁(table <code>checkaccount</code>.<code>t_acctsn_custno_pkid</code> trx id 336633 lock_mode X locks rec)<br>这一行的内容为 1 0000002 00 15444<br>( 0: len 8; hex 8000000000000002; asc         ;;<br>1: len 6; hex 0000000522f0; asc     “ ;;<br>2: len 7; hex 31000001721bc8; asc 1   r  ;;<br>3: len 20; hex 3030303030303220202020202020202020202020; asc 0000002             ;;<br>4: len 2; hex 3030; asc 00;;<br>5: len 8; hex 3135343434202020; asc 15444   ;;<br>注意asc后的内容为行中每列的内容的ASCII码)</li><li>(2) TRANSACTION (update  t_acctsn_custno_pkid  set acctSN=’15445’  where custNo = ‘0000002’) 共需3个锁(LOCK WAIT 3 lock struct(s)),其中1个表级意向写锁 IX ,2个行锁(2 row lock(s))。发生死锁时在已经持有主键索引上(index <code>PRIMARY</code>) 某一行的X锁(t table <code>checkaccount</code>.<code>t_acctsn_custno_pkid</code> trx id 336632 lock_mode X locks rec)。这一行的内容为  1 0000002 00 15444。并且发生死锁时在尝试获取主键索引上(index <code>PRIMARY</code>) 某一行的X锁table <code>checkaccount</code>.<code>t_acctsn_custno_pkid</code> trx id 336632 lock_mode X locks rec)。这一行的内容为 2 0000001 00 5725.<br>注1：死锁日志中(1) TRANSACTION中并不显示已持有的锁信息<br>注2：死锁日志分析中红字部分与表数据中的id列不对应，经过多次实验(2: len 7; hex 31000001721bc8; asc 1   r  ;;)这一列asc 后的内容并不显示不固定，有时是数字有时是字母有时是符号，推测与id有关但是显示的并不是id的值，估计与id列类型(<code>id</code> bigint(20) NOT NULL AUTO_INCREMENT,)有关系。</li></ul><h4 id="1-事物时序分析"><a href="#1-事物时序分析" class="headerlink" title="1)事物时序分析"></a>1)事物时序分析</h4><table><thead><tr><th>Transaction1</th><th>Transaction2</th></tr></thead><tbody><tr><td></td><td>（select acctSN+1 from t_acctsn_custno_pkid where custNo = ‘0000002’ for update）<br>1 对主键索引上所有行一行一行的加X锁<br>2释放custNo 为 ‘0000001’的行上的X锁保留custNo 为 ‘0000002’的行上的X锁</td></tr><tr><td>（select acctSN+1 from t_acctsn_custno_pkid where custNo = ‘0000001’ for update）<br>3对主键索引上所有行一行一行的加X锁，首先先对主键索引上custno为0000001的行加X锁<br>4 尝试获取主键索引上custno为0000002上的X锁</td><td></td></tr><tr><td></td><td>（update  t_acctsn_custno_pkid  set acctSN=’15445’  where custNo = ‘0000002’）<br>5对主键索引上所有行一行一行的加X锁，尝试获取主键索引上custno为0000001上的X锁</td></tr></tbody></table><p>注3：根据前面的知识储备，及对死锁日志的分析，在上表的时序下满足了死锁产生的条件。</p><h3 id="4-解决方法"><a href="#4-解决方法" class="headerlink" title="4 解决方法"></a>4 解决方法</h3><p>根据以上分析，绕了一大圈解决方法其实很简单 在custNo上加索引即可，其实custNo作为查询条件是应该有索引的，上线的同学疏忽了，造成了比较严重的线上故障，引以为戒。</p>]]></content>
    
    <summary type="html">
    
      一次真实线上故障的总结
    
    </summary>
    
      <category term="Technique" scheme="http://www.fintx.org/categories/Technique/"/>
    
    
      <category term="mysql" scheme="http://www.fintx.org/tags/mysql/"/>
    
      <category term="deadlock" scheme="http://www.fintx.org/tags/deadlock/"/>
    
  </entry>
  
  <entry>
    <title>基于GitLab Flow 的开发/测试/运维统一上线流程</title>
    <link href="http://www.fintx.org/20170705-dev-qa-ops-unified-flow-base-on-gitlab-flow.html"/>
    <id>http://www.fintx.org/20170705-dev-qa-ops-unified-flow-base-on-gitlab-flow.html</id>
    <published>2017-07-05T12:04:08.000Z</published>
    <updated>2018-05-13T13:40:51.436Z</updated>
    
    <content type="html"><![CDATA[<!-- 标签 方式，要求版本在0.4.5或以上 --><blockquote class="blockquote-center"><p>完善的上线流程是系统稳定的重要保证.</p></blockquote>    <p>很多中小型的初创公司使用Gitlab管理自己的代码，想必大家在项目的开发上线中也踩过了很多坑。作为一个过来人选择了Gitlab官方推荐的带有环境分支Gitlab flow（Environment branches with GitLab flow）的最佳实践作为基础，为研发、质量保证和运维团队制定了统一的上线流程。</p><p><img src="./uploads/posts/dev-qa-ops-unified-flow-base-on-gitlab-flow/UnifiedFlow.jpg" alt="开发-测试-运维统一上线流程"></p><h3 id="一般原则"><a href="#一般原则" class="headerlink" title="一般原则"></a>一般原则</h3><ul><li>分支分为永久分支和临时分支，都是在master分支以外建立。</li><li>永久分支不会被删除，包括开发的分支master，准生产分支pre-production，生产分支production。</li><li>临时分支分为功能分支Feature和修复分支Fix，在开发完成会被删除。功能分支必须有对应的需求，修复分支必须对应bug。禁止建立其他类型的分支。</li><li>临时分支的建立需从master分支创建，并在开发过程中实时合并master的变更。</li><li>Feature和Fix在开发人员在开发环境部署测试，master在测试环境中部署测试，pre-production在仿真环境中部署测试，production在生产环境发布。</li><li>代码的变化，必须由”上游”向”下游”发展。Feature和Fix是master的”上游”，master是pre-production的上游，pre-production是production的”上游”。比如，生产环境出现了bug 或者要开发新的feature，这时就要建一个临时分支，开发完成把它合并到master，确认没有问题，再cherry-pick到pre-production，这一步也没有问题，才进入production。</li><li>质量保证团队成员在开发环境进行第1轮测试，在测试环境进行第2轮测试，在仿真环境进行第3轮测试，发布生产环境之后进行最终验收。</li><li>在代码从临时分支合并发布到master分支的时候必须通过质量保证的第1轮测试和顾问的code review，从master分支提交到pre-production的时候必须通过第2轮测试并在提交后通知运维团队上线日期并提交必要的运维工单，从pre-production提交到production必须通过第3轮测试和经过运维团队的批准，整个上线结果以质量保证最终验收为准。</li><li>任何不符合上述流程的代码分支创建和合并必须由项目负责人和项目顾问一起多数通过。任何不经过或者减少测试次数的发布必须通知研发 测试 产品团队负责人并经由多数通过。    </li></ul><h3 id="特殊情况"><a href="#特殊情况" class="headerlink" title="特殊情况"></a>特殊情况</h3><ul><li>并行进行的项目虽然提交发布时间上会有冲突，但是“代码通常不会冲突”，不同的feature需要同时修改同一段代码的情况概率很低，通常采用cherry-pick发布需要发布的部分即可 。   </li><li>线上故障并且修复代码和已提交未上线的代码有“代码冲突”的，过批准可以从production分支创建临时分支，开发完成提交production之后必须马上提交master和pre-production流程与正常流程相同，但是其他非紧急需求必须为此让路，即故障和紧急需求提交合并到master或pre-production分支之前禁止此分支提交合并代码,合并完成紧急代码之后优先对紧急代码修复的功能进行测试。    </li><li>紧急需求不能遵守一般开发流程的，开发团队原则上不接受或者必须延后开发上线时间，特别紧急情况经过并且开发代码和已提交未上线代码有“代码冲突”的，经批准按线上故障流程处理。    </li><li>未列举情况原则上经过批准可以按线上故障流程处理。</li></ul>]]></content>
    
    <summary type="html">
    
      完善的上线流程是系统稳定的重要保证
    
    </summary>
    
      <category term="DevOps" scheme="http://www.fintx.org/categories/DevOps/"/>
    
    
      <category term="development" scheme="http://www.fintx.org/tags/development/"/>
    
      <category term="operations" scheme="http://www.fintx.org/tags/operations/"/>
    
  </entry>
  
  <entry>
    <title>业务与技术的思考</title>
    <link href="http://www.fintx.org/20170628-thinking-about-business-and-technology.html"/>
    <id>http://www.fintx.org/20170628-thinking-about-business-and-technology.html</id>
    <published>2017-06-28T13:44:05.000Z</published>
    <updated>2018-05-19T17:03:47.208Z</updated>
    
    <content type="html"><![CDATA[<!-- 标签 方式，要求版本在0.4.5或以上 --><blockquote class="blockquote-center"><p>做一些踏踏实实的事情.</p></blockquote>    <p>Technology is tool, business is king.<br>技术是业务需求驱动的。用了再高深的技术，不满足正当的需求也是失败的项目。反过来说超出正当的需求而用了高深的技术，虽不一定是失败也是不理智的。用分布式系统为图书馆做个图书管理系统，这基本是只能在学校里发生的事情。当然也不排除是业务革命的可能，比如谷歌图书馆。但是世界上能有几个谷歌图书馆呢。<br>纵观国内的互联网金融乃至整个互联网行业，浮躁充斥着各个角落。由于高速发展带来的财富效应，资本聚集。即使遭遇寒冬，仍不能让人们冷静下来。在此感染下，催熟了一大批“高级工程师”，“架构师”。他们言必分布式语必微服务，张口cloud，闭口serverless。看了个例子就熟练XXX。“吹水”成了看家本领和最擅长的技能。找一家创业公司，拿着不菲的薪资，干着忽悠老板，忽悠投资人的事。他们从各种地方找各种人用开源软件七拼八凑，用他口中那些技术做了个每天个位数访问量的系统。殊不知，淘宝在2008年（PV2.5亿，会员5000万）之前还基本是一个单一系统。即便考虑技术的进步，PV过千万会员过百万之前还是省省吧。认真把基础的项目划分模块划分做好，分布式服务化都是水到渠成的事。即使是大型微服务化系统也是由一个个单一的节点组成，每一个节点本质都是一个单体系统。单一体系统都没做好就大干快上微服务无异于沙上建塔。其结果可想而知。问题百出，人肉硬扛，累跑运维是肯定的，没准还成本奇高。关键是最终才发现，业务没做好。当然公司最后如何与这些人是其无关，都可以怪公司业务没做好。当然失败的经验也是经验，技术当作成绩可以拿来忽悠下一家公司。<br> 合适的才是最好的。是否合适取决于公司战略，业务，个人能力，团队的水平，和对技术的熟悉程度。对于创业公司来说，面临的问题与有成熟的大公司完全不同。用分布式，自建私有云，上规模的公司有高水平的研发团队，可靠的运维团队，几乎用不完的资源做后盾。这些公司业务完善稳定，技术改造同时不用面临业务的压力。但对创业公司而言，资源有限，人员有限，资金有限，团队水平参差不齐，互相不熟悉。在这种情况下，用最熟练的技术，快速实现业务需求，把业务流程，用户体验作好，精雕细琢。先让业务稳定下来才是首要目标。有了经过业务洗礼的可靠技术运维团队，有了一定的用户量做基础，才有可能支持系统进一步优化。<br> 踏踏实实先做好业务的每一个细节。说来简单，可现实是残酷的。金融企业竟然连一个账务系统都没有，自己收了多少钱，该交多少税，最后赚了多少，哪些产品盈利，哪些产品亏损，一概不知道。拿企业最重要的资金收付来说，钱收的时候到没到，该收的够不够，多收少收怎么办，这些要么都是人工处理，要么根本不管对错。某银消费金融公司与下属企业上百万的款项成了糊涂账，并遭内外合伙骗贷造成巨额亏损。这种事情绝对不是个例。<br>罗马不是一天建成，好的系统是一步步迭代来的而不是设计出来的。“97%的情况下，过早优化是万恶之源”。有限的资源下，过度的技术设计必然导致业务设计不足，这是自寻死路。对业务需求，技术支撑不足才是初创阶段的主要矛盾。开始就按百亿估值做系统的创业公司，都死了。上来就按百亿级流量做分布式设计的架构师，都跑了。<br>希望FinTx能为大家提供一个探讨金融业务，架构，设计，技术，法规等话题的场所，并且提供一些能支持当下业务也能扩展未来发展的基础组件。大家一起做一些踏踏实实的事情。</p>]]></content>
    
    <summary type="html">
    
      谨以此篇作为FinTx的开始
    
    </summary>
    
      <category term="Thinking" scheme="http://www.fintx.org/categories/Thinking/"/>
    
    
      <category term="technology" scheme="http://www.fintx.org/tags/technology/"/>
    
      <category term="business" scheme="http://www.fintx.org/tags/business/"/>
    
  </entry>
  
</feed>
