并行与并发根本区别在于是否会竞争共享资源, 解决并发问题两个大的方向: 解决资源资源和解决并发问题。
解决竞争资源
共享资源是引起问题的根本原因, 如果将共享资源变为非共享,这样降低了并发问题产生的几率, 关系数据库的分库分表,NoSql/NewSql的水平扩展都属于这个思路, MySQL多个buffer pool,JAVA的ConcurrentHashMap等都属于这种思路。解决竞争资源可以提高效率, 但不能根本上解决问题:并发问题还是存在的。
解决并发问题
可以通过CAS,多版本,悲观锁的方式从根本上解决。
CAS
CAS可以理解成乐观锁的一种, 在数据库层面一般是采用version的方式实施。应用在写的场景上。
多版本
多版本的典型用法就是MVCC方式, 在各种数据库中被大量采用:主要思路是保留多版本数据, 在请求时根据请求时间返回数据。只能用于读场景。
Mysql的MVCC仅在RC/RR两种隔离级别下工作:
RC隔离级别: 会对每次请求(select)都重新生成ReadView(记录活跃的Transaction),因此多次请求返回的结果可能不同, 所以存在重复读取的问题。
RR隔离级别:
仅在Transaction第一次访问的时候生成一次ReadView,因此多次请求返回的结果相同,避免了重复读取的问题。
锁(悲观锁)
锁是解决问题的根本方法, 在多写竞争的时候只能采用锁的方式。mysql的锁分为行锁和表锁,在获取行锁(Share或Exclude)时,会同时在表中加入相应的意向锁(IS,IX),方便在加表锁时判断是否有行级别锁。
其他
区间锁:在RR隔离界别下, 通过二级非唯一索引或区间查询的方式,添加区间锁,防止出现幻读问题。
Select加锁: RR,RC隔离界别下, 默认select不加锁, 可通过for update, in share mode等语句显示加锁。
事务:锁是同事务绑定的, 非更新操作(即使是读写事务)不会生成事务id, 锁仅在事务释放后才释放。全表扫描会在所有记录上加锁, 并不会在表上加锁。
其他存储引擎:因为使用MyISAM、MEMORY、MERGE这些存储引擎的表在同一时刻只允许一个会话对表进行写操作,所以这些存储引擎实际上最好用在只读,或者大部分都是读操作,或者单用户的情景下。