为什么Zookeeper天生就是一副分布式锁的胚子?
`description` varchar(1024) NOT NULL DEFAULT "" COMMENT '描述', PRIMARY KEY (`id`), UNIQUE KEY `uiq_idx_resource` (`resource`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='数据库分布式锁表'; ①获得锁 我们可以插入一条数据: INSERT INTO database_lock(resource, description) VALUES (1, 'lock'); 因为表 database_lock 中 resource 是唯一索引,所以其他请求提交到数据库,就会报错,并不会插入成功,只有一个可以插入。插入成功,我们就获取到锁。 ②删除锁 INSERT INTO database_lock(resource, description) VALUES (1, 'lock'); 这种实现方式非常的简单,但是需要注意以下几点: ①这种锁没有失效时间,一旦释放锁的操作失败就会导致锁记录一直在数据库中,其他线程无法获得锁。这个缺陷也很好解决,比如可以做一个定时任务去定时清理。 ②这种锁的可靠性依赖于数据库。建议设置备库,避免单点,进一步提高可靠性。 ③这种锁是非阻塞的,因为插入数据失败之后会直接报错,想要获得锁就需要再次操作。 如果需要阻塞式的,可以弄个 for 循环、while 循环之类的,直至 INSERT 成功再返回。 ④这种锁也是非可重入的,因为同一个线程在没有释放锁之前无法再次获得锁,因为数据库中已经存在同一份记录了。 想要实现可重入锁,可以在数据库中添加一些字段,比如获得锁的主机信息、线程信息等。 那么在再次获得锁的时候可以先查询数据,如果当前的主机信息和线程信息等能被查到的话,可以直接把锁分配给它。 乐观锁 顾名思义,系统认为数据的更新在大多数情况下是不会产生冲突的,只在数据库更新操作提交的时候才对数据作冲突检测。如果检测的结果出现了与预期数据不一致的情况,则返回失败信息。 乐观锁大多数是基于数据版本(version)的记录机制实现的。何谓数据版本号? 即为数据增加一个版本标识,在基于数据库表的版本解决方案中,一般是通过为数据库表添加一个 “version”字段来实现读取出数据时,将此版本号一同读出,之后更新时,对此版本号加 1。 在更新过程中,会对版本号进行比较,如果是一致的,没有发生改变,则会成功执行本次操作;如果版本号不一致,则会更新失败。 为了更好的理解数据库乐观锁在实际项目中的使用,这里也就举了业界老生常谈的库存例子。 一个电商平台都会存在商品的库存,当用户进行购买的时候就会对库存进行操作(库存减 1 代表已经卖出了一件)。 如果只是一个用户进行操作数据库本身就能保证用户操作的正确性,而在并发的情况下就会产生一些意想不到的问题。 比如两个用户同时购买一件商品,在数据库层面实际操作应该是库存进行减 2 操作。 但是由于高并发的情况,第一个用户购买完成进行数据读取当前库存并进行减 1 操作,由于这个操作没有完全执行完成。 第二个用户就进入购买相同商品,此时查询出的库存可能是未减 1 操作的库存导致了脏数据的出现【线程不安全操作】。 数据库乐观锁也能保证线程安全,通常代码层面我们都会这样做: (编辑:PHP编程网 - 黄冈站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |