1. 文件锁
文件锁是保持文件同步的一种手段,当多个用户同时操作同一个文件时,文件锁可以保证数据不发生冲突。NFSv2和NFSv3依靠NLM协议实现文件锁,NFSv4本身实现了文件锁,不需要NLM协同工作。NFS中的文件锁既可以加在客户端,也可以加在服务器端。如果客户端挂载NFS文件系统时使用了选项nolock,表示在客户端加锁。这种情况下可以保证同一个客户端的多个进程访问同一个文件的过程不发生冲突,但是不同客户端访问同一个文件时还可能发生冲突,因为文件锁加在了客户端,其他客户端不知道这个文件锁的存在,客户端加锁时和其他文件系统的加锁过程没有什么区别。如果客户端挂载NFS文件系统时使用了选项lock,表示在服务器端加锁,这样所有的客户端都可以检查服务器端是否存在文件锁,因此所有客户端访问同一个文件时都不会发生冲突。
1.1 简介
NFS4实现“租赁锁”。每个锁拥有一样的“租赁期”。客户端的读写操作将刷新“租赁期”。租赁期到期后,锁将被服务器释放。
1.2 相关概念
- Client :客户端是访问NFS服务器的资源的实体
- Clientid :客户端唯一标识,用于认证;采用verifier 和 Id构成
+ verifier :一个 防重放攻击的随机值,其作用是确保请求的唯一性和时效性;verifier 由两部分组成:1、stamp:一个 32 位随机数 或 时间戳;2、machine_name:客户端的主机名
+ Id:Id串一般由如下几部分组成:客户端地址(ip+port),服务器地址(ip+port), MAC等机器唯一的信息 - Server :负责协调客户端访问文件系统的实体
- Stateid:16字节变量用于标识特定文件状态(打开和锁定)。由服务器分配,根据stateid可找到相应的状态信息表。用于会话和操作状态,组成:
+ 客户端 ID(Client ID):客户端在建立会话时,服务器分配的唯一标识符(如 CL<随机数>)。
+ 会话 ID(Session ID):会话建立时服务器生成的唯一 ID。
+ 操作类型(Op Type):标识 Stateid 对应的操作类型(如打开文件、锁定等)。
+ 随机数或时间戳:确保唯一性,防止重放攻击。
1.3 关键流程
- 客户端认证
客户端启动 --> 发送nfs_client_id4(verifier和id串) ,SETCLIENTID
–> 服务器分配clientid --> 客户端发送clientid 给服务器端,二次确认SETCLIENTID_CONFIRM --> 认证成功(建立server记录) - 状态操作
客户端用clientid为特定lock_owner发起锁申请 --> 服务器锁定文件,并发回stateid (建立状态表)–> 客户端用stateid进行文件读写–> 撤销状态,即撤销stateid
参考:https://www.cnblogs.com/zhenjing/archive/2011/05/15/NFS4_lock.html
2. 分布式锁
分布式锁是一种在分布式系统中,用于解决多个节点对共享资源并发访问问题的机制。它确保在分布式环境下,同一时刻只有一个节点能够访问或修改某个共享资源,从而避免数据不一致或竞争条件的发生。
2.1 分布式锁特性
-
互斥性:同一时刻,只能有一个节点获取到锁,其他节点必须等待锁被释放后才能获取。
-
安全性:锁的设置必须包含过期时间,以防止节点崩溃导致锁无法释放,造成死锁。即使持有锁的节点崩溃,锁也能够在一定时间后自动释放,允许其他节点获取锁。
-
对称性:加锁和解锁必须由同一个节点执行,即“谁加的锁,谁释放锁”。这通常通过在加锁时记录节点的唯一标识(如节点ID)来实现。
-
可靠性:分布式锁的实现需要具备异常处理能力和容灾能力,以确保在分布式系统中的高可用性。
2.2 实现方式
-
基于数据库的分布式锁
- 原理:通过在数据库中创建一个具有唯一约束的锁表,加锁时插入一行记录,释放锁时删除这行记录。
- 优点:简单易用,易于理解。
- 缺点:性能受数据库性能限制,加锁和解锁操作需要访问数据库,开销较大。
-
基于Redis的分布式锁
- 原理:利用Redis的键值存储机制和原子操作命令(如SETNX、EXPIRE)来实现。SETNX命令用于在键不存在时设置值,从而实现加锁;EXPIRE命令用于设置键的过期时间,防止死锁。解锁时,需要判断当前持有锁的节点是否仍然有效,通常使用Lua脚本确保操作的原子性。
- 优点:性能高,适合高并发场景;Redis支持集群模式,具有较好的扩展性。
- 缺点:单点故障问题,如果Redis节点挂掉,整个分布式锁将失效;误解锁问题,需要确保解锁操作的原子性。
-
基于ZooKeeper的分布式锁
- 原理:利用ZooKeeper的临时顺序节点和监听机制来实现。每个节点在ZooKeeper上创建一个临时顺序节点,ZooKeeper根据节点的创建顺序进行排序。序号最小的节点获得锁,其他节点监听比自己序号小的节点的变化。当持有锁的节点释放锁(即删除临时节点)时,监听到的节点将获取锁。
- 优点:解决了单点故障问题,ZooKeeper集群保证了高可用性和强一致性;提供了丰富的节点管理和监听机制。
- 缺点:性能相对较低,尤其是在高并发场景下可能存在性能瓶颈;ZooKeeper的部署和管理相对复杂。
-
基于消息队列的分布式锁
- 原理:通过在消息队列中发布锁请求和释放请求来实现。消息队列的先进先出特性保证了锁的获取顺序。
- 优点:适用于高并发场景,能够处理大量的锁请求。
- 缺点:需要消息队列的支持,增加了系统的复杂性;消息队列的性能和可靠性对分布式锁的实现有重要影响。
2.3 应用场景
- 解决缓存雪崩问题:在分布式系统中,当大量的请求同时涌入系统,并且缓存中的一些数据失效时,可能导致数据库宕机。利用分布式锁控制热点数据的更新,可以避免缓存雪崩问题。
- 控制资源访问:分布式锁可以用于控制分布式环境中的资源访问,如文件系统、数据库连接、网站接口等,避免资源竞争问题。
- 协调分布式事务:在分布式事务中,多个节点需要协调对共享资源的操作。分布式锁可以用来确保这些操作按照正确的顺序执行,从而保证事务的一致性。
2.4 使用注意
选择合适的分布式锁实现方式:根据系统的需求和特点选择合适的分布式锁实现方式。例如,对于性能要求较高的系统,可以选择基于Redis的分布式锁;对于一致性要求较高的系统,可以选择基于ZooKeeper的分布式锁。