mysql(一):mysql的一些基础常识

2018-12-18  本文已影响92人  骑着母猪砍大象

转载请注明作者和出处https://www.jianshu.com/p/4bfbe89c9c1f

作者简介: 一个本该成为游戏职业选手却被编程耽误的程序员


线程

     mysql是线程级别的,每个客户端连接会在服务器进程中拥有一个线程,该线程只能轮流在某个cpu核心或者cpu中运行,服务器只会缓存线程,因此不会为每个新的链接创建新的线程或者销毁线程

    mysql的锁从功能上分为共享锁(shared lock)排他锁(exclusive lock),也可以称为读锁(read lock)写锁(write lock),顾名思义,读锁的是多个用户在读取同一个资源,互不干扰,写锁是排他的,也就是说一个写锁会阻塞其他写锁和读锁,也就是说,在一个用户(一个链接)执行写锁的时候,其他用户处于等待状态。
   注意,锁的各种操作都是需要消耗资源的,检查锁是否解除,释放锁,都会增加系统的开销。所以锁的数量和数据的安全性在大型项目中需要一个中间平衡点。

锁的粒度

锁的粒度在不同的存储引擎表现是不一样的。
   表锁(table lock)顾名思义,就是锁住整张表,其他连接不能进行任何操作,是开销最小的锁。
   行锁(table lock)顾名思义,就是锁住修改的数据,其他连接不能进行任何操作,是开销最大的锁。

ACID的概念

隔离级别

  • Read Uncommitted(读取未提交内容)
    在该隔离级别,所有事务都可以看到其他未提交事务的执行结果。本隔离级别很少用于实际应用,因为它的性能也不比其他级别好多少。读取未提交的数据,也被称之为脏读(Dirty Read)。
  • Read Committed(读取提交内容)
    这是大多数数据库系统的默认隔离级别(但不是MySQL默认的)。它满足了隔离的简单定义:一个事务只能看见已经提交事务所做的改变。这种隔离级别 也支持所谓的不可重复读(Nonrepeatable Read),因为同一事务的其他实例在该实例处理其间可能会有新的commit,所以同一select可能返回不同结果。
  • Repeatable Read(可重读)
    这是MySQL的默认事务隔离级别,它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行。不过理论上,这会导致另一个棘手的问题:幻读 (Phantom Read)。简单的说,幻读指当用户读取某一范围的数据行时,另一个事务又在该范围内插入了新行,当用户再读取该范围的数据行时,会发现有新的“幻影” 行。InnoDB和Falcon存储引擎通过多版本并发控制(MVCC,Multiversion Concurrency Control)机制解决了该问题。
  • Serializable(可串行化)
    这是最高的隔离级别,它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简言之,它是在每个读的数据行上加上共享锁。在这个级别,可能导致大量的超时现象和锁竞争。
    这四种隔离级别采取不同的锁类型来实现,若读取的是同一个数据的话,就容易发生问题。例如:
    脏读(Drity Read):某个事务已更新一份数据,另一个事务在此时读取了同一份数据,由于某些原因,前一个RollBack了操作,则后一个事务所读取的数据就会是不正确的。
    不可重复读(Non-repeatable read):在一个事务的两次查询之中数据不一致,这可能是两次查询过程中间插入了一个事务更新的原有的数据。
    幻读(Phantom Read):在一个事务的两次查询中数据笔数不一致,例如有一个事务查询了几列(Row)数据,而另一个事务却在此时插入了新的几列数据,先前的事务在接下来的查询中,就会发现有几列数据是它先前所没有的。

死锁

指两个以上的事务在统一资源上相互占用,并请求锁定对方的资源,从而导致恶循循环的现象


上面都是一些理论知识,我们来看看一些实际的例子

死锁

事务1

START TRANSACTION
UPDATE USER SET name = '张三' WHERE id = 1;//A
UPDATE USER SET name = '李四' WHERE id = 2;//B
COMMIT;

事务2

START TRANSACTION
UPDATE USER SET age = 100 WHERE id = 2;//C
UPDATE USER SET age = 101 WHERE id = 1;//D
COMMIT;

我们看两个例子,正常来说这ABCD四个例子相互之间互不干扰,可是巧了,正好A和C同时执行完语句,同时也锁定住了这条数据,双方在准备执行下一条语句的时候发现B和D都被锁住了,于是等啊等,都需要对方等待释放锁,同时又持有对方所需要的锁,陷入了死循环,这就是死锁,目前市面上常用的InnoDB处理死锁的办法就是将最少行级的排他锁释放回滚,这也是相比较而言比较简单,大多数存储引擎所考虑的的算法

隔离级别

创建数据库

CREATE TABLE test( id int unsigned AUTO_INCREMENT, name varchar(100) not null, age int(2) not null, primary key(id) )engine=innodb default charset=utf8;

插入测试数据

insert into test (age,name) values(12,'老王'),(20,'老李');
数据截图

--- --- --- --- --- --- --- --- -- --- --- --- --- --- --- --- --- --- 帅气的分割线--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---

read uncommitted(未提交读)

1.首先我们设置当前的事物模式为read uncommitted(未提交读),这是test表的初始值


用户A

2.我们在创建B用户,针对其中一条数据进行事务修改,修改后查询修改数据;


B用户

3.我们此时在用a用户去查询数据库,我们发现a用户查询到的数据是b用户在事务中修改并且还未提交的数据,如果这时候b用户发生事务回滚,那么a用户就会出现脏读


A用户

Read Committed(读取提交内容)

1.还是回到刚刚那个情景,设置当前模式为read committed,查询test表的所有记录;


A用户

2.在用户A提交事务之前,打开客户端B,更新表test;


B用户

3.这时候B还没有提交,我们看A用户查询的数据,发现他并没有查询到B用户所更改的数据,解决了脏读;


A用户

4.此时我们提交B用户的修改,B用户修改的已经提交到了数据库;


B用户

5.我们在用A用户读取数据,A此时还没有提交数据,我们再次读取数据时,发现数据不一致,这就是不可重复读;(务隔离级别为读提交时,写数据只会锁住相应的行)


A用户

Repeatable Read(可重读)

1.用户A,设置事务模式为repeatable read模式,查询test记录;


A用户

2.用户B,开启新事务修改并提交;


B用户

3.用户A,查询出来的记录结果没变化,没有出现不可重复读问题;


用户A

4.接着A用户执行修改,本来应该是99+1等于100,但是结果为667,这是因为可重复读取是使用mvcc机制,select读取的是快照(历史版本),并且select不会更新版本号,而insert,update,delete会更新版本号,是当前读(当前版本)。

用户A

5.事务隔离级别为可重复读时,如果检索条件有索引(包括主键索引)的时候,默认加锁方式是next-key 锁;如果检索条件没有索引,更新数据时会锁住整张表。一个间隙被事务加了锁,其他事务是不能在这个间隙插入记录的,这样可以防止幻读。


Serializable(可串行化)

最后一种隔离,自己可以下去做一下尝试,这种隔离第一是开销最大,他是表锁,当A用户锁表的时候,b用户并不能进行相对应地操作,并发性极低,很少用到;


参考资料

上一篇下一篇

猜你喜欢

热点阅读