数据库的ACID , 并发问题,事务等级,乐观锁,CAS算法,索引

1、mysql的四大基本要素(ACID):
原子性:针对操作,事务如同生物中的原子一样密不可分,事务内的事必须是全部完成,如果发生了故障,那么需要回滚到事务开始前的状态。
一致性:个人理解的一致性是一种状态。事务执行前和执行后的一致性不会发生改变。例如A给B转钱, 那么A-=100, B+=100 。这样保证了一致性。 另外提一下原子性和一致性的区别? 例如一组银行转账的操作中 A-=100 ,B+=500 ,这样遵循了原子性,但是违背了一致性。
隔离性:同一时间,同一数据只允许被一个事务操作。
持久性:事务完成后,数据是被持久保存到数据库,不允许回滚。
2、事务的三个并发问题:
脏读(针对未提交数据):事务A对数据进行更新,并且未提交到数据库。此时事务B读取了数据,但是事务A发生了数据回滚。那么事务B就发生了脏读。
不可重复读(事务期间插入了其他的完整事务,重点在其他事务的delete和update):一个事务内的多次读取,得到的结果不同。原因是在第一次读取和第二次读取之间,其他事务进行了修改。
幻读(事务期间插入了其他的完整事务,重点在其他事务的insert):事务A第一次读发现有10个员工,此时事务B进行了插入操作,事务A再次读取的时候发现有11个员工,在事务A看来有一条数据是凭空出现的。
3、事务的四种隔离级别(用于解决并发问题):
sql> set session transaction isolation level read uncommitted

Read Uncommitted (读 未提交):最低的隔离级别,一个事务可以获取另一个事务未提交的数据。应用程序A对数据进行读取 t = 450 ,应用程序B此时对数据进行修改 t = 400 ,但是并未进行提交。 此时A读取的数据是t = 400。万一此时程序B进行回滚,那么A读到的数据就是脏数据。另外,此时如果A进行 t = t-50的sql更新,再次读到的数据是 t = 400。 很混乱,因此不能用这种级别。

Read committed(读 已提交):数据库默认的隔离级别, 事务A第一次读取到t = 400 , 此时事务B 更新 t = 300; 事务A再次进行读取 t = 400 ,解决了读脏数据的问题。 此时事务B进行提交commit, 事务A第三次进行读取,t = 300 ,出现不可重复读问题。

repeatable read(可重复读,锁住每行数据): 事务A进行第一次查询 t = 400 ,事务B进行修改 t = 300 并且事务Bcommit,此时事务A进行第二次查询,t = 400 。解决了不可重复读问题。 此时B插入了一条数据, A执行select,查找出了新增数据的,出现幻读。

serializable(序列化,锁住整张表),不会出现幻读

4、悲观锁和乐观锁(引入并发编程咯)
不可重复读锁定表的每一行(行锁);幻读锁定整张表(表锁)。
锁的机制是怎样的呢 ?

4.1 悲观锁
每次总认为其他人拿到数据会进行修改,因此事务A拿到数据时,对数据进行上锁,其他事务希望拿到数据就只能阻塞,等待事务A释放锁。这里讨论一下并发编程的只是。Synchronize和ReentrantLock都是悲观锁,他们的区别在于sync是以来JVM实现的,后者是JDK实现的。LOCK可以指定公平锁(先等待的线程先获得锁)和非公平锁。sync只能是非公平锁。

4.2 乐观锁
认为别人拿到数据不会修改,但是在更新的时候会判断一下在此期间别人有没有更新这个数据。用版本号机制和CAS(compare and swap)算法实现。

4.2.1 版本号机制
假设数据库的表上有一个字段 version,初始化为1。 有一个balance = 50余额数据。

1、事务A读出数据,此时version = 1 , 修改balance = balance + 30 = 80 。
2、事务A进行的过程中,事务B也读取数据,version = 1 ,事务B修改 balance = balance - 20 = 30。
3、事务A完成了工作,把version+1,接着进行commit提交,数据库的version = 1,满足提交的数据版本大于数据库记录版本,因此数据库进行更新 version=2,balance = 80。
4、事务B进行也完成了工作,version+1 = 2, 接着进行commit提交事务,但是数据库记录version = 2, 不满足提交的数据版本大于数据库记录版本,因此提交请求被驳回,事务B进行回滚

4.2.1 CAS算法
CAS算法涉及三个操作数

需要读写的内存中 V
旧的预期值 A
即将要更新的目标值 B
CAS指令执行时,当且仅当内存地址V的值和预期的A相等,才将内存地址V的值修改为B。否则什么都不做。整个比较和替换操作是原子操作。

下面的代码针对源码进行分析,最终探究到 “比较和替换是原子操作”
AtomicInteger中自增的方法,调用了CAS,看下图。

1)下面是对getAndAddInt方法的讲解

Object o:如果是atomicInteger,传入的是this
long offset:内存的偏移位置
int data:需要添加的值

