当前位置: 首页 > news >正文

福建住房与城乡建设厅网站海外广告推广公司

福建住房与城乡建设厅网站,海外广告推广公司,wordpress主题和插件,api网站制作Mysql事务 锁升级 行锁升级表锁#xff1a;如果不是索引查找#xff0c;或者索引失效#xff0c;此时需要全表扫描#xff0c;会升级为锁整张表。 为什么Mysql要把扫描到的每一行以及其间隙都加锁#xff1f;这是为了防止幻读出现。幻读导致的问题是破坏了一致性声明如果不是索引查找或者索引失效此时需要全表扫描会升级为锁整张表。 为什么Mysql要把扫描到的每一行以及其间隙都加锁这是为了防止幻读出现。幻读导致的问题是破坏了一致性声明并且导致binlog混乱 mvcc 更新和删除不会改变旧版本数据而是将旧版本数据复制到undoLog中。 redoLog undolog 事务回滚 、mvcc 从本质上说为实现MVCC机制InnoDB存储引擎在数据库每⾏数据的后⾯添加了3个字段6字节的事务idDB_TRX_ID字段、7字节的回滚指针DB_ROLL_PTR字段、6字节的DB_ROW_ID字段 6字节的事务idDB_TRX_ID字段。 ⽤来标识最近⼀次对本⾏记录做修改insert、update的事务的标识符即最后⼀次修改本⾏记录的事务id。如果是delete操作在InnoDB存储引擎内部也属于⼀次update操作即更新⾏中的⼀个特殊位将⾏标识为已删除并⾮真正删除。 27字节的回滚指针DB_ROLL_PTR字段。 主要指向上⼀个版本的⾏记录能够从最新版本的⾏记录逐级向上找到要查找的⾏版本记录。 36字节的DB_ROW_ID字段。 这个字段包含⼀个随着新数据⾏的插⼊操作⽽单调递增的⾏id当由InnoDB存储引擎⾃动产⽣聚集索引时聚集索引会包含这个⾏id否则这个⾏id不会出现在任何索引中。 binlog binlog记录模式Row和Statement 1.Row模式 Row模式下的BinLog⽂件会记录每⼀⾏数据被修改的情况然后在MySQL从数据库中对相同的数据进⾏修改。 Row模式的优点是能够⾮常清楚地记录每⼀⾏数据的修改情况完全实现主从数据库的同步和数据的恢复。 Row模式的缺点是如果主数据库中发⽣批量操作尤其是⼤批量的操作会产⽣⼤量的⼆进制⽇志。⽐如使⽤alter table操作修改拥有⼤量数据的数据表结构时会使⼆进制⽇志的内容暴涨产⽣⼤量的⼆进制⽇志从⽽⼤⼤影响主从数据库的同步性能。 2.Statement模式 Statement模式下的BinLog⽂件会记录每⼀条修改数据的SQL语句MySQL从数据库在复制SQL语句的时候会通过SQL进程将BinLog中的SQL语句解析成和MySQL主数据库上执⾏过的SQL语句相同的SQL语句然后在从数据库上执⾏SQL进程解析出来的SQL语句。 Statement模式的优点是由于不记录数据的修改细节只是记录数据表结构和数据变更的SQL语句因此产⽣的⼆进制⽇志数据量⽐较⼩这样能够减少磁盘的I/O操作提升数据存储和恢复的效率。 Statement模式的缺点是在某些情况下可能会导致主从数据库中的数据不⼀致。例如在MySQL主数据库中使⽤了last_insert_id()和now()等函数(主从执行sql返回的结果不一致)会导致MySQL主从数据库中的数据不⼀致。 binlog写入 事务在Commit阶段会将产⽣的⽇志事件写⼊磁盘的BinLog⽂件中。因为不同的事务会以串⾏的⽅式将⽇志事件写⼊BinLog⽂件中所以⼀个事务中包含的⽇志事件信息在BinLog⽂件中是连续的中间不会插⼊其他事务的⽇志事件 两阶段提交 之所以要先redo prepare后bin是由于binlog不具备主动的崩溃恢复能力。 BinLog组提交机制 在不开启binlog的场景下redolog可以采用组提交多个事务的redologbuffer都写完后再进行统一的fsync。但是开启binlog后由于要进行 二阶段提交而redolog的prepare操作会上一把锁prepare_commit_mutex这把锁导致多个事务无法同时prepare从而导致无法组提交redolog。 加锁前prepare和write的顺序不一样导致不一致问题 加锁后 这个问题在MySQL 5.6中得到了解决。在MySQL 5.6中提交事务时会在InnoDB存储引擎的上层将事务按照⼀定的顺序放⼊⼀个队列队列中的第⼀个事务称为leader其他事务称为follower。在执⾏顺序上虽然还是先写BinLog再写事务⽇志但是写⽇志的机制发⽣了变化移除了prepare_commit_mutex锁。开启BinLog后组提交功能不会失效。BinLog的写⼊和redoLog写⼊都是通过组提交功能进⾏的。这种实现⽅式称为⼆进制⽇志组提交Binary Log Group Commit,BLGC。 BLGC的实现主要分为Flush、Sync和Commit三个阶段。 1Flush阶段将每个事务的BinLog写⼊对应的内存缓冲区。 2Sync阶段将内存缓冲区中的BinLog写⼊磁盘的BinLog⽂件如果队列中存在多个事务则此时只执⾏⼀次刷盘操作就可以将多个事务的BinLog刷新到磁盘的BinLog⽂件中这就是BLGC操作。 3Commit阶段leader事务根据队列中事务的顺序调⽤存储引擎层事务的提交操作由于InnoDB存储引擎本身就⽀持组提交功能因此解决了prepare_commit_mutex锁导致的组提交功能失效的问题。 在Flush阶段将BinLog写⼊内存缓冲区时不是写完就⽴刻进⼊Sync阶段⽽是等待⼀定时间多积累⼏个事务的BinLog再⼀起进⼊Sync阶段 这里有个问题这种BLGC的方式我读下来本质上是先一块写binlog再一块写redolog那会不会导致写redo的时候失败然后binlog和redolog不一致呢 https://zhuanlan.zhihu.com/p/143052123?utm_id0 其实是先一块写prepare然后再一块写binlog以prepare的顺序最后再commit。 XA事务 1事务管理器主要对参与全局事务的各个分⽀事务进⾏协调并与资源管理器进⾏通信。 2资源管理器主要提供对对事务资源的访问能⼒。实际上⼀个数据库就可以看作⼀个资源管理器。 3应⽤程序主要⽤来明确全局事务和各个分⽀事务指定全局事务中的各个操作 因为XA事务是基于两阶段提交的分布式事务所以XA事务也被拆分为Prepare阶段和Commit阶段。 在Prepare阶段事务管理器向资源管理器发送准备指令资源管理器接收到指令后执⾏数据的修改操作并记录相关的⽇志信息然后向事务管理器返回可以提交或者不可以提交的结果信息。 在Commit阶段事务管理器接收所有资源管理器返回的结果信息如果某⼀个或多个资源管理器向事务管理器返回的结果信息为不可以提交或者超时则事务管理器向所有的资源管理器发送回滚指令。如果事务管理器收到的所有资源管理器返回的结果信息为可以提交则事务管理器向所有的资源管理器发送提交事务的指令。 JDBC使用XA import com.mysql.jdbc.jdbc2.optional.MysqlXAConnection; import com.mysql.jdbc.jdbc2.optional.MysqlXid; import javax.sql.XAConnection; import javax.transaction.xa.XAException; import javax.transaction.xa.XAResource; import javax.transaction.xa.Xid; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.SQLException; public class MysqlXAConnectionTest {public static void main(String[] args) throws SQLException { //打印XA⽇志boolean writeLog true; // 获得资源管理器操作接⼝实例RM1Connection conn1 DriverManager.getConnection(jdbc:mysql://localhost:3306/test, binghe, binghe123); //配置打印XA⽇志XAConnection xaConn1 new MysqlXAConnection((com.mysql.jdbc.Connection) conn1, writeLog);XAResource rm1 xaConn1.getXAResource(); // 获得资源管理器操作接⼝实例RM2Connection conn2 DriverManager.getConnection(jdbc:mysql://localhost:3306/test, binghe,binghe123); //配置打印XA⽇志XAConnection xaConn2 new MysqlXAConnection((com.mysql.jdbc.Connection) conn2, writeLog);XAResource rm2 xaConn2.getXAResource(); // 应⽤程序请求事务管理器执⾏⼀个分布式事务事务管理器⽣成全局事务idbyte[] gtrid binghe123.getBytes();int formatId 1;Xid xid1null;Xid xid2null;try { // 分别执⾏RM1和RM2上的事务分⽀ // 事务管理器⽣成rm1上的事务分⽀idbyte[] bqual1 binghe001.getBytes();xid1 new MysqlXid(gtrid, bqual1, formatId); // 执⾏rm1上的事务分⽀rm1.start(xid1, XAResource.TMNOFLAGS);PreparedStatement ps1 conn1.prepareStatement(INSERT into xa_test(name) VALUES (binghe));ps1.execute();rm1.end(xid1, XAResource.TMSUCCESS); // 事务管理器⽣成rm2上的事务分⽀idbyte[] bqual2 binghe002.getBytes();xid2 new MysqlXid(gtrid, bqual2, formatId); // 执⾏rm2上的事务分⽀rm2.start(xid2, XAResource.TMNOFLAGS);PreparedStatement ps2 conn2.prepareStatement(INSERT into xa_test(name) VALUES (binghe));ps2.execute();rm2.end(xid2, XAResource.TMSUCCESS); // 两阶段提交 // 第⼀阶段通知所有的资源管理器准备提交事务分⽀int rm1_prepare rm1.prepare(xid1);int rm2_prepare rm2.prepare(xid2); // 第⼆阶段提交所有事务分⽀boolean onePhase false; //所有事务分⽀都进⼊准备状态提交所有事务分⽀if (rm1_prepare XAResource.XA_OK rm2_prepare XAResource.XA_OK ) {rm1.commit(xid1, onePhase);rm2.commit(xid2, onePhase);} else { //如果有事务分⽀没有进⼊准备状态则回滚所有的分⽀事务rm1.rollback(xid1);rm2.rollback(xid2);}} catch (XAException e) { // 如果出现异常也要进⾏回滚rm1.rollback(xid1);rm2.rollback(xid2);e.printStackTrace();}} }Spring事务 从本质上讲Spring事务是对数据库事务的进⼀步封装。也就是说如果数据库不⽀持事 务Spring也⽆法实现事务操作。Spring并不是直接管理事务的⽽是提供了多种事务管理器。通过这 些事务管理器Spring将事务管理的职责委托给了Hibernate、MyBatis、JTA等持久化框架的事务来实现。 事务传播机制 本质上是新的方法操作面对当前是否存在事务的不同表现。如果当前没有事务是开启一个事务(REQUIRED)还是以非事务的方式执行(SUPPORT)还是抛出异常(MANDATORY)如果当前有事务是加入这个事务与其组成一个整体(REQUIRED)还是挂起当前事务并新建一个事务执行(REQUIRES_NEW)还是嵌套这个事务(NESTED)还是抛出异常(NEVER)还是挂起当前事务以非事务的方式执行(NOT_SUPPORTED) 使用场景 REQUIREDSpring中默认的传播机制适⽤于⼤部分场景 NOT_SUPPORTED适⽤于发送提示信息、站内信、短信、邮件等这类场景要求不影响系统 的主体业务逻辑即使操作失败也不应该对主体逻辑产⽣影响不能使主体逻辑的事务回滚 REQUIRES_NEW总是创建新的事务执⾏适⽤于不受外层⽅法事务影响的场景。例如记录⽇ 志的操作不管主体业务逻辑是否已经完成⽇志都要记录下来不能因为主体业务逻辑异常 事务回滚⽽导致⽇志操作冋滚 注意如果在同一个service类中定义的两个方法 内层REQUIRES_NEW并不会开启新的事务save和update中回滚都会导致整个事务的回滚 如果在不同的service中定义的两个方法 内层REQUIRES_NEW会开启新的事务并且二者独立事务回滚互不影响 事务失效如果同⼀个类中的两个⽅法A和B上均添加了事务注解⽅法A调⽤⽅法B则⽅法B的事务会失效如果异常被捕获则也不会触发事务回滚Spring中默认回滚的事务异常类型为RuntimeException如果不是则不会触发事务回滚可以在注解上定义目标异常类型 分布式事务的基本概念 架构演进 这种架构的优点如下。 1架构简单项⽬开发和维护成本低。 2所有项⽬模块部署在⼀起对于⼩型项⽬来说⽅便维护。 但是其缺点也是⽐较明显的。 1所有模块耦合在⼀起对于⼤型项⽬来说不易开发和维护。 2项⽬各模块之间过于耦合⼀旦有模块出现问题整个项⽬将不可⽤。 3⽆法针对某个具体模块来提升性能。 4⽆法对项⽬进⾏⽔平扩展。 正是由于单体应⽤架构存在诸多缺点才逐渐演变为垂直应⽤架构。 这种架构的优点如下。 1对系统进⾏拆分可根据不同系统的访问情况有针对性地进⾏优化。 2能够实现应⽤的⽔平扩展。 3各系统能够分担整体访问流量解决了并发问题。 4⼦系统发⽣故障不影响其他⼦系统的运⾏情况提⾼了整体的容错率。 这种架构的缺点如下。 1拆分后的各系统之间相对独⽴⽆法进⾏互相调⽤。 2各系统难免存在重叠的业务会存在重复开发的业务后期维护⽐较困难 将系统演变为垂直应⽤架构之后当垂直应⽤越来越多时重复编写的业务代码就会越来越 多。此时我们需要将重复的代码抽象出来形成统⼀的服务供其他系统或者业务模块调 ⽤这就是分布式架构 这种架构的优点如下。 1将重复的业务代码抽象出来形成公共的访问服务提⾼了代码的复⽤性。 2可以有针对性地对系统和服务进⾏性能优化以提升整体的访问性能。 这种架构的缺点如下。 1系统之间的调⽤关系变得复杂。 2系统之间的依赖关系变得复杂。 3系统维护成本⾼。 在分布式架构下当部署的服务越来越多时重复的代码就会变得越来越多不利于代码的复 ⽤和系统维护。为此我们需要增加⼀个统⼀的调度中⼼对集群进⾏实时管理这就是 SOA⾯向服务架构。 这种架构的优点是通过注册中⼼解决了各个服务之间服务依赖和调⽤关系的⾃动注册与发现。 这种架构的缺点如下。 1各服务之间存在依赖关系如果某个服务出现故障可能会造成服务器崩溃。 2服务之间的依赖与调⽤关系复杂增加了测试和运维的成本 微服务架构是在SOA架构的基础上进⾏进⼀步的扩展和拆分。在微服务架构下⼀个⼤的项⽬ 拆分为⼀个个⼩的可独⽴部署的微服务每个微服务都有⾃⼰的数据库 这种架构的缺点如下。 1开发成本⽐较⾼。 2涉及各服务的容错性问题。 3涉及数据的⼀致性问题。 4涉及分布式事务问题 分布式事务场景 将⼀个⼤的应⽤系统拆分为多个可以独⽴部署的应⽤服务需要各个服务远程协作才能完成某 些事务操作这就涉及分布式事务的问题。总的来讲分布式事务会在3种场景下产⽣分别 是跨JVM进程、跨数据库实例和多服务访问单数据库。 数据一致性问题 总的来说数据的⼀致性问题包含数据多副本、调⽤超时、缓存与数据库不⼀致、多个缓存节 点数据不⼀致等场景。 数据多副本场景 如果数据的存储存在多副本的情况当⽹络、服务器或者系统软件出现故障时可能会导致⼀ 部分副本写⼊成功⼀部分副本写⼊失败造成各个副本之间数据的不⼀致。调⽤超时场景 调⽤超时场景包含同步调⽤超时和异步调⽤超时。 同步调⽤超时往往是由于⽹络、服务器或者系统软件异常引起的例如服务A同步调⽤服务B 时出现超时现象导致服务A与服务B之间的数据不⼀致。 异步调⽤超时是指服务A异步调⽤服务B同样是由于⽹络、服务器或者系统软件异常导致调⽤ 失败出现服务A与服务B之间的数据不⼀致的情况。⼀个典型的场景就是⽀付成功的异步回调 通知。缓存与数据库不⼀致场景 这种场景主要针对缓存与数据库。在⾼并发场景下⼀些热数据会缓存到Redis或者其他缓存 组件中。此时如果对数据库中的数据进⾏新增、修改和删除操作缓存中的数据如果得不到 及时更新就会导致缓存与数据库中数据不⼀致。多个缓存节点数据不⼀致场景 这种场景主要针对缓存内部各节点之间数据的不⼀致。例如在Redis集群中由于⽹络异常等 原因引起的脑裂问题就会导致多个缓存节点数据不⼀致。 针对数据不一致的解决方案ACID特性、CAP理论、Base理论、DTP模型、2PC两阶段提交模型、3PC三阶段提交模型、TCC模型、可靠消息最终⼀致性模型、最⼤努⼒通知模型等。 分布式事务理论知识 CAP C 1存在数据同步的过程应⽤程序的写操作存在⼀定的延迟。 2为了保证各节点数据的⼀致性需要对相应的资源进⾏锁定待数据同步完成后再释放锁 定的资源。 3如果数据写⼊并同步成功所有节点都会返回最新的数据。相反地如果数据写⼊或者同 步失败所有节点都不会存在最新写⼊的数据。 A 1所有的请求都会被响应。 2不会存在响应超时或者响应错误的情况。 3如果对不同的应⽤程序设定了超时响应时间⼀旦超过这个时间系统将不可⽤。 P 1⼀个节点挂掉不影响其他节点对外提供服务。 2分区容忍性是分布式系统必须具备的基础能⼒。 BASE Base理论是对CAP理论中AP的⼀个扩展它通过牺牲强⼀致性来获得可⽤性。Base理论中的 Base是基本可⽤Basically Available、软状态Soft State和最终⼀致性 Eventually Consistent的缩写。当系统出现故障时Base理论允许部分数据不可⽤但是 会保证核⼼功能可⽤允许数据在⼀段时间内不⼀致但是经过⼀段时间数据最终是⼀致 的。 BA 基本可⽤是指分布式系统出现故障时允许其损失系统的部分可⽤性⽐如响应时间或者功能 上的损失但是要保证系统基本可⽤。例如在电商业务场景中添加购物⻋和下单功能出现故 障时商品浏览功能仍然可⽤。 S 软状态是指允许系统中存在中间状态这些中间状态不会影响系统的整体可⽤性只是允许系 统各个节点之间的数据同步存在延迟。例如在电商业务场景中订单中的“⽀付中”“退款中”等状 态就是中间状态当达到⼀段时间后就会变成“⽀付成功”或者“退款成功”的状态。 E 最终⼀致性是指系统中各个节点的数据副本经过⼀段时间的同步最终能够达到⼀致的状态。 最终⼀致性需要保证数据经过⼀段时间的同步达到⼀致并不要求各个节点的数据保持实时⼀ 致。例如在电商业务场景中订单中的“⽀付中”“退款中”等状态最终会变成“⽀付成功”“退款成 功”的状态经过⼀段时间的延迟能够使得订单中的状态与最终的交易结果⼀致。 强一致性分布式事务解决方案 在分布式事务领域最早采⽤的是符合CAP理论的强⼀致性事务⽅案来解决分布式事务问题。 强⼀致性分布式事务要求在任意时刻查询参与全局事务的各节点的数据都是⼀致的 强⼀致性事务解决⽅案存在如下优点。 1数据⼀致性⽐较⾼。 2在任意时刻都能够查询到最新写⼊的数据。 强⼀致性事务解决⽅案也存在着如下缺点。 1存在性能问题在分布式事务未完全提交和回滚之前应⽤程序不会查询到最新的数据。 2实现复杂。 3牺牲了可⽤性。 4不适合⾼并发场景 在强⼀致性事务解决⽅案中典型的⽅案包括DTP模型全局事务模型、2PC模型⼆阶段 提交模型和3PC模型三阶段提交模型3种。 DTP模型 1事务⼀个事务就是⼀个完整的⼯作单元具备ACID特性。 2全局事务由事务管理器管理的事务能够⼀次性操作多个资源管理器。 3分⽀事务由事务管理器管理的全局事务中每个资源管理器中独⽴执⾏的事务。 4控制线程执⾏全局事务的线程这个线程⽤来关联应⽤程序、事务管理器和资源管理器 三者之间的关系也就是表示全局事务和分⽀事务的关系通常称为事务上下⽂环境。 1AP应⽤程序Application Program可以理解为参与DTP分布式事务模型的应⽤程序。 2RM资源管理器Resource Manager可以理解为数据库管理系统或消息服务管理器。 应⽤程序可以通过资源管理器对相应的资源进⾏有效的控制。相应的资源需要实现XA定义的接 ⼝。 3TM事务管理器Transaction Manager负责协调和管理DTP模型中的事务为应⽤程序 提供编程接⼝同时管理资源管理器。 2PC模型 1.Prepare阶段 在Prepare阶段事务管理器给每个参与全局事务的资源管理器发送Prepare消息资源管理器 要么返回失败要么在本地执⾏相应的事务将事务写⼊本地的Redo Log⽂件和Undo Log⽂ 件此时事务并没有提交。 2.Commit阶段 如果事务管理器收到了参与全局事务的资源管理器返回的失败消息则直接给Prepare阶段执 ⾏成功的资源管理器发送回滚消息否则向每个资源管理器发送Commit消息。相应的资源管 理器根据事务管理器发送过来的消息指令执⾏对应的事务回滚或事务提交操作并且释放事 务处理过程中使⽤的锁资源。 值得注意的是2PC模型存在着如下的缺点。 1同步阻塞问题事务的执⾏过程中所有参与事务的节点都会对其占⽤的公共资源加锁 导致其他访问公共资源的进程或者线程阻塞。 2单点故障问题如果事务管理器发⽣故障则资源管理器会⼀直阻塞。 3数据不⼀致问题如果在Commit阶段由于⽹络或者部分资源管理器发⽣故障导致部分 资源管理器没有接收到事务管理器发送过来的Commit消息会引起数据不⼀致的问题。 4⽆法解决的问题如果在Commit阶段事务管理器发出Commit消息后宕机并且唯⼀接收 到这条Commit消息的资源管理器也宕机了则⽆法确认事务是否已经提交。 3PC 3PC模型是指三阶段提交模型是在2PC模型的基础上改进的版本。3PC模型把2PC模型中的 Prepare阶段⼀分为⼆最终形成3个阶段CanCommit阶段、PreCommit阶段和doCommit或 者doRollback阶段。 其中CanCommit只是询问不涉及undolog和redolog的写入。资源管理器收到CanCommit消息认为能够执⾏事务会向事务管理器响应Yes消息进⼊预备状态。资源管理器收到PreCommit消息后执⾏事务操作将Undo和Redo信息写⼊事务⽇志并向事务管理器响应Ack状态但此时不会提交事务。事务管理器接收到doCommit消息后正式提交事务并释放执⾏事务期间占⽤的资源同时向事务管理器响应事务已提交的状态 对比 3PC比2PC多了一个cancommit阶段减少了不必要的资源浪费。 因为2PC的第一阶段直接就执行事务操作并将事务信息写入日志了要是后面失败就浪费了资源而3PC的cancommit阶段可以校验是否可以执行如果不能执行就直接返回这样就减少了资源的浪费。 3PC引入超时机制同时在协调者和参与者中都引入超时机制。 2PC只有协调者有超时机制超时后才发送回滚指令。 3PC协调者和参与者都有超时机制 协调者超时cancommit、precommit中如果收不到参与者的反馈则协调者向参与者发送终止指令。 参与者超时 precommit阶段中参与者接收不到协调者的指令会自己进行中断cancommit阶段不存在超时因为协调者第一次给参与者发送指令所以在precommit阶段才是参与者第一次遇到超时的情况docommit阶段参与者等不到协调者的指令参与者会自己进行提交。 参与者超时实际上解决了单点故障问题但引发了不一致的问题。 缺陷 在3PC模型中如果资源管理器⽆法及时收到来⾃事务管理器发出的消息那么资源管理 器就会执⾏提交事务的操作⽽不是⼀直持有事务的资源并处于阻塞状态但是这种机制会导 致数据不⼀致的问题。 如果由于⽹络故障等原因导致资源管理器没有及时收到事务管理器发出的Abort消息则资源 管理器会在⼀段时间后提交事务这就导致与其他接收到Abort消息并执⾏了事务回滚操作的资 源管理器的数据不⼀致。 给我的感觉这几种模型好像都是2PC的思想 最终⼀致性分布式事务解决方案 最终⼀致性分布式事务解决⽅案主要⽤于不要求结果数据时刻保持⼀致、允许存在中间状态 但经过⼀段时间后各个节点的数据能够达到⼀致状态的场景 最终⼀致性分布式事务解决⽅案的优点如下。 1性能⽐较⾼这是因为最终⼀致性分布式事务解决⽅案不要求数据时刻保持⼀致不会因 ⻓时间持有事务占⽤的资源⽽消耗过多的性能。 2具备可⽤性。 3适合⾼并发场景。 最终⼀致性分布式事务解决⽅案的缺点如下。 1因为数据存在短暂的不⼀致所以在某个时刻查询出的数据状态可能会不⼀致。 2对于事务⼀致性要求特别⾼的场景不太适⽤。 最终一致性的服务模式 可查询操作 在分布式事务的执⾏过程中如果出现了错误需要明确知道其他操作的处理情况。此时需要其他服务提供可查询的接⼝以保证通过可查询的接⼝获取其他服务的处理情况。 幂等操作 为了保证数据的最终⼀致性系统会提供很多重试操作。如果这些重试操作涉及的⽅法中某些⽅法的实现不具有幂等性则即使重试操作成功了也⽆法保证数据最终⼀致性。 通常有两种实现幂等性的⽅式⼀种是通过业务操作本身实现幂等性另⼀种是通过系统缓存 所有的请求与处理结果当再次检测到相同的请求时直接返回之前缓存的处理结果 TCC 1.Try阶段 1完成所有业务的⼀致性检查。 2预留必要的业务资源并需要与其他操作隔离。 2.Confirm阶段 1此阶段会真正执⾏业务操作。 2因为在Try阶段完成了业务的⼀致性检查所以此阶段不会做任何业务检查。 3只⽤Try阶段预留的业务资源进⾏操作。 4此阶段的操作需要满⾜幂等性。 3.Cancel阶段 1释放Try阶段预留的业务资源。 2此阶段的操作需要满⾜幂等性。 疑问与2PC的区别为什么二者一个是为了实现强一致性另一个是为了实现最终一致性。 https://zhuanlan.zhihu.com/p/419829407 https://zhuanlan.zhihu.com/p/462669898 里面提到了TCC本质上是一个业务层面上的2PC他要求业务在使用TCC模式时必须实现三个接口Try()、Confirm()和Cancel() 对try预留资源的理解使得confirm和cancel幂等。如果没有try只有confirm和cancel则无法实现幂等。我认为冻结库存字段和预增加积分字段应该每笔订单单独一行这样才能实现幂等否则无法实现幂等即使能那try也没意义了。为什么冻结库存为2库存为98呢因为要先减库存不减的话其他事务觉得库存还有很多造成库存的超发。积分的话由于是加的无所谓。 但是我看到书上说实现幂等的方式是在分⽀事务记录表中增加事务的执⾏状态每次执⾏分⽀事务以及Confirm阶段和Cancel阶段的⽅法时都查询此事务的执⾏状态以此判断事务的幂等性。那我就不太理解这个冻结库存的作用了甚至是不理解try的作用。直接减不就行了。cancel再加回来反正都幂等了。 但是https://zhuanlan.zhihu.com/p/488213115这篇文章印证了我的思路的确是靠冻结库存来实现幂等的。 try预留资源使得confrim和cancel幂等cancel只需要让资源加上预留资源然后预留资源清0confirm只需要让预留资源清零。这样就幂等了。 为什么说TCC是最终一致性呢因为try之后其实是存在一个中间态的。 可补偿操作 如果某些数据处于不正常的状态需要通过某种⽅式进⾏业务补偿使数据能够达到最终⼀致性这种因数据不正常⽽进⾏的补偿操作就是可补偿操作服务模式。业务服务对外提供操作数据的接⼝时也需要对外提供补偿业务的接⼝当其他服务调⽤业务服务操作数据的接⼝出现异常时能够通过补偿接⼝进⾏业务补偿操作 可靠消息 可靠消息最终⼀致性分布式事务解决⽅案指的是事务的发起⽅执⾏完本地事务之后发出⼀条 消息事务的参与⽅也就是消息的消费者一定能够接收到这条消息并处理成功。这个⽅案强 调的是只要事务发起⽅将消息发送给事务参与⽅事务参与⽅就⼀定能够执⾏成功事务最终 达到⼀致的状态。 以电商⽀付场景向⽤户发放优惠券为例具体流程为订单服务向RocketMQ发送Half消息 Half消息是RocketMQ中的概念发送成功后RocketMQ会向订单服务响应Half消息发送 成功的状态。接下来订单服务执⾏本地事务修改订单数据的状态并向RocketMQ发送提 交事务或者回滚事务的消息。如果是提交事务的消息则RocketMQ会向优惠券服务投递事务 消息优惠券服务收到消息后会执⾏为⽤户发放优惠券的逻辑。如果是回滚消息则 RocketMQ会删除相应的消息不再向优惠券服务投递对应的事务消息 疑问为啥不在订单事务成功之后直接投递消息而是非要在订单事务执行前先发一个half ⾸先事务发起⽅将消息发送给可靠消息服务这⾥的可靠消息服务可以基于本地数据表实 现也可以基于消息队列中间件实现。然后事务参与⽅从可靠消息服务中接收消息。事务发 起⽅和可靠消息服务之间、可靠消息服务和事务参与⽅之间都是通过⽹络进⾏通信的。由于⽹ 络本身的不稳定性可能会造成分布式事务问题因此在实现上需要引⼊消息确认服务和消 息恢复服务。 消息确认服务会定期检测事务发起⽅业务的执⾏状态和消息库中的数据如果发现事务发起⽅ 业务的执⾏状态与消息库中的数据不⼀致消息确认服务就会同步事务发起⽅的业务数据和消 息库中的数据保证数据⼀致性确保事务发起⽅业务完成本地事务后消息⼀定会发送成功。 消息恢复服务会定期检测事务参与⽅业务的执⾏状态和消息库中的数据如果发现事务参与⽅ 业务的执⾏状态与消息库中的数据不⼀致这⾥的不⼀致通常指的是事务参与⽅消费消息 后执⾏本地事务操作失败导致事务参与⽅本地事务的执⾏状态与消息库中的数据不⼀ 致消息恢复服务就会恢复消息库中消息的状态使消息的状态回滚为事务发起⽅发送消息 成功但未被事务参与⽅消费的状态。 这里的消息库可以是基于数据库表的和业务库绑定在一起也可以是一个单独的消息中间件。 注意事项 1.事务发送⽅本地事务与消息发送的原⼦性问题 1原⼦性问题产⽣的原因 可靠消息最终⼀致性要求事务发起⽅的本地事务与消息发送的操作具有原⼦性也就是事务发 起⽅执⾏本地事务成功后⼀定要将消息发送出去执行本地任务失败自然就不会发消息了。 执⾏本地事务和发送消息要么都成功要么都失败。 2原⼦性问题的解决⽅案 在实际的解决⽅案中可以通过消息确认服务解决本地事务与消息发送的原⼦性问题。 2.事务参与⽅接收消息的可靠性问题 1可靠性问题产⽣的原因 由于服务器宕机、服务崩溃或⽹络异常等原因导致事务参与⽅不能正常接收消息或者接收 消息后处理事务的过程中发⽣异常⽆法将结果正确回传到消息库中。此时就会产⽣可靠性 问题。 2可靠性问题的解决⽅案 可以通过消息恢复服务保证事务参与⽅的可靠性。 3.事务参与⽅接收消息的幂等性问题 1幂等性问题产⽣的原因 在实际场景中由于某种原因可靠消息服务可能会多次向事务参与⽅发送消息如果事务参 与⽅的⽅法不具有幂等性就会造成消息重复消费的问题这就是典型的幂等性问题。 2幂等性问题的解决⽅案 解决⽅案就是事务参与⽅的⽅法实现要具有幂等性只要参数相同⽆论调⽤多少次接⼝或⽅ 法得出的结果都与第⼀次调⽤接⼝或⽅法得出的结果相同。 分布式事务的最终一致性解决方案的核心思想业务方提供幂等操作和查询操作查询事务是否执行成功 分布式事务原理 上面介绍了XA、TCC、可靠消息等分布式事务解决方案下面主要讲述的是这几种方案的原理 XA原理 DTP模型 JTA规范 MySQL XA事务 1在XA START和XA END之间执⾏的是业务SQL语句⽆论是否执⾏成功都应该执⾏ XA END语句。 2在IDLE状态下的事务可以直接执⾏XA COMMIT这⾥我们可以这样理解当只有⼀个资源 管理器的时候可以直接退化成⼀阶段提交。 3只有状态为Failed的时候才能执⾏XA ROLLBACK进⾏XA事务回滚。 4XA事务和⾮XA事务即本地事务是互斥的。例如已经执⾏了XA START命令来开启⼀ 个XA事务则本地事务不会被启动直到XA事务被提交或回滚为⽌。相反的如果已经使⽤ START TRANSACTION启动了⼀个本地事务则XA语句不能被使⽤直到该事务被提交或回 滚为⽌。 XA面临的问题 单点故障⼀旦协调者事务管理器发⽣故障参与者资源管理器会⼀直阻塞下去。尤其在两阶段提交的第 ⼆个阶段如果协调者发⽣故障那么所有的参与者都将处于锁定事务资源的状态中⽆法继 续完成事务操作如果是协调者宕机可以重新选举⼀个协调者但是⽆法解决因为协调者宕 机导致的参与者处于阻塞状态的问题。 数据不一致在Commit阶段当协调者向参与者发送commit请求后发⽣了局部⽹络异常或者在发送 commit请求的过程中协调者发⽣了故障会导致只有⼀部分参与者接收到了commit请求。⽽ 这部分参与者接到commit请求之后就会执⾏commit操作但是其他部分未接到commit请求的 参与者⽆法执⾏事务提交。于是整个分布式系统便出现了数据不⼀致性的现象。 解决思路 为了防止事务管理器宕机后状态丢失可以持久化的全局事务状态来恢复事务管理器的执⾏逻辑通过记录ack表来判断资源管理器是否收到了请求并在重启后向那些没有收到事务管理器指令的资源管理器重新发送指令。资源管理器⻓时间没有收到事务管理器的rollback或者commit语句时会⼀直持有数据库中相关数据的记录锁不仅占⽤系统资源⽽且会使得相关数据记录⽆法被其他业务修改因此资源管理器要有⾃动回滚或者提交的功能。事务管理器集群化部署防止单点故障 已落地的XA规范解决方案Atomikos、Hmily、Narayana TCC分布式事务原理 在某种程度上TCC分布式事务的三个阶段与关系型数据库的事务操作也存在类似的地⽅ 在⼀个分布式或微服务系统中TCC分布式事务的Try阶段是先把多个应⽤中的业务资源锁定预留执⾏分布式事务的资源。同样关系型数据库的DML操作会锁定数据库的⾏记录持有数据库的资源。TCC分布式事务的Confirm操作是在所有涉及分布式事务的应⽤的Try阶段都执⾏成功后确认并提交最终事务状态的操作⽽关系型数据库的Commit操作是在所有的DML操作执⾏成功之后提交事务。TCC分布式事务的Cancel操作是在涉及分布式事务的应⽤没有全部执⾏成功时将已经执⾏成功的应⽤进⾏回滚⽽关系型数据库的回滚操作是在执⾏的DML操作存在异常时执⾏的。关系型数据库中的Commit操作和Rollback操作也是⼀对反向业务操作TCC分布式事务中的Confirm操作和Cancel操作也是⼀对反向业务操作。 TCC核心组成 主业务服务是TCC分布式事务的发起⽅在下单扣减库存的业务场景中订单服务是TCC分布 式事务的发起⽅就是主业务服务。 从业务服务主要负责提供TCC业务操作是整个业务活动的操作⽅。从业务活动必须实现TCC 分布式事务Try、Confirm和Cancel三个阶段的接⼝供主业务服务调⽤。由于在TCC分布式事 务的执⾏过程中Confirm阶段的操作和Cancel阶段的操作可能会被执⾏多次因此需要 Confirm阶段的操作和Cancel阶段的操作保证幂等性。 TCC管理器在整个TCC分布式事务的执⾏过程中管理并控制着整个事务活动包括记录并维 护TCC全局事务的事务状态和每个从业务服务的分⽀事务状态并在参与分布式事务的所有分 ⽀事务的Try阶段都执⾏成功时⾃动调⽤每个分⽀事务的Confirm阶段的操作完成分布式事 务同时会在参与分布式事务的某些分⽀事务执⾏失败时⾃动调⽤分⽀事务的Cancel操作回 滚分布式事务。 在使⽤TCC分布式事务解决分布式场景下的数据⼀致性问题时需要将原本的⼀个事务接⼝改 造成三个不同的事务逻辑也就是前⽂说的Try阶段、Confirm阶段和Cancel阶段。 原本⼀个接⼝的⽅法完成的事务逻辑也要分拆成如下执⾏流程。 1依次执⾏所有参与TCC分布式事务的分⽀事务Try阶段的操作。 2如果每个分⽀事务Try阶段的逻辑都执⾏成功则TCC分布式事务管理器会⾃动调⽤每个分 ⽀事务Confirm阶段的⽅法并执⾏完成整个分布式事务的逻辑。 3如果某个分⽀事务的Try逻辑或者Confirm逻辑的执⾏出现问题则TCC分布式事务管理器 会⾃动感知这些异常信息然后⾃动调⽤每个分⽀事务Cancel阶段的⽅法执⾏Cancel逻辑回 滚之前执⾏的各种操作使数据恢复到执⾏TCC分布式事务之前的状态。 讲得直⽩点就是如果遇到如下情况TCC分布式事务会在Try阶段检查参与分布式事务的各个 服务、数据库和资源是否都能够保证分布式事务正常执⾏能否将执⾏分布式事务的资源预留 出来⽽不是先执⾏业务逻辑操作。 1数据库或其他数据存储服务宕机例如下单扣减库存时库存数据库宕机了。 2某个应⽤服务宕机例如下单扣减库存时库存服务宕机了。 3参与分布式事务的资源不⾜例如下单扣减库存时商品库存不⾜。 如果参与分布式事务的服务都正常执⾏了也就是说数据库或其他数据存储能够正常提供服 务所有参与分布式事务的应⽤服务正常执⾏分布式事务时需要的资源充⾜并且在Try阶段 顺利预留出执⾏分布式事务需要的资源再执⾏TCC分布式事务的Confirm阶段就能够⼤概 率保证分布式事务成功执⾏。 如果在Try阶段某个服务执⾏失败了可能是数据库或者其他数据存储宕机了或者是这个服 务宕机了也有可能是这个服务对应的数据资源不⾜。此时会⾃动执⾏各个服务Cancel阶段 的逻辑回滚Try阶段执⾏的业务逻辑将数据恢复到执⾏分布式事务之前的状态。 其实通过上⾯的逻辑TCC分布式事务还是不能保证执⾏结果数据的⼀致性。这⾥存在着⼀ 个问题那就是如果发⽣了异常情况例如在下单扣减库存的业务场景中订单服务突然宕 机然后重启订单服务TCC分布式事务如何保证之前没有执⾏完的事务继续执⾏呢 这种问题在实际的业务场景中是经常出现的在设计TCC分布式事务框架时必须要考虑这种异 常场景。在执⾏TCC分布式事务时需要记录⼀些分布式事务的活动⽇志将这些活动⽇志存 储到⽂件或者数据库中将分布式事务的各个阶段和每个阶段执⾏的状态全部记录下来。 TCC业务场景举例 在电商业务场景中⼀个典型的业务场景就是⽀付订单。这个场景包含修改订单状态、扣减存库、增加积分、创建出库单等业务这些业务要么全部执⾏成功要么全部执⾏失败必须是⼀个完整的事务。如果不能构成⼀个完整的事务就有可能出现库存未扣减或者超卖的问题也就是如下图的1、2、3、4操作必须全部成功或全部失败 Try阶段 Confirm阶段 Cancel阶段 可靠消息最终⼀致性分布式事务原理 可靠消息最终⼀致性的基本原理是事务发起⽅消息发送者执⾏本地事务成功后发出⼀条消息事务参与⽅消息消费者接收到事务发起⽅发送过来的消息并成功执⾏本地事务。事务发起⽅和事务参与⽅最终的数据能够达到⼀致的状态。 这⾥主要强调如下两点。 1事务发起⽅⼀定能够将消息成功发送出去。 2事务参与⽅⼀定能够成功接收到消息。 ⽹络的不确定性可能会造成事务发起⽅发送消息失败也可能会造成事务参与⽅接收消息失败即造成分布式事务的问题。 本地消息表 为了防⽌在使⽤消息⼀致性⽅案处理分布式事务的过程中出现消息丢失的情况使⽤本地事务保证数据业务操作和消息的⼀致性也就是通过本地事务将业务数据和消息数据分别保存到本地数据库的业务数据表和本地消息表中然后通过定时任务读取本地消息表中的消息数据将消息发送到消息中间件等到消息消费者成功接收到消息后再将本地消息表中的消息删除。这种⽅式实现的分布式事务就是基于本地消息表的可靠消息最终⼀致性分布式事务。 核心思想将分布式系统同步数据的操作通过消息或者日志的形式异步执行并定时重试需要保证幂等性。存放消息的本地消息表和存放数据的业务数据表位于同⼀个数据库中这种设计能够保证使⽤本地事务达到消息和业务数据的一致性 流程 第⼀步事务发起⽅向业务数据表成功写⼊数据后会向本地消息表发送⼀条消息数据因为写业务数据和写消息数据在同⼀个本地事务中所以本地事务会保证这条消息数据⼀定能够正确地写⼊本地消息表。 第⼆步使⽤专⻔的定时任务将本地消息表中的消息写⼊消息中间件如果写⼊成功会将消息从本地消息表中删除。否则继续根据⼀定的规则进⾏重试操作。 第三步如果消息根据⼀定的规则写⼊消息中间件仍然失败可以将失败的消息数据转储到“死信”队列数据表中后续进⾏⼈⼯⼲预以达到事务最终⼀致性的⽬的。 第四步事务参与⽅也就是消息消费者会订阅消息中间件的消息当接收到消息中间件的消息时完成本地的业务逻辑。 第五步事务参与⽅的本地事务执⾏成功则整个分布式事务执⾏成功。否则会根据⼀定的规则进⾏重试。如果仍然不能成功执⾏本地事务则会给事务发起⽅发送⼀条事务执⾏失败的消息以此来通知事务发起⽅进⾏事务回滚。 可靠消息最终⼀致性分布式事务解决⽅案是处理分布式事务的典型⽅案也是业界使⽤⽐较多的⼀种⽅案。基于本地消息表实现的可靠消息⽅案是其中⼀种具体实现⽅式这种实现⽅式有如下明显的优点。 1使⽤消息中间件在多个服务之前传递消息数据在⼀定程度上避免了分布式事务的问题。 2作为业界使⽤⽐较多的⼀种⽅案相对⽐较成熟。 也有⽐较明显的缺点如下所示。 1⽆法保证各个服务节点之间数据的强⼀致性。 2某个时刻可能会查不到提交的最新数据。 3消息表会耦合到业务库中需要额外⼿动处理很多发送消息的逻辑不利于消息数据的扩展。如果消息表中存储了⼤量的消息数据会对操作业务数据的性能造成⼀定的影响。 4消息发送失败时需要重试事务参与⽅需要保证消息的幂等。 5如果消息重试后仍然失败则需要引⼊⼈⼯⼲预机制。 6消息服务与业务服务耦合不利于消息服务的扩展和维护。 7消息服务不能共⽤每次需要实现分布式事务时都需要单独开发消息服务逻辑增加了开发和维护的成本。 独立消息服务 独⽴消息服务是在本地消息表的基础上进⼀步优化将消息处理部分独⽴部署成单独的服务以便消息服务能够单独开发和维护这样就实现了消息服务和业务服务的解耦、消息数据和业务数据的解耦⽅便对消息服务进⾏扩展。 第⼀步事务发起⽅向可靠消息服务成功发送消息后执⾏本地事务。 第⼆步可靠消息服务接收到事务发起⽅发送的消息后将消息存储到消息库中并将消息记录的状态标记为“待发送”并不会⻢上向消息中间件发送消息。同时向事务发起⽅响应消息发送已就绪的状态。 第三步当事务发起⽅的事务执⾏成功时事务发起⽅会向可靠消息服务发送确认消息否则发送取消消息。 第四步当可靠消息服务接收到事务发起⽅发送过来的确认消息时会将消息发送到消息中间件并将消息库中保存的当前消息记录状态标记为“已发送”。如果可靠消息接收到事务发起⽅发送的取消消息会直接将消息库中保存的当前消息删除或者标记为“已删除”。 第五步消息中间件接收到可靠消息服务发送过来的消息时会将消息投递给业务参与⽅业务参与⽅接收到消息后执⾏本地事务并将执⾏结果作为确认消息发送到消息中间件。 第六步消息中间件将确认结果投递到可靠消息服务可靠消息服务接收到确认消息后根据结果状态将消息库中的当前消息记录标记为“已完成”。 第七步如果事务发起⽅向可靠消息服务发送消息失败会触发消息重试机制。如果重试后仍然失败则会由消息确认服务定时校对事务发起⽅的事务状态和消息数据库中当前消息的状态发现状态不⼀致时采⽤⼀定的校对规则进⾏校对。 第⼋步如果可靠消息服务向消息中间件发送消息失败会触发消息重试机制。如果重试后仍然失败则会由消息恢复服务根据⼀定的规则定时恢复消息库中的消息数据。 第七步的消息确认服务确保了事务发起方向可靠消息服务发送消息一定成功第八步的消息恢复服务确保了可靠消息服务向中间件发送消息一定成功中间件确保了向事务参与方投递消息一定成功。 使⽤独⽴消息服务实现分布式事务的优点如下所示。 1消息服务能够独⽴部署、独⽴开发和维护。 2消息服务与业务服务解耦具有更好的扩展性和伸缩性。 3消息表从本地数据库解耦出来使⽤独⽴的数据库存储具有更好的扩展性和伸缩性。 4消息服务可以被多个服务共⽤降低了重复开发消息服务的成本。 5消息数据的可靠性不依赖于消息中间件弱化了对于消息中间件的依赖性。 缺点如下。 1发送⼀次消息需要请求两次接⼝。 2事务发起⽅需要开发⽐较多的事务查询接⼝在⼀定程度上增加了开发成本。 RocketMQ RocketMQ提供的事务监听接口 public interface RocketMQLocalTransactionListener {RocketMQLocalTransactionState executeLocalTransaction(Message msg, Object arg);RocketMQLocalTransactionState checkLocalTransaction(Message msg); }返回的是事务消息的状态 TransactionStatus.CommitTransaction提交事务消息消费者可以消费此消息 TransactionStatus.RollbackTransaction回滚事务它代表该消息将被删除不允许被消费。 TransactionStatus.Unknown 中间状态它代表需要检查消息队列来确定状态 RocketMQ实现事务消息主要分为两个阶段正常事务的发送及提交、事务信息的补偿流程 整体流程为 正常事务发送与提交阶段 生产者发送一个半消息给MQServer半消息是指消费者暂时不能消费的消息 服务端响应消息写入结果半消息发送成功 开始执行本地事务 根据本地事务的执行状态执行Commit或者Rollback操作 事务信息的补偿流程 如果MQServer长时间没收到本地事务的执行状态会向生产者发起一个确认回查的操作请求 生产者收到确认回查请求后检查本地事务的执行状态 根据检查后的结果执行Commit或者Rollback操作 补偿阶段主要是用于解决生产者在发送Commit或者Rollback操作时发生超时或失败的情况checkLocalTransaction的作用 https://zhuanlan.zhihu.com/p/159573084 其实有个很严重的问题RocketMQ似乎无法做到消费者消费失败情况下的回滚 根据chatGPT所言当MQServer向MQ Subscriber commit后如果Subscriber执行失败并返回 TransactionStatus.RollbackRocketMQ 将标记该消息的事务状态为回滚。当消息的事务状态被标记为回滚后RocketMQ 将尝试重新发送该消息以便它再次触发消息消费方的本地事务。如果一直失败此时要么重试后记录要么进入死信队列最终由我们手动保证最终一致性 https://zhuanlan.zhihu.com/p/115553176 也就是说RocketMQ并不能保证分布式事务的一致性消费者失败后无法回滚生产者 总结来看分布式事务的核心要点 通知事务参与方失败要及时通知给事务发起方TCC是通过框架本地消息表是通过数据库 回滚一旦失败事务发起方和参与方要提供回滚接口供事务的回滚操作RocketMQ不满足 幂等由于分布式环境下的网络波动事务的提交操作和回滚操作可能需要反复进行此时需要保证幂等性。 可查询为了防止状态未知阻塞等待需要提供接口展示本地事务的执行状态
http://www.pierceye.com/news/210500/

