1、MySQL数据库的基本结构
最上层的客户端层负责处理链接处理和授权认证,安全等功能。
第二层架构是MySQL中的核心层,MySQL大多数核心功能都在这一层搞定。包括查询解析,优化,缓存以及MySQL所有的内置函数。所有的跨存储引擎的功能都在这一层实现。
第三层是MySQL所有的存储引擎。每一种存储引擎都有其不同的地方,也有其优势和劣势。存储引擎除了InnoDB以外都不会去解析SQL。InnoDB因为涉及到外键,所以需要解析SQL。
2、MySQL的并发控制
无论何时,只要涉及到读写操作都会涉及到并发问题。日志,邮件,excel等等都会有这样的问题。我之前写过一个功能是解析日志判断接口中日志打印的参数和传递的参数是否一致。最开始我没有考虑到这个问题,写了个线程池直接多线程给接口发请求。结果打印出来的日志全是乱七八糟的,日志格式也被改变了。
MySQL作为数据库,肯定不能允许并发问题的发生。不可能两个人同时对一条数据进行修改会出现很多种不同的结果,那样就乱套了。
读写锁:
我们常用的邮件功能,如果两个人同时阅读同一封邮件是完全没有问题的。但是如果一个人在阅读一封邮件,但是同时另外一个人修改了这封邮件,那么就会导致读邮件的人可能会看到不同的结果。有可能是空白邮件,也有可能是没有修改以前的邮件,还有可能乱码等各种问题。数据库也存在同样的问题。
解决这类问题的最经典的方法就是加共享锁(读锁)和排它锁(写锁)。
共享锁又称读锁。读一个数据的时候多少个对象同时读取都无所谓,都可以同时读取数据,互不干扰。
排它锁又称写锁。一个写锁会阻塞其他的共享锁和排它锁。在有对象在进行写的操作时,数据库会将数据锁死,不允许读也不会允许别的用户对其修改。
粒度锁:
除了读写锁,MySQL还有一种锁机制叫做粒度锁。读写锁虽然可以解决读写矛盾问题,但是还有个很严重的问题就是我们应该锁住多大的范围?是只要有一条内容被修改我们就锁住整个数据库不让读还是锁住整个表又或者仅仅只是锁住被修改的那一行不让读?这个就涉及到粒度锁。
我们最希望的方式是尽量精确的锁定被修改的地方而不是锁定所有资源,这样可以支持更高的并发量,只要修改的数据不互相冲突即可。
但是问题是加锁也需要消耗资源。锁的各种操作,包括获得锁,检查锁是否被释放,释放锁等等操作,都会大幅度的增加系统的消耗。所以采取哪一种的锁策略,也需要在锁的开销和数据安全之间寻求平衡。
MySQL最重要的两种锁策略分别是表锁和行级锁。
表锁是MySQL中开销最小的锁策略。当一张表中有一行被更改时,当前表就不再允许查询和修改。一个用户在对表进行写操作时需要首先获取表的排它锁。当其获取了排它锁,别人就不能再对这张表进行访问,除非他修改完该表并且释放了锁。
行级锁可以最大程度的支持并发处理,但是他也是锁消耗最大的锁。行级锁只会获取需要修改的行的排它锁,整张表的其余行依然可以进行访问。
3、MySQL的事务
说明事务最好的方式就是转账。
如果A要给B转账200元钱。从数据库的操作来看就是
1. 开始事务
2. A账户减少200元
3. B账户增加200元
4. 提交事务
当第三条执行失败时,事务没有被提交,所以会直接回滚到执行事务以前。A的账户不会减少200元。
事务的四大特性:原子性,一致性,隔离性,持久性
原子性:一个事务必须被视为不可分割的最小单元,整个事务必须全部提交成功或者全部提交失败。
一致性:数据库总是从一个一致性的状态转换到另外一个一致性的状态。
隔离性:通常来说一个事务在没有提交以前对于别的事务来说是不可见的。前面转账的例子如果在执行B账户增加200元操作的同时另一个事务查询了A的账户,A的账户应该还是未减少之前的状态。
持久性:一旦事务提交,那么数据就会被永久的保存到数据库中。这是哪怕是系统崩溃了,死机了,数据也不会丢失。但是这也只是理论上,否则也不会需要做备份了。
隔离性实际上比想象的要复杂的多得多。在SQL中定义了四中隔离级别,级别越低的并发能力越强,当然安全性也就越差。级别越高的并发能力越差,安全性也越强。
四大隔离级别:未提交读,提交读(不可重复读),可重复读,可串行化
未提交读:在这个隔离级别下事务里面未提交的部分别的事务也可以看到,这又被称之为脏读。这个级别会导致非常多的问题,比如查询后根据查询到的数据修改数据,就会导致数据错误。在性能上来说这个级别的并发性能是最好的,但是基本上不用,因为太不安全。
提交读(不可重复读):大多数的数据库的默认事务隔离级别都是不可重复读,但是MySQL不是。不可重复读满足了前面隔离性的简单定义,一个事务如果没有被提交,那么就不会被别的事务读取结果。但是同一个事务中两次读取同一个数据可能获取的结果不一样。
可重复读:该级别可以保证多次读取同样记录的结果是一样的,MySQL默认的就是该隔离级别,但是其无法解决幻读的问题。
可串行化:可串行化是隔离级别的最高级别,它强制所有的事务必须串行执行,避免了幻读的问题,但是效率太低,一般也不会用。