工作后一段时间后,慢慢发现读写分离的思想出现过很多。比如在 MySQL 集群中的读写分离,MySQL 引擎 InnoDB 中的 MVCC,以及 JDK 中的 CopyOnWriteArrayList 中都有体现。
一、MySQL 集群读写分离
大多数互联网业务中,往往读多写少,这时候数据库的读会首先成为数据库的瓶颈。如果我们已经优化了 SQL,但是读依旧还是瓶颈时,这时就可以选择“读写分离”架构了。
1、具体实施
- 集群分为主从库,主库用来写,可以不加索引,提高写效率,丛库追加索引,提高读效率
2、带来的问题
主从架构会剧透延迟性,会带来一致性问题,对实时性要求搞的业务会产生影响
- 写后立刻读: 在写入数据库之后某个时间内读操作去主库,其他时间去丛库
- 二次查询:先去丛库读,读不到就去主库读,注意别给主库带来太大写压力
- 特殊处理:实时性要求搞的业务读写放在主库,次要业务使用读写分离
二、InnoDB 的 MVCC
数据库通常使用锁实现隔离性。最近的锁,锁住资源之后禁止其他资源访问,不论是读还是写都会被排斥,但是在读多写少的情况下,对读进行锁互斥就显得没有必要。于是引入了读写锁,主要对读进行加锁。后来发现并发量还是不够,最终出现了 MVCC(Multi-Version Concurrency Control)即 多版本并发控制
- MVCC 是 MySQL 引擎 InnoDB 对并发处理的无锁实现
三、CopyOnWriteArrayList
顾名思义,这是一个写时复制的 ArrayList,是线程安全的。发生结构性变化时的时候加锁,此时会创建一个副本数组给其他线程进行读取,变化完成之后再替换之前的数组。因此不能保证实时性,只能保证最终一致性,适合读多写少的情况。
1、源码解析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
| public class CopyOnWriteArrayList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
final transient Object lock = new Object();
private transient volatile Object[] array;
public E get(int index) { return elementAt(getArray(), index); } @SuppressWarnings("unchecked") static <E> E elementAt(Object[] a, int index) { return (E) a[index]; }
public E remove(int index) { synchronized (lock) { Object[] es = getArray(); int len = es.length; E oldValue = elementAt(es, index); int numMoved = len - index - 1; Object[] newElements; if (numMoved == 0) newElements = Arrays.copyOf(es, len - 1); else { newElements = new Object[len - 1]; System.arraycopy(es, 0, newElements, 0, index); System.arraycopy(es, index + 1, newElements, index, numMoved); } setArray(newElements); return oldValue; } }
public boolean add(E element) { synchronized (lock) { checkForComodification(); CopyOnWriteArrayList.this.add(offset + size, element); expectedArray = getArray(); size++; } return true; }
}
|