1、db模型
class DistributedLock(BaseModel):
id = PrimaryKeyField()
lock_name = CharField()
lock_time = DateTimeField()
time_out = IntegerField()
class Meta:
db_table = 't_lock'
对lock_name创建唯一性索引。
lock_time是当前时间,time_out是锁超时时间。
2、python实现
创建锁
def acquire_lock(lock_name, time_out=5):
log_info(f"Try to acquire lock: {lock_name}, and time out is {time_out}")
try:
with db.atomic():
new_record = DistributedLock(lock_name=lock_name, time_out=time_out)
affected_rows = new_record.save()
if affected_rows > 0:
log_info(f"Acquire lock {lock_name} success.")
return True
else:
log_info(f"Acquire lock {lock_name} failed.")
return False
except Exception as e: # pylint: disable=broad-except
log_exception(f"Error acquiring lock: {e}")
return False
这里申请锁的时候就将唯一索引写入db中,用事务保证原子操作。
def test():
lock_name = "acquire_lock_name" # 这里的lockname要保证是唯一的
attempts = 5
i = 0
while i < attempts:
if not acquire_lock(lock_name, time_out=10): # 循环5次获取锁
time.sleep(random.uniform(0.1, 0.2)) # 随机睡眠0.1到0.2秒之间的时间
i += 1
continue
这里可以尝试多次获取锁,获取不到锁就随机sleep一段时间然后重试,超时机制是为了防止死锁,确保锁会被释放。
重试加随机等待,避免进程间竞争过于激烈。
销毁锁
def clean_expire_lock():
log_info("Start to clean expire lock.")
locks = DistributedLock.select()
current_time = datetime.datetime.now()
for lock in locks:
lock_name = lock.lock_name
lock_time = lock.lock_time
lock_timeout = lock.time_out
second_diff = (current_time - lock_time).total_seconds()
if second_diff >= lock_timeout:
DistributedLock.delete().where(DistributedLock.lock_name == lock_name).execute()
通过定时任务按时去清理掉当前时间减去lock_time之后转换成seconds,然后大于time_out的记录。
3. 多进程支持性分析
支持多进程的原因:
数据库级别的互斥:所有进程共享同一个数据库,通过数据库事务确保锁操作的原子性
唯一性约束:lock_name作为唯一标识,确保同一时刻只有一个进程能成功插入记录
超时机制:10秒超时防止死锁,确保锁最终会被释放
重试策略:5次重试加随机等待,避免进程间竞争过于激烈
4. 当前实现的优势
分布式友好:不依赖进程内存,支持跨机器部署
可靠性高:基于数据库事务,确保锁操作的原子性
容错性好:超时机制防止死锁,异常情况下能自动释放
5. 潜在改进建议
虽然当前实现已经支持多进程,但可以考虑以下优化:
锁清理机制:增加定时任务清理过期的锁记录
锁状态监控:添加锁使用情况的监控和告警
性能优化:对于高并发场景,可以考虑使用Redis等内存数据库提升锁性能
结论:当前的锁机制设计合理,能够有效支持多进程环境下的业务并发控制。
确认arrange_preheat_task方法中的数据库分布式锁机制能够支持多进程环境,通过数据库事务和唯一性约束确保并发安全,具备重试和超时机制防止死锁。
490

被折叠的 条评论
为什么被折叠?



