分布式事务和分布式锁是什么

本文详细介绍了分布式锁的概念、必要性以及三种常见的实现方式:基于数据库、Redis和ZooKeeper。分布式锁用于解决多服务间的并发控制,确保数据的一致性。数据库实现依赖于唯一索引和行数据删除,Redis实现利用SETNX、EXPIRE命令保证锁的获取与释放,而ZooKeeper通过创建临时节点实现锁的获取与释放。每种方式都有其优缺点,适用于不同的场景。

分布式事务(简单来说就是服务调用服务)

	因为分布式的出现,事务A和事务B是在不同服务器上不同连接,想要同时回滚
	那么就得使用分布式事务

分布式事务和事务的传播行为的区别

	事务的传播行为是不涉及分布式问题的,是在一个微服务中的,只是涉及事务A,嵌套在B方法中,对其整体事务的考虑

分布式锁考虑的问题(分布式锁是分布式 和 集群 两方面的应用场景)(分布式锁,有2个问题要解决,1.机器之间的共享数据可见同步,2.在上锁 解决并发问题)

简单来说 单机,可以通过锁来控制,一个方法只能同时 被一个线程执行,都是多机集群后,首先多机的数据内存可见性要解决,解决后,还要保证 一个时刻 只能有 一个线程 去 获得 仅一个机器的锁,即当一个机器的 锁被拿到,其他机器的 那个方法不能被任何线程去拿锁执行
1.分布式锁适用的场景 是 分布式 + 集群
2.以前没有集群的时候,我们对于一个服务,对并发问题的考虑是加锁

	3.但是有了集群后,要考虑
		1.数据的同步性问题(先得将不同应用的 共享数据 数据一致,因为不同内存的共享数据
		  不存在 可见性,共享性)
		  
		2.数据同步性解决后,那么这时 不同应用的 共享数据,才一致,这时再考虑,
		  共享变量进行多线程同步访问的 线程安全问题

什么是分布式锁?实现分布式锁的三种方式

在很多场景中,我们为了保证数据的最终一致性,需要很多的技术方案来支持,比如分布式事务、分布式锁等。那具体什么是分布式锁,分布式锁应用在哪些业务场景、如何来实现分布式锁呢?

一 为什么要使用分布式锁

我们在开发应用的时候,如果需要对某 一个共享变量进行多线程同步访问的时候,可以使用我们学到的锁进行处理,并且可以完美的运行,毫无Bug!

注意这是单机应用,后来业务发展,需要做集群,一个应用需要部署到几台机器上然后做负载均衡,大致如下图:

上图可以看到,变量A存在三个服务器内存中(这个变量A主要体现是在一个类中的一个成员变量,是一个有状态的对象),如果不加任何控制的话,变量A同时都会在分配一块内存,三个请求发过来同时对这个变量操作,显然结果是不对的!即使不是同时发过来,三个请求分别操作三个不同内存区域的数据,变量A之间不存在共享,也不具有可见性,处理的结果也是不对的!

如果我们业务中确实存在这个场景的话,我们就需要一种方法解决这个问题!

为了保证一个方法或属性在高并发情况下的同一时间只能被同一个线程执行,在传统单体应用单机部署的情况下,可以使用并发处理相关的功能进行互斥控制。但是,随着业务发展的需要,原单体单机部署的系统被演化成分布式集群系统后,由于分布式系统多线程、多进程并且分布在不同机器上,这将使原单机部署情况下的并发控制锁策略失效,单纯的应用并不能提供分布式锁的能力。为了解决这个问题就需要一种跨机器的互斥机制来控制共享资源的访问,这就是分布式锁要解决的问题!

二、分布式锁应该具备哪些条件

在分析分布式锁的三种实现方式之前,先了解一下分布式锁应该具备哪些条件:

1、在分布式系统环境下,一个方法在同一时间只能被一个机器的一个线程执行;
2、高可用的获取锁与释放锁;
3、高性能的获取锁与释放锁;
4、具备可重入特性;
5、具备锁失效机制,防止死锁;
6、具备非阻塞锁特性,即没有获取到锁将直接返回获取锁失败。

