乐观锁

  • 乐观地认为数据没有别处修改,在完成业务的时候再拿锁(其实不会上锁,只是判断有无修改)。
  • mysql没有提供具体乐观锁,是要程序自己实现。

做法

用version字段(主要用这个),或时间戳字段:

  • select data as old_data, version as old_version from … where …;
  • 处理data、version;
  • update set date = new_data,version = new_version where version = old_version。
  • updated row > 0,则成功提交事务;否则回滚。

悲观锁

  • 悲观地认为数据会被别处修改,因此先确保获取锁成功再进行业务操作。(一锁二查三更新)
  • 表锁、行锁、共享锁、排它锁,都是悲观锁,都是操作前先上锁。
  • mysql提供这种机制,直接用就行。

跟乐观锁开销比较

  • 取锁成功情况下,乐观锁开销较小;
  • 取锁失败情况下,需要回滚,则乐观锁开销较大。

总结:写操作少时,用乐观锁;写操作多时,用悲观锁。

表锁、行锁

  • 表锁锁定整个表,开销小,加锁快;不会出现死锁;锁定力度大,锁冲突概率高,并发度低;
  • 行锁锁定若干行,开销大,加锁慢;会出现死锁;锁定力度小,锁冲突概率低,并发度高。

共享锁、排他锁、自旋锁

  • 共享锁:又叫读锁,加锁后,其他线程可读,不可写;
  • 排它锁:又叫写锁,加锁后,其他线程不可读、不可写;
  • 自旋锁:跟排它锁类似,但不会引起调用者睡眠。

排他锁、自旋锁,比较

原理
  • 排它锁:线程->sleep(加锁)->running(解锁),过程有上下文切换,cpu抢占,信号量发送等;
  • 自旋锁:线程->running(加锁->解锁),死循环检测锁的标志位。
开销

初始开销,排它锁较高;但时间越长,排它锁不变,自旋锁线性增长。

场景
  • 互斥锁,适用于临界区锁时间较长的操作:临界区有IO操作,临界区竞争激烈,单核处理器等;
  • 自旋锁,适用于临界区锁时间非常短,且CPU资源不紧张,一般用于多核的服务器。