相关文章:

  • 柳州市诚信体系建设网站网站数据库网络错误
  • 微站网站vps lnmp wordpress
  • 哪里有网站建设哪家好word做网站框架
  • 企业建网站的费用百度掘金入口官网
  • 德洲网站建设wordpress的title设置
  • 苏州企业网站制作服务河北关键词排名推广
  • 营销型集团网站建设镇江网站推广
  • 怎样才能制做免费网站golang 网站开发 开源
  • 哈尔滨做网站哪好免费网站模板
  • 网站怎么做才有效果如何用博客网站做cpa
  • 网站申请书博客系统做网站
  • 灰色行业老域名做网站不收录初学者的网站建设
  • 网站做成微信小程序贵州企业seo
  • 在淘宝做印刷网站怎么办wordpress 主题 edu
  • 成都设计公司网站线上线下一体化营销
  • 网站你懂我意思正能量晚上下载注册公司需要多少钱手续费
  • 在线html网站开发广州网站排名优化公司
  • 如何在免费网站上做推扩自己怎么来建设网站
  • 福安市教育局建设网站做架构图简单的网站
  • 如何快速进行网站开发seo是什么东西
  • 网站建设需要具备哪些学编程多少钱学费
  • 建设工程许可证在那个网站办金融行业网站制作
  • 邢台专业做网站价格信息流广告是什么
  • 网站开发的母的目的和意义.建设购物平台网站
  • 立方米网站建设做淘宝客网站用什么程序好
  • 怎样做网站挣钱建筑资料软件
  • 涿州建设局网站苏州市高新区建设局网站
  • 个人soho要怎么做企业网站成都包装设计公司
  • 网站开发 chrome浏览器崩溃ruhe用dw做网站
  • 全屏网站 图片优化个人网站cms系统