关于原子性,很多人在很多地方都听说过,大家都耳熟能详。在事务的ACID中,有原子性的概念,在并发编程的原子性、可见性、顺序上也有原子性的概念。有些人认为它们是一样的,甚至很多人在谈到原子性时也是这样说:原子性是指事务是一个不可分割的工作单元,事务中的操作要么发生,要么全部不发生。然而,实际上,虽然它们都被称为原子性,但这个原子性并不是另一个原子性。数据库中的原子性确实是一个不可分割的工作单元,要么执行,要么不执行。因为事务可以是Commit也可以是Rollback。但是,在并发编程中,一个操作是不能回滚的,一个线程在执行过程中可能会失败。如果失败,则无法回滚。这是否意味着一个操作不能保证原子性?因此,在并发编程中,我们将CPU执行过程中一个或多个操作不被打断的特性称为原子性。这里的原子性是通过加锁来保证的。实际上,保证的是一系列不能拆分执行的操作。即在执行过程中,需要互斥和排斥,其他线程不能执行。举个例子来说明这两种原子性的区别。Redis中的Lua脚本能保证原子性吗?网上的文章很多,有的说可以,有的说不行。让我告诉你,两者都对,也不对。就是因为大家混淆了这两个原子的区别。我们都知道,当我们要在一个事务中执行多个命令时,我们会选择使用Lua脚本。Redis会将一个要执行的Lua脚本封装成一个单独的事务,如果在事务执行过程中有其他客户端请求,脚本执行器会暂存起来,等待脚本处理完毕。只有这样临时请求才会恢复。这样可以保证整个脚本是整体执行的,中间不会插入其他命令。这就是原子性中所谓的“不可分割”的特性。但是如果一个命令在事务执行过程中产生错误,事务不会回滚,也不会影响后续命令的执行。也就是说,Redis保证Lua脚本是原子执行的,但不保证脚本中的所有操作要么执行要么滚动。也就是说,在Redis中执行Lua脚本可以保证并发编程中不可拆分的原子性,但不保证在数据库ACID中要么全部执行要么翻转的原子性。