o 和offet 用于读取在内存上位置的值v
我们看CAS(o,offset,v,v+delta)的四个参数,前两个参数用于再次从内存中取值,取到的值和v进行比较, 如果两者相等,那么该内存上的值覆盖为 v + delta。


2) 进入compareAndSwapInt后,最后调用Unsafa_compareAndSwapInt
这个函数体现了 "比较"

看return语句, 如果这里返回真,那么compareAndSwapInt也返回真,那么CAS算法结束。因此关键在于 Atomic:cmpxchg(x,addr,e) == e ? 了


3) 进入Atomic:cmpxchg
这个函数体现了 "替换"
windows_x86的实现:下面这段代码我也没看懂,它实现的功能是比较dest内存上的值是否等于 compare_value ,如果相等那么dest内存赋值为exchange_value

一些代码细节非常精彩,
((1)) mp = is_MP() 用于判断是否多核,如果多核返回1
((2)) LOCK_IF_MP(mp) mp=1,即多核时,进行lock上锁。随后进行赋值。(这里完成了赋值是原子的)

5、索引
5.1 定义
索引是按照数据库中的一个或者多个列的值进行排序的结构。假设想按职员的姓来查找,那么相比搜索所有的行会快很多。(暂时参考二分理解)

5.2 优缺点
优点:1)加快数据检索时间 2)创建唯一性索引,保证数据表中每一行数据的唯一性 3)加速表和表之间的连接 4)在使用分组和排序子句进行检索时,可以显著减少分组和排序的时间。

缺点:1)索引需要占用数据表意外的物理存储空间 2)创建索引和维护索引需要花费一定的时间 3)更新操作时,需要重建索引。

5.3 根据数据库功能划分的索引类型
聚集索引:类比查字典的过程,现在需要查找"安"字,当我们在翻开目录的时候,就是使用索引。我们查找到拼音 ”an” 的位置是 第10页,那么就翻开字典到10 ,从10到后面进行查找。 我们此时希望查找“吧”字, 那么只需要在"an"的基础上继续向后翻页,找到"ba"。 因此聚集索引的特点是:建立的索引和数据库物理位置是一致的,表和索引按某种顺序重新排列。

非聚集索引:当你在查字典时,使用了偏旁方式查找。例如查找驰,那么对于在正文的第290页。 而“驰”在目录表的下一个位置是“张”,并且对应在正文的位置是60页。
非聚集索引的特点是:两个索引key在索引中是相邻的,但是对应的数据在数据库中的位置是无序的。

这张表总结了两种索引的抉择
聚集索引: create clustered index ind_xx_name on tablename(colunm,colunm)

不适用: 经常修改索引列,大数目的不同值,都不应使用聚集索引,因此索引自身维护耗时。

适用: 区间查找,排序查找。例如邮箱系统中,展示未读的邮件。通常未读的邮件都是近一个月内的,如果那么按照日期建立聚集索引那么检索效率会快很多。聚集索引非常珍贵,不要一味的用在主键上,这是极大的浪费。

非聚集索引: create index ind_xx_name on tablename(colunm,colunm)

需要使用了额外的空间存储,况且修改操作需要进行更新索引
因为只能有一个聚集索引,因此这算是第二选择。

唯一索引:UNIQUE create unique index stusno on student(sno);
标明此索引的每一个索引值只对应唯一的数据记录,对于单列唯一性索引,保证单列不重复; 对于多列唯一性索引,保证了多个值的组合不重复。

主键索引:primary key
主键索引是唯一索引的特例,关系型数据库中会自动创建主键索引。使用主键索引进行查找会加快速度。

5.4 索引的数据结构实现
1、哈希索引

就是简单得把key存入哈希表,使用于单条记录查询。例如查询id=2的员工信息,那么直接调用map.get(2)得到磁盘地址。从而得到数据

2、B+树
数据本身就是索引,按照B+树结构存储。树的叶子结点data域保存了完整的数据记录。因此通过聚集索引进行的查找,就能直接找出数据。而其他的非聚集索引,或称辅助索引的data域保存的是聚集索引的key,因此在查找出key之后,还需要使用聚集索引进行查找。

5.5 两种不同的引擎
MyISAM引擎
myisam只支持表级锁,用户在操作myisam表时,select,update,delete,insert语句都会给表自动加锁,如果加锁以后的表满足insert并发的情况下,可以在表的尾部插入新的数据。也可以通过lock table命令来锁表,这样操作主要是可以模仿事务,但是消耗非常大,一般只在实验演示中使用。

InnoDB
支持事务安全的引擎,支持外键、行锁、事务是他的最大特点。如果有大量的update和insert,建议使用InnoDB,特别是针对多个并发和QPS较高的情况

5.6 覆盖索引
通过聚集索引所选的列进行查询时,找出来的节点上面的data域就是所希望获得的数据。这叫覆盖索引。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值