最近工作中遇到了一个非常棘手有趣的故障. 让我结结实实通了两宵,睡了一个周末才缓过来.不过这篇文章讲的并不是这个故障的原因, 而是修复故障所带来的衍生问题和思考.

在升级解决这些机器的过程中, 机器被挂起5~6s.在这段时间中,机器无法响应心跳,并且更重要的是,机器上的时钟发生了滞后, 这对于严格依赖时间偏移的分布式应用的影响是巨大的.也说明了在实际生产中完全有可能出现这个级别的时间紊乱.

对于使用Raft/Paxos的分布式应用,当然这些一致性协议的细节的不同实现可能依赖时间(比如确认当前是否仍是Leader可以通过距离上次心跳的时间来判断),但事实上几乎不受影响.

背景

我总结了下生产中实际使用的分布式锁, 大概有如下几类

单机Redis分布式锁

不同的客户端通过生成随机字符串对制定的Key进行set if not exists + ttl 操作来进行抢占,通过key的TTL时间来决定持有锁的时间. 然后通过LUA脚本执行事务操作进行Compare and delete进行释放锁.

其中的TTL和本机时钟有关.

集群Redis分布式锁

大名鼎鼎Redis作者给出的基于Redis cluster的分布式锁实现,命名为Redlock, 假如一个集群中有5台Redis实例, 那么分别对这5台机器进行上述的单机分布式锁的操作, 操作完成之后判断有几台机器操作成功, 如果成功数量>=3, 则判断所有获取锁的操作时间和TTL的差距, 如果操作时间显著小于TTL, 则认为成功获取锁, 如果不成功, 则反向操作进行CAS释放锁.

Redis作者是超级大牛, 有Redis Cluster这样的作品, 是绝对的分布式专家, 可是给出的这个Redlock算法, 有很多遐想的空间...

数据库锁

  1. 利用insert进行唯一id插入
  2. 利用事务中的select for update 行锁进行操作