三、分布式锁的三种实现方式

目前几乎很多大型网站及应用都是分布式部署的,分布式场景中的数据一致性问题一直是一个比较重要的话题。分布式的CAP理论告诉我们“任何一个分布式系统都无法同时满足一致性(Consistency)、可用性(Availability)和分区容错性(Partition tolerance),最多只能同时满足两项。”所以,很多系统在设计之初就要对这三者做出取舍。在互联网领域的绝大多数的场景中,都需要牺牲强一致性来换取系统的高可用性,系统往往只需要保证“最终一致性”,只要这个最终时间是在用户可以接受的范围内即可。

在很多场景中,我们为了保证数据的最终一致性,需要很多的技术方案来支持,比如分布式事务、分布式锁等。有的时候,我们需要保证一个方法在同一时间内只能被同一个线程执行。

基于数据库实现分布式锁;
基于缓存(Redis等)实现分布式锁;
基于Zookeeper实现分布式锁;

四、基于数据库的实现方式

基于数据库的实现方式的核心思想是:在数据库中创建一个表,表中包含方法名等字段,并在方法名字段上创建唯一索引,想要执行某个方法,就使用这个方法名向表中插入数据,成功插入则获取锁,执行完成后删除对应的行数据释放锁。

(1)创建一个表:

	DROP TABLE IF EXISTS `method_lock`;
	CREATE TABLE `method_lock` (
	  `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
	  `method_name` varchar(64) NOT NULL COMMENT '锁定的方法名',
	  `desc` varchar(255) NOT NULL COMMENT '备注信息',
	  `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
	  PRIMARY KEY (`id`),
	  UNIQUE KEY `uidx_method_name` (`method_name`) USING BTREE
	) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COMMENT='锁定中的方法';

在这里插入图片描述

(2)想要执行某个方法,就使用这个方法名向表中插入数据:

INSERT INTO method_lock (method_name, desc) VALUES ('methodName', '测试的methodName');

因为我们对method_name做了唯一性约束,这里如果有多个请求同时提交到数据库的话,数据库会保证只有一个操作可以成功,那么我们就可以认为操作成功的那个线程获得了该方法的锁,可以执行方法体内容。

(3)成功插入则获取锁,执行完成后删除对应的行数据释放锁:

delete from method_lock where method_name ='methodName';

注意:这只是使用基于数据库的一种方法,使用数据库实现分布式锁还有很多其他的玩法!

使用基于数据库的这种实现方式很简单,但是对于分布式锁应该具备的条件来说,它有一些问题需要解决及优化:

1、因为是基于数据库实现的,数据库的可用性和性能将直接影响分布式锁的可用性及性能,所以,数据库需要双机部署、数据同步、主备切换;

2、不具备可重入的特性,因为同一个线程在释放锁之前,行数据一直存在,无法再次成功插入数据,所以,需要在表中新增一列,用于记录当前获取到锁的机器和线程信息,在再次获取锁的时候,先查询表中机器和线程信息是否和当前机器和线程相同,若相同则直接获取锁;

3、没有锁失效机制,因为有可能出现成功插入数据后,服务器宕机了,对应的数据没有被删除,当服务恢复后一直获取不到锁,所以,需要在表中新增一列,用于记录失效时间,并且需要有定时任务清除这些失效的数据;

4、不具备阻塞锁特性,获取不到锁直接返回失败,所以需要优化获取逻辑,循环多次去获取。

5、在实施的过程中会遇到各种不同的问题,为了解决这些问题,实现方式将会越来越复杂;依赖数据库需要一定的资源开销,性能问题需要考虑。

五、基于Redis的实现方式

1、选用Redis实现分布式锁原因:

(1)Redis有很高的性能;
(2)Redis命令对此支持较好,实现起来比较方便

2、使用命令介绍:

(1)SETNX

SETNX key val:当且仅当key不存在时,set一个key为val的字符串,返回1;若key存在,则什么都不做,返回0。

(2)expire

