目录
数据库最终的是存储。所以首要的是一个存储模块(文件系统),可以存在在机械硬盘或者是固态硬盘,光有存储不行,得需要一个程序实例组织这些数据才能使用。所以大方向下分为存储和程序实例。
关于程序实例,其实和做的项目一样,也许对功能进行划分。
首先需要对数据的格式以及文件的分个进行统一的管理,即把物理数据通过逻辑的形式来表示出来,这一功能划分为存储管理。
为了更好的优化程序,引入缓存机制,把取出来的数据存放在缓存里,下次需要的时候,直接从内存返回。
需要提供给外界指令可以操作我们的数据库,所以需要SQL解析模块。
做的sql操作需要记录下来,方便做主从同步或者灾难恢复,因此需要有日志管理办法来记录操作。
再者,需要给用户管理数据的私密空间,即权限划分。
设计系统除了考虑正常的功能,还需要考虑异常的功能,该如何恢复,所以需要引入容灾机制。
为了进一步提高查询的速度,以及支持并发,需要引入最为重要的两个模块:索引管理(提高查询),锁管理(支持并发)。
关于索引
快速查询数据
主键、唯一键、普通键
二叉树查找树进行二分查找
建立B-Tree结构
建立B+-Tree结构(Mysql)
索引是建立越多越好吗?
数据量小的表不需要建立索引,建立索引会增加额外的开销,索引也是一些存储在磁盘上的数据。
数据变更需要维护索引,更多的索引意味着更多的维护成本,也意味着需要更多的空间。
MyISAM默认用的是表级锁,不支持行级锁
InnoDB默认支持的是行级锁,也支持表级锁
关于MyISAM:假设有一条select语句A对数据库操作需要十秒钟,此时如果有另外一条语句B想要更新某一行(即使不在select的范围钟),那么B语句会等待A语句执行完毕之后再执行,这就是MyISAM的表级锁。其原理是,当执行select的时候,会对表加一个读锁,而对数据进行增删改的时候,会对表加一个写锁,当读锁未被释放的时候,另外一个访问想要增加写锁就会被阻塞,直到所有的读锁被释放。可以使用lock tables [table_name] read;手动给表增加读锁,使用unlock tables;可以解锁。
读锁也称为共享锁,可以理解为两条select语句可以同时对一个数据库操作,即使其中一条执行非常慢。写锁也称为排它锁,即不能同时执行,需等待其它写锁释放才能执行。也可再select语句后面加上for update来手动上排它锁,上共享锁是lock in share mode;。
关于InnoDB:由于InnoDB的事务是自动提交的,所以先把自动事务提交关闭:set autocommit=0;然后再对某一行进行select操作,同时另外一个session对这一行数据进行写操作,此操作会被阻塞,但如果此时对其它行数据进行写操作,将会很快的执行成功,这就是行级锁。其他操作与共享锁和排它锁的限制基本一致。
值得一提的是,InnoDB在没有用到索引的时候是用的表级锁。(如果不走索引的select添加读锁,那么此时即使是更新不同行也需要等待读锁解锁。
X:排它锁 S:共享锁
原子性(Atomicity):原子性是指事务包含的所有操作要么全部成功,要么全部失败回滚。
一致性(Consistency):一致性是指事务必须使数据库从一个一致性状态变换到另一个一致性状态,也就是说一个事务执行之前和执行之后都必须处于一致性状态。拿转账来说,假设用户A和用户B两者的钱加起来一共是5000,那么不管A和B之间如何转账,转几次账,事务结束后两个用户的钱相加起来应该还得是5000,这就是事务的一致性。
隔离性(Isolation):隔离性是当多个用户并发访问数据库时,比如操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。即要达到这么一种效果:对于任意两个并发的事务T1和T2,在事务T1看来,T2要么在T1开始之前就已经结束,要么在T1结束之后才开始,这样每个事务都感觉不到有其他事务在并发地执行。关于事务的隔离性数据库提供了多种隔离级别。
持久性(Durability):持久性是指一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的,即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作。
1.更新丢失
mysql所有事务隔离级别几乎都避免了更新丢失。
开始存款事务:查询账户余额为100元,存入100元,余额变为200元,提交事务。更新丢失。
开始取款事务:查询账户余额为100元,取出10元,将余额修改为90元,回滚事务,余额回复为100元。
2.read uncommitted引起的脏读(最低事务隔离级别)
将事务隔离级别设置成未提交读(可以读其它session未提交的数据)set session transaction isolation level read uncommitted;
session1:
start transaction;//开启事务
update account_innodb set balance = 100-10 where id =1; //取钱操作
selete balance from account_innodb where id=1;//结果balance=90
此时事务未提交,开启另外一个session
session2:
start transaction;//开启事务
selete balance from account_innodb where id=1;//结构balance=90 按照正常的业务逻辑,在别的事务未提交之前,结果应该要是100才是对的,因为session1还不确定是否要回滚!这就是脏读。
此时session1进行回滚
rollback; //回滚操作
selete balance from account_innodb where id=1;//结果balance=100
但是此时session1还在进行操作,并不知道session2已经将数据回滚,所以还会继续使用未提交的数据
update account_innodb set balance=balance+20;//存20块钱
commit; //提交事务
selete balance from account_innodb where id=1;//结果balance=110!
白白丢了10块钱啊......
3.如何避免第二点出现的脏读?
使用read committed(只允许读其他事务已提交的数据)(Oracle默认事务隔离级别)
将事务隔离级别提升一个档次 set session transaction isolation level read committed
区别:
在第二点的session2操作:selete balance from account_innodb where id=1;结果为100!
此时session1回滚之后,session再进行存钱操作,结果是符合实际业务的。
4.不可重复读REPEATABLE-READ
总结:
更新丢失——mysql所有事务隔离级别在数据库层面上均可避免
脏读——READ-COMMITTED事务隔离界别以上可避免
不可重复读——REPEATABLE-READ事务隔离级别以上可避免
可以通过 select@@tx_isolation查看当前session事务隔离级别,
信息加载中,请等待
微信客服(速回)
微信客服(慢回)