@一贤爱吃土豆

1.引言

  • 分布式环境下,传统的一些技术会失败,如传统的synchronized或者lock锁,以及创建数据库的事务,无法保证ACID,还有定时任务也可能会出现重复执行的问题。

2.分布式锁介绍

  • 由于传统的锁是基于Tomcat服务器内部的,搭建集群之后,导致锁失效,使用分布式锁来处理。
  • 分布式锁介绍:

3.分布式锁解决方案(重点)

  • 单体架构中的锁:
 public class ThreadDemo {public static void main(String[] args) {User user1 = new User("张三");User user2 = new User("李四");MyThread myThread1 = new MyThread(user1);MyThread myThread2 = new MyThread(user2);for(int i =0;i<10;i++){new Thread(myThread1).start();new Thread(myThread2).start();}}}class User {public String name;public User(String name){this.name = name;}public void add() throws InterruptedException {synchronized (User.class){ // this和User.class的区别?Thread.sleep(1000);System.out.println("name:"+name+",threadNmae:"+Thread.currentThread().getName()+"---> add");Thread.sleep(1000);}}}class MyThread implements  Runnable{private User user;public MyThread(User user){this.user = user;}@Overridepublic void run() {try {user.add();} catch (InterruptedException e) {e.printStackTrace();}}}
  • 双重锁机制:
@Override
public List<User> getUserList() {List<User> userList = null;userList= (List<User>) redisTemplate.opsForValue().get("userList");if(userList == null){synchronized (this){if(userList == null){System.out.println("查询数据库");userList = userDao.select(null);// 进行缓存重建redisTemplate.opsForValue().set("userList",userList);redisTemplate.expire("userList",5, TimeUnit.SECONDS); // 5s后失效}}}return userList;
}

3.1:环境搭建

  • 创建SpringBoot
  • 编写抢购的业务
@RestController
public class SecondKillController {//1. 准备商品的库存public static Map<String,Integer> itemStock = new HashMap<>();//2. 准备商品的订单public static Map<String,Integer> itemOrder = new HashMap<>();static{itemStock.put("牙刷",10000);itemOrder.put("牙刷",0);}@GetMapping("/kill")public String kill(String item) throws InterruptedException {//1. 减库存Integer stock = itemStock.get(item);if(stock <= 0){return "商品库存数不足!!!";}Thread.sleep(100);itemStock.put(item,stock - 1);//2. 创建订单Thread.sleep(100);itemOrder.put(item,itemOrder.get(item) + 1);//3. 返回信息return "抢购成功!!!" + item + ": 剩余库存数为 - " + itemStock.get(item) + ",订单数为 - " + itemOrder.get(item);}
}
  • 下载ab压力测试
ab -n 请求数 -c 并发数 访问的路径
  • 测试

3.2:Zookeeper实现分布式锁原理

3.3:Zookeeper实现分布式锁

  • 导入依赖:
<dependency><groupId>org.apache.zookeeper</groupId><artifactId>zookeeper</artifactId><version>3.6.0</version><exclusions><exclusion><artifactId>slf4j-api</artifactId><groupId>org.slf4j</groupId></exclusion><exclusion><artifactId>slf4j-log4j12</artifactId><groupId>org.slf4j</groupId></exclusion><exclusion><artifactId>log4j</artifactId><groupId>log4j</groupId></exclusion></exclusions>
</dependency><dependency><groupId>org.apache.curator</groupId><artifactId>curator-recipes</artifactId><version>4.0.1</version><exclusions><exclusion><artifactId>slf4j-api</artifactId><groupId>org.slf4j</groupId></exclusion></exclusions>
</dependency>
  • 编写配置类:
@Configuration
public class ZkConfig {@Beanpublic CuratorFramework cf(){RetryPolicy retryPolicy = new ExponentialBackoffRetry(3000,2);CuratorFramework curatorFramework = CuratorFrameworkFactory.builder().connectString("192.168.199.109:2181,192.168.199.109:2182,192.168.199.109:2183").retryPolicy(retryPolicy).build();curatorFramework.start();return curatorFramework;}
}
  • 在业务代码中添加分布式锁:
@RequestMapping(value = "/seckill")public String seckill(String gname) throws Exception {//InterProcessMutex基于Zookeeper实现了分布式互斥锁InterProcessMutex interProcessMutex = new InterProcessMutex(cf, "/seckill_lock");// 获取锁//...加锁// interProcessMutex.acquire(); // 没有获取到锁就一直等待if(interProcessMutex.acquire(1, TimeUnit.SECONDS)){ // 等待1s就放弃获取锁资源//限时等待,指定排队多久就放弃获取锁资源// 1.先减库存Integer stock = goodsMap.get(gname);if (stock <= 0) {return "库存不足。。。。";}goodsMap.put(gname, stock - 1);Thread.sleep(100);// 2.加订单数量Integer count = orderMap.get(gname);orderMap.put(gname, count + 1);Thread.sleep(100);// 释放锁interProcessMutex.release();return "抢购成功【" + gname + "】,库存:" + goodsMap.get(gname) + ",订单:" + orderMap.get(gname);}else{return "请稍后再试。。。";}}

3.4:Redis实现分布式锁原理

3.5:Redis实现分布式锁

  • 导入依赖,添加配置文件:
# redis依赖
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
# 配置文件
spring:redis:host: 192.168.199.109port: 6379
  • 编写工具类:
@Component
public class RedisLockUtil {@Autowiredprivate StringRedisTemplate redisTemplate;public boolean lock(String key,String value,int second){return redisTemplate.opsForValue().setIfAbsent(key,value,second, TimeUnit.SECONDS);}public void unlock(String key){redisTemplate.delete(key);}
}
  • 修改业务逻辑代码:
@GetMapping("/redis/kill")
public String redisKill(String item) throws Exception {//...加锁if(lock.lock(item,System.currentTimeMillis() + "",1)){// 业务代码。。。// 释放锁lock.unlock(item);}
}
  • 测试:

4.分布式任务介绍

5.分布式任务解决方案(重点)

5.1:Elastic-Job介绍

官网:http://elasticjob.io/index_zh.html
http://shardingsphere.apache.org/elasticjob/index_zh.html

  • 由当当网基于Quartz + Zookeeper的二次开放产品

    • 基于Zookeeper分布式锁,保证只有一个服务去执行定时任务。
    • 基于Zookeeper实现了注册中心,自动帮助我们去调度指定的服务执行定时任务。
    • 基于Zookeeper实现了注册中心,基于心跳的方式,自动去检测服务的健康情况。

5.2:Elastic-Job实现分布式任务

  • 创建SpringBoot工程:
  • 导入依赖:
<dependency><groupId>com.dangdang</groupId><artifactId>elastic-job-lite-spring</artifactId><version>2.1.5</version>
</dependency>
  • 配置Zookeeper信息:
// 注册中心
@Bean
public CoordinatorRegistryCenter center(){CoordinatorRegistryCenter regCenter = new ZookeeperRegistryCenter(new ZookeeperConfiguration("192.168.199.109:2181,192.168.199.109:2182,192.168.199.109:2183", "elastic-job-demo"));regCenter.init();return regCenter;
}
  • 创建指定的定时任务:
@Component
public class MyElasticJob implements SimpleJob {@Overridepublic void execute(ShardingContext context) {switch (context.getShardingItem()) {case 0:System.out.println("执行0任务!!");break;case 1:System.out.println("执行1任务!!");break;case 2:System.out.println("执行2任务!!");break;// case n: ...}}
}
  • 配置执行的周期,并且开始调度任务:
    javaSimpleJob表示作业的名称,0/5 * * * *设置作业调度的间隔,3表示作业的分片。shardingItemParameters(“0=A,1=B,2=C”)表示设置分片的序列号和个性化参数。
// 执行任务调度信息
@Bean
public SpringJobScheduler scheduler(MyElasticJob job,CoordinatorRegistryCenter center){// 定义作业核心配置JobCoreConfiguration simpleCoreConfig = JobCoreConfiguration.newBuilder("demoSimpleJob", "0/10 * * * * ?", 3).shardingItemParameters("0=A,1=B,2=C").build();// 定义SIMPLE类型配置SimpleJobConfiguration simpleJobConfig = new SimpleJobConfiguration(simpleCoreConfig, MyElasticJob.class.getCanonicalName());// 定义Lite作业根配置LiteJobConfiguration simpleJobRootConfig = LiteJobConfiguration.newBuilder(simpleJobConfig).build();// 定义SpringJobSchedulerSpringJobScheduler scheduler = new SpringJobScheduler(job,center,simpleJobRootConfig);scheduler.init();return scheduler;
}
  • 测试:

6.分布式事务介绍

6.1:分布式事务介绍


6.2:Base理论

  • CAP理论:C:一致性,A:可用性,P:分区容错性。分布式环境下,三者取其二。

    • Eureka:AP,保证了可用性,舍弃了一致性。
    • Zookeeper,Redis:CP(强一致性),每一个节点必须能够找到Master才能对外提供服务,舍弃了可用性。
  • 最终一致性和强一致性:
    • 强一致性:不管在任何时间段查询任何节点数据都必须一直,比如ZK。
    • 最终一致性:在一段时间内查询节点数据不一致,但是经过一段时间查询每个节点,数据必须一致,比如数据库的主从复制。
  • Base理论:
    • Base是Basically Available(基本可用),Soft State (软状态 ),Eventual Consitancy (最终一致性)三个短语缩写,Base理论是对CAP中AP的一个扩展,通过牺牲强一致性来获得可用性,当服务出现故障允许部分不可用,但要担保核心功能可用。允许数据在一段时间内不一致,但最终要达到一致,而满足Base理论事务,称之为柔性事务。
  • Base理论特点,BA:基本可用,S:软状态,E:最终一致性。
    • 基于CAP理论演化而来的,通过牺牲强一致性来获得可用性。
    • 核心思想:允许部分功能不可用但要担保核心功能可用。允许数据在一段时间内不一致,但最终要达到一致。
    • 基本可用:分布式系统在出现故障时,允许损失部分可用功能,保证核心功能可用。如,电商网站交易付款出现问题了,商品依然可以正常浏览。分布式系统中因为一个原因,导致出现了一些问题,允许损失掉部分服务的可用性,保证我核心功能的高可用,注意,这绝不等价于系统不可用
    • 软状态:由于不要求强一致性,所以BASE允许系统中存在中间状态(也叫软状态),这个状态不影响系统可用性,如订单的"支付中"、“数据同步中”等状态,待数据最终一致后状态改为“成功”状态。允许系统之间存在一个中间状态,并不会影响正常的去使用整个系统,允许数据的同步存在延迟。
    • 最终一致:最终一致是指经过一段时间后,所有节点数据都将会达到一致。如订单的"支付中"状态,最终会变为“支付成功”或者"支付失败",使订单状态与实际交易结果达成一致,但需要一定时间的延迟、等待。

7.分布式事务解决方案(重点)

7.1:2PC两段提交

  • 2是指两个阶段,P指的是准备阶段,C指的是提交阶段。

    • 准备阶段,参与者需要开启事务,执行SQL,保证数据库中已经存在相应的数据。参与者会向TransactionManager发送OK。
    • 提交阶段当TransactionManager收到了所有的参与者的通知之后,向所有的参与者发送Commit请求。
  • 问题1:执行的性能是很低的。一般是传统事务的10倍以上。
  • 问题2:TransactionManager是没有超时时间的。
  • 问题3:TransactionManager存在单点故障的问题。

7.2:3PC三段提交

  • 三段提交在二段提交的基础上,引入了超时时间机制,并且在二段提交的基础上,又多了一个步骤,在提交事务之前,再询问一下,数据库的日志信息,是否已经完善。
  • Undo日志记录某数据被修改前的值,可以用来在事务失败时进行rollback;Redo日志记录某数据块被修改后的值,可以用来恢复未写入data file(数据库存放数据的文件)的已成功更新的数据。

什么是undo?----》记录修改前的数据,用于回滚事务
undo日志用于存放数据修改被修改前的值,假设修改 tba 表中 id=2的行数据,把Name=’B’ 修改为Name = ‘B2’ ,那么undo日志就会用来存放Name=’B’的记录,如果这个修改出现异常,可以使用undo日志来实现回滚操作,保证事务的一致性 。
什么是redo?—》记录修改后的数据,用于提交事务后写入数据文件,DB宕机后方便数据恢复。

redo是当数据库对数据做修改的时候,需要把数据页从磁盘读到buffer pool中,然后在buffer pool中进行修改,那么这个时候buffer pool中的数据页就与磁盘上的数据页内容不一致,如果这个时候发生非正常的DB服务重启,数据并没有同步到磁盘文件中,也就是会发生数据丢失,如果这个时候,能够在有一个文件,当buffer pool 中的data page变更结束后,把相应修改记录记录到这个文件,那么当DB服务发生宕机的情况,恢复DB的时候,也可以根据这个文件的记录内容,重新应用到磁盘文件,数据保持一致。

什么是Buffer Pool?
应用系统分层架构,为了加速数据访问,会把最常访问的数据,放在缓存(cache)里,避免每次都去访问数据库。
操作系统,会有缓冲池(buffer pool)机制,避免每次访问磁盘,以加速数据的访问。
MySQL作为一个存储系统,同样具有缓冲池(buffer pool)机制,以避免每次查询数据都进行磁盘IO。
缓冲池(buffer pool)是一种常见的降低磁盘访问的机制;
缓冲池通常以页(page)为单位缓存数据;

7.3:TCC机制

  • TCC(Try,Confirm,Cancel),和你的业务代码切合在一起。

    • Try:尝试去预执行具体业务代码。 下单订ing。。。
    • try成功了:Confirm:再次执行Confirm的代码。
    • try失败了:Cancel:再次执行Cancel的代码。

  • TCC分为三个阶段 :

    • Try阶段是做业务检查(一致性)及资源预留(隔离),此阶段仅是一个初步操作,它和后续的Confirm一起才能真正构成一个完整的业务逻辑。
    • Confirm阶段是做确认提交,Try阶段所有分支事务执行成功后开始执行Confirm。通常情况下,采用TCC则认为Confirm阶段是不会出错的。即 :只要Try成功,Confirm一定成功。若Confirm阶段真的出错了,需引入重试机制或人工处理。
    • Cancel阶段是在业务执行错误需要回滚的状态下执行分支事务的业务取消,预留资源释放。通常情况下,采用TCC则认为Cancel阶段也是一定成功的。若Cancel阶段真的出错了,需引入重试机制或人工处理。
  • 事务模式原理:
    Try Confirm Cancle 如何实现A转账给B的呢?

//尝试方法
function try(){//记录日志todo save A 转出了 100 元 todo save B 转入了 100 元 //执行转账update amount set balacne = balacne-100 where id = 1update amount set balacne = balacne+100 where id = 2
}
//确认方法
function confirm(){//清理日志clean save A 转出了 100 元 clean save B 转出了 100 元
}//取消方法
function cancle(){//加载日志load log Aload log B//退钱update amount set balacne = balacne+100 where id = 1update amount set balacne = balacne-100 where id = 2
}

7.4:MQ分布式事务

  • RabbitMQ在发送消息时,confirm机制,可以保证消息发送到MQ服务中,消费者有手动ack机制,保证消费到MQ中的消息,完成最终一致性。

7.5:LCN实现分布式事务

  • 基于三段提交和TCC实现的:
    TX-LCN分布式事务框架,LCN并不生产事务,LCN只是本地事务的协调工,LCN是一个高性能的分布式事务框架,兼容dubbo、springcloud框架,支持RPC框架拓展,支持各种ORM框架、NoSQL、负载均衡、事务补偿。框架其本身并不操作事务,而是基于对事务的协调从而达到事务一致性的效果。(LCN不生产事务,它只是事务的搬运工…)

    特性一览:
    1、一致性,通过TxManager协调控制与事务补偿机制确保数据一致性
    2、易用性,仅需要在业务方法上添加@TxTransaction注解即可
    3、高可用,项目模块不仅可高可用部署,事务协调器也可集群化部署
    4、扩展性,支持各种RPC框架扩展,支持通讯协议与事务模式扩展

文档地址:https://www.codingapi.com/docs/txlcn-setting-manager/
- 1. 服务发起者 在事务协调者内创建事务组,并将本事务加入事务组
- 2. 事务参与者加入事务组,直到有结束标记出现
- 3. 事务协调者向所有的事务参与者发送询问,是否能够提交!全部提交则事务组提交!有一个回滚标记则事务组回滚!
- 4. 事务组执行操作之后,释放所有锁资源!

LCN原理:

  1. 首先我们的lcn协调者(TM)会和lcn客户端(TC)通过引入的netty一直保持着长连接(持续监听)。
  2. 当请求的发起方(调用方)进入接口业务之前,会通过AOP技术进到@LcnTransaction注解中去LCN协调者那边生成注册一个全局的事务组Id(groupId)。
  3. 当发起方(调用方)通过rpc调用参与方(被调用方)的时候,lcn重写了Feign客户端,会从ThreadLocal中拿到该事务组Id(groupId),并将该事务组Id设置到请求头中。
  4. 参与方(被调用方)在请求头中获取到了这个groupId的时候,lcn会标识该服务为参与方并加入到该事务组,并会被lcn代理数据源,当该服务业务逻辑执行完成后,进行数据源的假关闭,并不会真正的提交或回滚当前服务的事务。
  5. 当发起方执行完全部业务逻辑的时候,如果无异常会告知lcn协调者,lcn协调者再分别告诉该请求链上的所有参与方可以提交了,再进行真正的提交。若发起方调用完参与方后报错了,也会告知lcn协调者,lcn协调者再告知所有的参与方进行真正的回滚操作,这样就解决了分布式事务的问题。
  • 创建一个协调者工程,创建两个服务
  • 协调者:添加依赖
<dependency><groupId>com.codingapi.txlcn</groupId><artifactId>txlcn-tm</artifactId><version>5.0.2.RELEASE</version>
</dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId>
</dependency>
  • 协调者:编写配置文件
server:port: 8080
spring:datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql:///lcn?serverTimezone=UTCusername: rootpassword: rootjpa:hibernate:use-new-id-generator-mappings: falseredis:host: 192.168.199.109port: 6379# 协调的端口号
tx-lcn:manager:port: 8070
  • 协调者:添加注解,开启分布式事务
 @EnableTransactionManagerServer
  • 协调者:准备表:
# 创建表
CREATE TABLE `t_tx_exception`  (`id` bigint(20) NOT NULL AUTO_INCREMENT,`group_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,`unit_id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,`mod_id` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,`transaction_state` tinyint(4) NULL DEFAULT NULL,`registrar` tinyint(4) NULL DEFAULT NULL,`remark` varchar(4096) NULL DEFAULT  NULL,`ex_state` tinyint(4) NULL DEFAULT NULL COMMENT '0 未解决 1已解决',`create_time` datetime(0) NULL DEFAULT NULL,PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
  • 服务: 添加依赖:
<dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.1.2</version>
</dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId>
</dependency><dependency><groupId>com.codingapi.txlcn</groupId><artifactId>txlcn-tc</artifactId><version>5.0.2.RELEASE</version>
</dependency><dependency><groupId>com.codingapi.txlcn</groupId><artifactId>txlcn-txmsg-netty</artifactId><version>5.0.2.RELEASE</version>
</dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId>
</dependency>
  • Order服务: 编写配置文件:
server:port: 8081spring:datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql:///lcn?serverTimezone=UTCusername: rootpassword: rootjpa:hibernate:use-new-id-generator-mappings: false
tx-lcn:client:manager-address: localhost:8070
  • Order服务:启动类添加注解
 @EnableDistributedTransaction@MapperScan("com.qf.mapper")
  • Order服务:controller
@RestController
public class OrderController {@Autowiredprivate OrderService orderService;@GetMapping("/create")public String create(){orderService.createOrder();return "创建订单成功";}
}
  • Order服务:Service层添加注解,启动类需要配置@Bean创建RestTemplate对象
@Service
public class OrderServiceImpl implements OrderService {@Autowiredprivate RestTemplate restTemplate;@Autowiredprivate OrderMapper orderMapper;@Override@Transactional@LcnTransactionpublic void createOrder() {//减库存restTemplate.getForObject("http://localhost:8082/item", String.class);//        int i=1/0;//创建订单orderMapper.createOrder();}
}
  • Order服务:mapper接口
public interface OrderMapper {@Insert("insert into t_order(id,name,money)values(1,'张三疯',22)")void createOrder();
}
  • item服务:配置文件
server:port: 8082
spring:datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql:///lcn?serverTimezone=UTCusername: rootpassword: 123
tx-lcn:client:manager-address: localhost:8070
  • item服务:启动类添加注解
@EnableDistributedTransaction
@MapperScan(basePackages = "com.qf.mapper")
  • item服务:Controller层
@RestController
public class ItemController {@Autowiredprivate ItemService itemService;@GetMapping("/item")public String item(){itemService.updateStock();return null;}
}
  • item服务:Service层
@Service
public class ItemServiceImpl implements ItemService {@Autowiredprivate ItemMapper itemMapper;@Override@Transactional@LcnTransactionpublic void updateStock() {itemMapper.updateStock();}
}
  • item服务:mapper层
public interface ItemMapper {@Update("update t_item set stock = stock-1 where id = 1")void updateStock();
}
  • 测试,异常后,事务回滚

65.分布式处理方案相关推荐

  1. 企业实战之分布式锁方案一步步的演变历程!,Java数据库索引面试题

    这个方案是很多公司都这么用的,那我们调整一下代码 需要考虑到一些业务异常,需要把锁释放掉,加上try/finally,这个千万不要忘了 当是还是有一些问题,就是如果加锁成功后,业务没有完成.突然断电或 ...

  2. 面试必备的分布式事物方案

    四月初,去面试了本市的一家之前在做办公室无人货架的公司,虽然他们现在在面临着转型,但是对于我这种想从传统企业往互联网行业走的孩子来说,还是比较有吸引力的. 在面试过程中就提到了分布式事物问题.我又一次 ...

  3. Memcache分布式部署方案

    前言 应该是很久之前,我开始研究Memcache,写了一系列的学习心得,比如<Discuz!的Memcache缓存实现>等.后面的好几十条回复也让这篇文章成为了此博客中颇受关注的一员. 同 ...

  4. 第02课:主流分布式缓存方案的解读及比较

    分布式缓存一般被定义为一个数据集合,它将数据分布(或分区)于任意数目的集群节点上.集群中的一个具体节点负责缓存中的一部分数据,整体对外提供统一的访问接口.分布式缓存一般基于冗余备份机制实现数据高可用, ...

  5. 企业级分布式批处理方案

    前言 先来讲解下在分布式任务调度SchedulerX中的MapReduce模型是什么,从字面来理解MapReduce就是一个任务切分多个子任务并行处理.在简单单机场景下就是开启多线程来同时处理一个大任 ...

  6. 分布式系统原理-分布式事务方案那么多,到底该选哪一个

    分布式系统原理系列目录 分布式系统的麻烦 副本与一致性 为什么需要一个分布式共识算法 世界上只有一种共识算法,那就是Paxos CAP定理,说起来一句话,实际坑不少 BASE,可用性高于强一致性 分布 ...

  7. Go 开源说第十六期预告:跨语言的分布式事务方案——DTM

    点击蓝字 关注我们 写在前面 GoCN开源说是GoCN推出的一档分享Go开源好项目的直播栏目,通过开源说希望能够帮助到开源作者们实现以下目标: 第一是去推广他们的开源项目 第二说说背后的设计原理和理念 ...

  8. 一种比较直观的分布式架构方案

    发信人: diogin (JingCheng·离别@六月), 板面: WebDev                            ? 标  题: Re: 一种比较直观的分布式架构方案 发信站: ...

  9. 真牛逼!我司用了7年的分布式锁方案...

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 提到数据一致性.操作原子性,诸如此类的一些与并发有关的词汇时不知道 ...

最新文章

  1. 2021-2027年中国透明导电膜玻璃行业市场研究及前瞻分析报告
  2. C++中类的静态成员变量和静态成员函数
  3. spring17:Bean的生命始末标签@PostConstruct,@PreDestroy和改变作用范围的@Scope标签
  4. c语言ascii图形输出,C语言实例10——有关ASCII图形的输出
  5. 显卡在电脑什么位置_显卡是什么?电脑显卡有什么用?——《作用篇》
  6. 转:如何求出grid图像每个cell对应的x,y坐标?
  7. 修改属性页CPropertyPage标题
  8. AutoMapper 使用实践
  9. php 建议查询DNS解析状态工具
  10. OpenResty实现LNMP的缓存前移(到达nginx前端层面)
  11. java socket 组包_关于socket 分包和组包
  12. linux windows双系统安装教程
  13. 标准商业计划书大纲模版
  14. TensorFlow2学习笔记:3、鸢尾花数据集载入
  15. tof传感器有什么用 tof传感器原理「手机百科」
  16. python程序设计基础课后习题答案(电子版,可复制)
  17. win7计算机默认用户名,win7系统任务管理器中用户名没有显示的解决方法
  18. nginx 配置https 负载均衡
  19. python pip 安装包失败问题,下载缓慢问题
  20. 孝经白话:五刑章第十一

热门文章

  1. 爱奇艺qsv视频下载后怎么转换为3gp格式 1
  2. itop4412开发板显示ADB驱动成功方法
  3. 2023年软考备考,系统分析师知识点速记,速看
  4. 图解IFRS9 金融工具(9) 套期会计概述
  5. MTK wifi AP mode/wifi 热点模式的相关问题
  6. Windows Server 2012 R2单域及域森林环境搭建
  7. win11 中Windows安全中心消失等引出的问题合集解决
  8. 2018年一季度总结
  9. Android 音频录音与播放
  10. 超强视频剪切工具Boilsoft Video Splitter7.02.2中文免费绿色版