expire key timeout:为key设置一个超时时间,单位为second,超过这个时间锁会自动释放,避免死锁。

(3)delete

delete key:删除key

在使用Redis实现分布式锁的时候,主要就会使用到这三个命令。

3、实现思想:

(1)获取锁的时候,使用setnx加锁,并使用expire命令为锁添加一个超时时间,超过该时间则自动释放锁,锁的value值为一个随机生成的UUID,通过此在释放锁的时候进行判断。

(2)获取锁的时候还设置一个获取的超时时间,若超过这个时间则放弃获取锁。

(3)释放锁的时候,通过UUID判断是不是该锁,若是该锁,则执行delete进行锁释放。

4、 分布式锁的简单实现代码:

#连接redis
redis_client = redis.Redis(host="localhost",
                           port=6379,
                           password=password,
                           db=10)

#获取一个锁
lock_name:锁定名称
acquire_time: 客户端等待获取锁的时间
time_out: 锁的超时时间
def acquire_lock(lock_name, acquire_time=10, time_out=10):
    """获取一个分布式锁"""
    identifier = str(uuid.uuid4())
    end = time.time() + acquire_time
    lock = "string:lock:" + lock_name
    while time.time() < end:
        if redis_client.setnx(lock, identifier):
            # 给锁设置超时时间, 防止进程崩溃导致其他进程无法获取锁
            redis_client.expire(lock, time_out)
            return identifier
        elif not redis_client.ttl(lock):
            redis_client.expire(lock, time_out)
        time.sleep(0.001)
    return False

#释放一个锁
def release_lock(lock_name, identifier):
    """通用的锁释放函数"""
    lock = "string:lock:" + lock_name
    pip = redis_client.pipeline(True)
    while True:
        try:
            pip.watch(lock)
            lock_value = redis_client.get(lock)
            if not lock_value:
                return True

            if lock_value.decode() == identifier:
                pip.multi()
                pip.delete(lock)
                pip.execute()
                return True
            pip.unwatch()
            break
        except redis.excetions.WacthcError:
            pass
    return False

5、测试刚才实现的分布式锁

例子中使用50个线程模拟秒杀一个商品,使用–运算符来实现商品减少,从结果有序性就可以看出是否为加锁状态。

def seckill():
    identifier=acquire_lock('resource')
    print(Thread.getName(),"获得了锁")
    release_lock('resource',identifier)


for i in range(50):
    t = Thread(target=seckill)
    t.start()

六、基于ZooKeeper的实现方式

ZooKeeper是一个为分布式应用提供一致性服务的开源组件,它内部是一个分层的文件系统目录树结构,规定同一个目录下只能有一个唯一文件名。基于ZooKeeper实现分布式锁的步骤如下:

(1)创建一个目录mylock;
(2)线程A想获取锁就在mylock目录下创建临时顺序节点;
(3)获取mylock目录下所有的子节点,然后获取比自己小的兄弟节点,如果不存在,则说明当前线程顺序号最小,获得锁;
(4)线程B获取所有节点,判断自己不是最小节点,设置监听比自己次小的节点;
(5)线程A处理完,删除自己的节点,线程B监听到变更事件,判断自己是不是最小的节点,如果是则获得锁。

这里推荐一个Apache的开源库Curator,它是一个ZooKeeper客户端,Curator提供的InterProcessMutex是分布式锁的实现,acquire方法用于获取锁,release方法用于释放锁。

优点:具备高可用、可重入、阻塞锁特性,可解决失效死锁问题。

缺点:因为需要频繁的创建和删除节点,性能上不如Redis方式。

七、总结


上面的三种实现方式,没有在所有场合都是完美的,所以,应根据不同的应用场景选择最适合的实现方式。

在分布式环境中,对资源进行上锁有时候是很重要的,比如抢购某一资源,这时候使用分布式锁就可以很好地控制资源。
当然,在具体使用中,还需要考虑很多因素,比如超时时间的选取,获取锁时间的选取对并发量都有很大的影响,上述实现的分布式锁也只是一种简单的实现,主要是一种思想