这两种实现我只能说非常好用, 尤其是在公司里, 单单是因为分布式锁请其他团队维护一个zookeeper或者redis不是一件很舒心的事情, 唯一的好处是依赖出了问题可以甩锅(逃.

但是DB是每个应用都会用的,另外, 使用DB还有一个额外的优点.后面讲资源服务器的时候会说到.

缺点是, 这两个用起来都比较麻烦, 使用insert的方法需要后台线程进行时间的更新和清理. 避免宕机后无法释放锁.意味着你还需要使用一个线程安全的数据结构和两个线程不停更新db.

使用行锁的方法, 需要关注db的性能, 尤其是压测时整个系统TPS的表现.

ETCD

etcd也支持mini transaction支持多key事务,针对一个key的事务就是cas吗, mini transaction觉得看起来很高级... 这个地方就不多说了, 实现和redis类似, 优点是自带容灾免疫

Zookeeper

这个严格来说, 是我见过生产环境中比较完美的分布式锁, 如果zk没有bug的话, 大家开玩笑说代码一定要比zk更稳定.

实现就是利用zk的临时节点, 然后每个客户端调用zk的递增创建自己的临时节点,然后选出最小的节点即是获得锁的节点.说zk最完美的是因为, 如果客户端失败, 会第一时间释放锁(当然, zk的session是有超时时间的, 这点其实和etcd的lease比较类似)

etcd的作者说zk的临时节点是一个broken的实现, 我问他为什么, 他并没有回我 QAQ

问题

那么, 这些分布式锁都有一个问题.

正常的流程是 获取锁 -> 执行动作 -> 释放锁. 问题出现在, 在执行动作的时候, 如何得知自己是否仍然持有锁?

因为代码中是否判断是否仍然持有锁依赖的依据是时间. 成功获取锁之后, 发生了长时间的GC, 或者和我遇到的情况一样 整机Hang住了5s, 在这5s的时间里, 其他客户端完全有可能获取到锁, 那么此时将有两个客户端同时获取锁, 如下图所示

这种情况下, 锁服务本身是没有问题的, 但是客户端可能会同时获取锁.

针对这种问题, 唯一的解决方案就是, 在真正的资源服务器, 也就是可能发生意外的并发访问的服务器逻辑中, 增加校验, 以保证资源的顺序访问和修改.

以Google的Chubby为例, 提供了如下几种解决方法.

获取锁成功后, 会返回一个包含了lock-id 和递增序列号的Sequencer, 针对不同的诉求级别, 提供了如下三种方式, 级别从严格到宽松

  1. 资源服务器在执行操作前, 请求chubby验证Sequencer是否valid.类似的功能zk也可以做
  2. 资源服务器维护曾经执行过的Sequencer序号, 拒绝小于当前已执行Sequencer的请求
  3. 和其他实现一样, Sequencer具有租约时长限制, 并且在客户端失联(非正常释放锁)后一段时间内禁止其他客户端获取锁

这里面就要说到使用MySQL做分布式锁的巧妙之处了, 如果你使用行锁, 那么你一定在一个事务里, 并且巧妙的是, 如果你访问的共享资源就是当前的DB, 或者你把外部资源的操作状态同步到db中, 那么你始终可以确定的在持有锁的情况下操作共享资源.

后记

  • 分布式锁的应用场景不同, 如果只是想把事情做一次, 并不一定要分布式锁
  • 如果只是一些不那么重要的业务, 比如给用户推送一条营销消息, 漏推或者重复推也可以接受的话, 那么使用一个不那么严格的分布式锁也没有什么问题.
  • 如果仅仅是单一的资源访问, 并且希望使用严格的分布式锁, 需要改动资源服务器的逻辑, 可以考虑直接在资源服务器中实现互斥和顺序访问
  • 如果是选主需求的话, Raft/Paxos最完美, Split-brain? 不存在的

ole db 访问接口 sqlncli 无法启动分布式事务_分布式锁真的安全吗?相关推荐

  1. ole db 访问接口 sqlncli 无法启动分布式事务_阿里终面:分布式事务原理

    本文提纲如下 前言 单数据源事务 & 多数据源事务 常见分布式事务解决方案 2.1. 分布式事务模型 2.2. 二将军问题和幂等性 2.3. 两阶段提交(2PC) & 三阶段提交(3P ...

  2. ole db 访问接口 sqlncli 无法启动分布式事务_分布式事务,看这篇就够了

    0. 前言 1. 单数据源事务 & 多数据源事务 2. 常见分布式事务解决方案 2.1. 分布式事务模型 2.2. 二将军问题和幂等性 2.3. 两阶段提交(2PC) & 三阶段提交( ...

  3. 链接服务器“xx“的 OLE DB 访问接口 “SQLNCLI“ 返回了消息 “没有活动事务。“

    没有活动事务 链接服务器的 OLE DB 访问接口 "SQLNCLI" 无法启动分布式事务 解决方案-------使用DCTPing工具检测 1.        双方启动MSDTC ...

  4. 链接服务器访问接口返回了消息没有活动事务,因为链接服务器 SQLEHR 的 OLE DB 访问接口 SQLNCLI10 无法启动分布式事务。...

    查看一下MSDTC啟動是否正確 1.运行 regedt32,浏览至 HKEY_LOCAL_MACHINE\Software\Microsoft\MSDTC. 添加一个 DWORD 值 TurnOffR ...

  5. 无法启动链接服务器XXX DB Link的 OLE DB 访问接口 SQLNCLI11 的嵌套事务。由于 XACT_ABORT 选项已设置为 OFF,因此必须使用嵌套事务。链接服务器XXX ...

    无法启动链接服务器"XXX DB Link"的 OLE DB 访问接口 "SQLNCLI11" 的嵌套事务.由于 XACT_ABORT 选项已设置为 OFF,因 ...

  6. 尚未注册 OLE DB 访问接口 SQLNCLI10 7043 错误

    =================================== 与链接服务器的测试连接失败. =================================== 执行 Transact-S ...

  7. 无法初始化链接服务器 (null) 的 OLE DB 访问接口 Microsoft.Jet.OLEDB.4.0 的数据源对象...

    无法初始化链接服务器 "(null)" 的 OLE DB 访问接口 "Microsoft.Jet.OLEDB.4.0" 的数据源对象 今天从access导数据到 ...

  8. 链接服务器 '(null)' 的 OLE DB 访问接口'STREAM' 返回了对列 '[!BulkInsert].field' 无效的数据...

    SSIS中数据流任务或者 DTS数据表对表同步(一般是从文本导到数据库表中),抛出下面异常 链接服务器 '(null)' 的 OLE DB 访问接口'STREAM' 返回了对列 '[!BulkInse ...

  9. SSIS 错误代码 DTS_E_OLEDB_EXCEL_NOT_SUPPORTED 没有可用的 OLE DB 访问接口 SSIS 的 64 位版本中不支持 Excel 连接管理器...

    在Server 2008 R2的系统中,使用SQL安装包的BIDS(vs 08 shell)开发SSIS,按例子建好一个后,提示" SSIS 错误代码 DTS_E_OLEDB_EXCEL_N ...

最新文章

  1. appcompat_v7 引起的新建Android工程编译不过的问题 (转载)
  2. Scala偏函数使用示例
  3. jsp 连接access数据库
  4. mysql重置增量_摆脱困境:在每种测试方法之前重置自动增量列
  5. 基于Docker的Redis集群简单搭建
  6. express与express-art-template两者相结合使用方法
  7. 设置xy轴名称_最强干货来了:Grasshopper运算器名称总结(上篇)
  8. thinkphp5 open_basedir 补充
  9. Mysql中有哪些数据类型(建议收藏)
  10. python生成矢量图_Jupyter Notebook输出矢量图实例
  11. MSSQL数据库管理---索引
  12. 超级实习生计划学习笔记——Redis字符串
  13. OASIS协议标准文档的解读_第一部分
  14. 湖南省工业职业技术学院 计算机,湖南工业职业技术学院
  15. 给LINUX安装JDK
  16. 争做社会主义接班人!!!
  17. 礼多人不怪:跟美国教授通邮件的18种礼仪
  18. 如何解决eclipse黑底白字快速需求
  19. java实现剩余年假计算_计算两个日期之间的天数、工时(去除周六日、年假日)...
  20. AD17报错:InvalidParameter Exception Occurred In Copy

热门文章

  1. 撸了个 DDD 项目,爽!
  2. 如何躲开技术人员35岁魔咒?【有惊喜系列】
  3. 智能驾驶正文 0 揭秘 ISO 21448,它是自动驾驶行业的新风向标?
  4. 【晒出你的第83行代码】剑指星辰,两位开发者贴出了小试牛刀的毕设代码
  5. 重定向 302 与localhost 学习笔记
  6. elasticsearch实现远程索引复制
  7. 设计一个移动应用的本地缓存机制
  8. Fatal error: Allowed memory size of 8388608 bytes exhausted (tried to allocate 359404 bytes) in
  9. mysql 5.6 read-committed隔离级别下并发插入唯一索引导致死锁一例
  10. CCNA笔记之第二十节:RIP协议(大实验4)