基本概念
wiki:In computer science, a lock or mutex (from mutual exclusion) is a synchronization mechanism for enforcing limits on access to a resource in an environment where there are many threads of execution. A lock is designed to enforce a mutual exclusion concurrency control policy.
在计算机科学中,锁或互斥量是一种同步机制,用于在多线程执行环境中,强行限制对资源访问。锁常被用于同步并发控制。
简单来讲,锁是用来控制多线程执行对资源的并发访问的。比如当一个资源只允许在任意时刻只有一个执行线程对其进行写操作,那当其他线程要访问资源时,就必须要检查该该资源上是否存在写操作锁,如果存在,必须要等待锁的释放并获得锁之后才能对资源进行访问。
悲观锁
悲观锁假设在一个完整事务发生的过程中,总是会有其他线程会更改所操作的资源,因此线程总是对资源加锁之后才会对其做更改。
乐观锁
乐观锁假设在一个完整事务发生的过程中,不一定会有其他线程会更改资源,一旦发现资源被更改,则停止当前事务回滚所有操作。
分布式锁
常见的锁有多线程锁、数据库事务中的行级锁、表级锁等,这些锁的特点都是发生在单一系统环境中,如果需要在不同的进程、不同机器和系统等分布式环境中控制对资源的访问,这时候我们需要一把分布式锁。
简单来说,分布式锁就是解决分布式环境中对资源的访问限制。
如何设计一把分布式锁
我们用 redis 来实现这把分布式的锁,redis 速度快、支持事务、可持久化的特点非常适合创建分布式锁。
分布式环境中如何消除网络延迟对锁获取的影响
锁,简单来说就是存于 redis 中一个唯一的 key。一般而言,redis 用 set 命令来完成一个 key 的设置(加锁),使用 get 命令获取 key 的信息(检查锁)。由于网络延迟的存在,简单的使用 set 和 get 命令可能会带来如下问题:
线程 A 检查锁是否存在(get)–>否–>加锁(set),在 A 发起加锁命令但是还没有加锁成功的时候,可能线程 B 已经完成了 set 操作,锁被 B 获得,但是 A 也发起了加锁请求,由于 set 命令并不检查 key 的存在,B 的锁很可能会被 A 的 set 操作破坏。
幸运的是,redis 提供了另一个命令 setx : 当指定的 key 不存在时,设置 key 的值为指定 value,如果存在,不做任何操作,成功则返回 1,失败则返回 0。也就是只要命令返回成功,线程就能正确获得锁,不需要再做类似 get 检查操作。
使用 setx 可以消除网络延迟对锁设置的影响。
加锁的客户端发生 crash 导致锁不能被正确释放应该怎么处理?
加锁成功并操作完成时候,就需要加锁线程对锁进行释放,以让出资源的控制权。释放锁,简单来说就是删除 redis 中这个唯一的 key,但是一定要保证删除的这个 key 是该线程创建的,因而锁创建时必须携带执行线程的唯一特征以标示创建者的身份。
如果加锁的线程出现异常 crash 了而不能及时删除锁,则会导致锁一直无法被正确释放,资源处于一直被占有,别的线程处于一直等待的状态。为了避免这样的情况发生,锁一定要在异常发生之后可以自己释放,以让出资源的控制权,可以使用 redis 的超时机制来达到这个目的。超时时间视不同的业务场景而定,一般是最大允许等待时间。需要注意的是,只有在加锁成功之后才可以对 key 设置 TTL,否则很容易导致 key 被多个线程不断设置 TTL 而无法过期。
if CONN.setnx(lockname, identifier):
CONN.expire(lockname, timeout)
加锁之后如何有效监测锁是否被篡改?
redis 提供了 pipeline 和事务操作来保证多个命令可以在一个事务内全部完成从而减少多次网络请求带来的开销,watch 命令又可以在事务开始执行之前对所要操作的 key 执行监测,从而保证了事务的完整性和一致性。因此,为了防止锁篡改,可以在加锁完成之后对锁进行 watch 操作,一旦锁发生变化,则终止事务,回滚操作。
pipe = CONN.pipeline(True)
pipe.watch(lock)
提供锁的宿主机( redis 服务器) crash 导致锁不能被正确建立和释放该如何处理?
不论是通信故障或是服务器故障而导致的锁服务器无法响应,此时都会导致客户端加锁和释放锁的请求无法完成,因此一定要有相应的应急处理,以确保程序流程的完整体验,加强客户端的健壮性。比如相应的超时提示,异常告警等。
哪些边界需要注意
1.只有锁正确释放才算是整个事务的完整结束,如果锁释放失败,比如被篡改、锁服务器异常等,不同的业务可以根据自己的需求进行变动和调整。
2.设置 TTL 一定要是加锁成功之后,否则所有获取锁的客户端都会尝试 TTL 导致锁无法过期。
3.锁的过期时间也就是获取锁的客户端的最大等待时间,这个时间根据执行的事务能够容忍的最长时间为限
一个简单的 python 实现
import time
import redis
import logging
logger = logging.getLogger('service.redis_lock')
CONN = redis.Redis(host='localhost')
def acquire_lock(lockname, identifier, wait_time=20, timeout=15):
end = time.time() + wait_time
while end > time.time():
if CONN.setnx(lockname, identifier):
CONN.expire(lockname, timeout) # set expire time
return identifier
time.sleep(0.001) #wait until the lock expired or release by some thread
return False
def release_lock(lockname, identifier):
pipe = CONN.pipeline(True)
try:
#watch lock once lock has been changed, break this transaction
pipe.watch(lockname)
#check if lock has been changed
if pipe.get(lockname) == identifier:
pipe.multi()
pipe.delete(lockname)
pipe.execute()
return True
pipe.unwatch() #execu when identifier not equal
except redis.exceptions.WatchError as e:
logger.error(e)
return False
except Exception as e:
logger.error(e)
return False
return False
if __name__ == '__main__':
print release_lock('h', 'a')
现在互联网公司面试的时候都会问到Redis,但是仅仅掌握以上所述是不够的,我们需要掌握更多的基础知识,这是我整理的一些需要掌握的知识技术点,分享给大家:
需要思维导图格式的可以加群:810589193
1. 高性能架构专题
1.1. 分布式架构思维
大型互联网架构演进过程
架构师应具备的分布式知识
主流分布式架构设计详解
1.2. Zookeeper分布式环境指挥官
zookeeper基础
zookeeper进阶
zk的使用举例
1.3. Nginx高并发分流进阶实战
Nginx模块简介
Nginx工作原理及安装配置
Nginx常用命令管理及升级
Nginx配置文件精讲
实战线上Nginx多站点配置
Nginx配置优化及深入剖析
Nginx Rewrite规则剖析
Nginx日志分析及脚本编写
Nginx日志切割案例讲解
Nginx防盗链案例配置
Nginx日常运维及故障解决
Nginx构建安全架构实战
企业实战Nginx+Tomcat动静分离架构实战
Nginx+Keepalived集群架构实战
Nginx+Keepalived双主架构案例实战
1.4. ActiveMq消息中间件
消息中间件(ActiveMQ、RabbitMQ、Kafka)简介及对比
软件下载、安装及部署
P2P、PUB\SUB模型详解
消息确认及重发机制
企业级高可用集群部署方案
ActiveMQ基于Spring完成分布式消息队列实战
1.5. RabbitMq消息中间件
RabbitMQ及高可用集群部署
深入学习RabbitMQ消息分发机制及主题消息分发
RabbitMQ消息路由机制分析
RabbitMQ消息确认机制分析
RabbitMQ基于Spring完成分布式消息队列实战
安装配置
集群化与镜像队列
1.6. Kafka百万级吞实战
基于ZooKeeper搭建高可用集群实战
Kafka消息处理过程解析
基于Java语言实现Kafka生产者与消费者实例
Kafka副本机制及选举原理窥探
使用Kafka实现日志实时上报统计分析实战
1.7. Memcached进阶实战
概述
开发基础
1.8. Redis高性能缓存数据库
Redis初入门及介绍
Redis主从模式
Redis常用命令及应用场景
Redis客户端
Redis持久化
哨兵核心机制
高可用集群
原子性
应用场景代码开发与设计分析实战
1.9. MongoDB进阶实战
mongodb入门
mongodb进阶
mongodb高级知识
最佳实践与注意事项
1.10. 高性能缓存开发实战
缓存雪崩解决方案实战
缓存粒度控制实战
缓存击穿实战
缓存热点KEY重建优化实战
缓存同步实战
Spring-Cache开发实战
1.11. Mysql高性能存储实战
Mysql
Mycat
1.12. FastDFS分布式文件存储实战
文件存储实战
文件同步实战
文件查询实战
分布式部署实战
1.13. 高并发场景分布式解决方案实战
分布式主键生成方案
Session跨域共享实战
分布式事务解决方案实战
分布式锁解决方案实战
分布式单点登录 SSO实战
分布式调度任务实战
分布式配置中心
针对以上的技术点,有十余年Java经验系统架构师、项目经理录制了一些视频,用来回答这些技术。
最后送福利了,现在加群:810589193免费可以获取Java工程化、高性能及分布式、高性能、高架构、性能调优、Spring、MyBatis、Netty源码分析等多个知识点高级进阶干货的相关视频资料,还有spring和虚拟机等书籍扫描版,还有更多面试题等你来拿
分享给喜欢Java,喜欢编程,有梦想成为架构师的程序员们,希望能够帮助到你们