<think>好的,我现在需要回答用户关于分布式事务分布式锁区别及应用场景的问题。首先,我需要回顾之前提供的引用内容,确保准确理解两者的定义不同之处。 根据引用[1],分布式事务主要关注跨服务操作的ACID属性,保证所有操作要么全部成功,要么全部失败。而分布式锁则是解决并发访问共享资源的问题,确保互斥访问。这里的关键点在于目标应用场景的不同。 接下来,引用[3]提到,分布式事务处理多个数据源之间的事务一致性,而分布式锁处理并发控制。实现机制上,事务需要协调多个服务,使用复杂的协议,而锁更关注资源的获取与释放。复杂度方面,事务更复杂,涉及更多故障处理。 然后,引用[2]指出分布式系统的CAP理论,强调分布式事务锁都是为了数据一致性,但侧重点不同。分布式锁用于同一时间只能有一个线程执行某段代码,而事务用于跨服务的数据操作。 现在,我需要将区别分点列出,确保结构清晰。应用场景部分需要具体例子,比如事务用于订单库存的跨服务操作,锁用于秒杀中的库存扣减。 还要注意用户提到的格式要求,行内公式用$...$,独立公式单独成段,但当前问题可能不涉及数学公式,所以可能不需要使用。但如果有涉及的地方,比如算法中的时间戳,可能需要用行内公式。 需要验证是否有遗漏点,比如引用[4]中的Redis锁实现,这可以作为分布式锁的一个例子,但用户问题主要问区别应用场景,所以可能不需要深入代码细节,但可以简要提到实现方式的不同。 最后,生成相关问题,参考用户示例中的结构,问题应围绕区别、应用场景、实现机制等展开。</think>### 分布式事务分布式锁区别及应用场景 #### 一、核心区别 1. **目标不同** - **分布式事务**:保证跨多个服务或数据库操作的**原子性**(ACID属性),确保所有操作要么全部成功,要么全部回滚[^1][^3]。例如,订单支付与库存扣减必须同时成功或失败。 - **分布式锁**:解决**并发控制**问题,确保同一时刻只有一个进程/服务能访问共享资源(如修改库存、生成唯一ID)[^2][^3]。 2. **实现机制不同** - **分布式事务**:需通过协调者(如Seata、XA协议)管理参与者的事务状态,使用**两阶段提交(2PC)**、**三阶段提交(3PC)**等复杂协议[^1][^3]。 - **分布式锁**:依赖锁的获取与释放机制,如Redis的`SETNX`命令、ZooKeeper的临时节点、数据库乐观锁等[^4]。例如,Redis锁的实现代码片段: ```java // Redis锁示例(基于引用[4]) public boolean requireLock(String key, String value) { if (redisTemplate.opsForValue().setIfAbsent(key, value)) { return true; } // ... 处理锁续期或超时逻辑 } ``` 3. **复杂度差异** - 分布式事务需处理**网络分区、超时、参与者故障**等复杂场景,需牺牲部分可用性(CAP理论中的CP)[^2][^3]。 - 分布式锁的实现相对简单,核心是**互斥性****容错性**,但对锁超时、续期机制要求严格[^4]。 --- #### 二、应用场景 | **场景** | **分布式事务** | **分布式锁** | |------------------------|------------------------------------------|---------------------------------------| | **典型用例** | 跨服务转账、订单与库存联动 | 秒杀库存扣减、全局ID生成 | | **一致性要求** | 强一致性(最终一致性可选,如TCC模式) | 最终一致性(通过锁保证短时互斥) | | **技术选型** | Seata、XA协议、Saga模式 | Redis锁、ZooKeeper锁、数据库乐观锁 | | **性能影响** | 高(协调成本高) | 低(仅资源竞争时存在开销) | --- #### 三、关键总结 - **分布式事务**适用于需要**跨服务数据强一致**的场景,如金融交易。 - **分布式锁**适用于需要**短时资源互斥访问**的场景,如防止重复提交、避免缓存击穿。 - 实践中,两者可能结合使用。例如,在扣减库存时用分布式锁,而订单创建与支付使用分布式事务[^3]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值