sql 入门经典(第五版) Ryan Stephens 学习笔记 后续——存储引擎+锁

一、引擎基础

1 查看系统支持的存储引擎

  show engines;

2 查看表使用的存储引擎两种方法:

  a、show table status from database_name where name='table_name';

  b、show create table table_name;

 查看使用的默认引擎:

  show variables like 'default_storage_engine';  //MySQL5.5以后默认使用InnoDB存储引擎,

如果显示的格式不好看,可以用g代替行尾分号

有人说用第二种方法不准确,我试了下,关闭掉原先默认的Innodb引擎后根本无法执行show create table table_name指令,因为之前建的是Innodb表,关掉后默认用MyISAM引擎,导致Innodb表数据无法被正确读取。

3 修改表引擎方法

  alter table table_name engine=innodb;

4 关闭Innodb引擎方法

  关闭mysql服务: net stop mysql

  找到mysql安装目录下的my.ini文件:

  找到default-storage-engine=INNODB 改为default-storage-engine=MYISAM

  找到#skip-innodb 改为skip-innodb

  动mysql服务:net start mysql

二、三种常见存储引擎介绍

(一)MyISAM
  它不支持事务,也不支持外键,尤其是访问速度快,对事务完整性没有要求或者以SELECT、INSERT为主的应用基本都可以使用这个引擎来创建表。

每个MyISAM在磁盘上存储成3个文件,其中文件名和表名都相同,但是扩展名分别为:

  • .frm(存储表定义)
  • MYD(MYData,存储数据)
  • MYI(MYIndex,存储索引)

  数据文件和索引文件可以放置在不同的目录,平均分配IO,获取更快的速度。要指定数据文件和索引文件的路径,需要在创建表的时候通过DATA DIRECTORY

和INDEX DIRECTORY语句指定,文件路径需要使用绝对路径。每个MyISAM表都有一个标志,服务器或myisamchk程序在检查MyISAM数据表时会对这个标志进行设置。

MyISAM表还有一个标志用来表明该数据表在上次使用后是不是被正常的关闭了。如果服务器以为当机或崩溃,这个标志可以用来判断数据表是否需要检查和修复。

如果想让这种检查自动进行,可以在启动服务器时使用--myisam-recover现象。这会让服务器在每次打开一个MyISAM数据表是自动检查数据表的标志并进行必要的修复处理。

MyISAM类型的表可能会损坏,可以使用CHECK TABLE语句来检查MyISAM表的健康,并用REPAIR TABLE语句修复一个损坏到MyISAM表。

  MyISAM的表还支持3种不同的存储格式:

  • 静态(固定长度)表
  • 动态表
  • 压缩表

  其中静态表是默认的存储格式。静态表中的字段都是非变长字段,这样每个记录都是固定长度的,这种存储方式的优点是存储非常迅速,容易缓存,出现故障容易恢复;

缺点是占用的空间通常比动态表多。静态表在数据存储时会根据列定义的宽度定义补足空格,但是在访问的时候并不会得到这些空格,这些空格在返回给应用之前已经去掉。

同时需要注意:在某些情况下可能需要返回字段后的空格,而使用这种格式时后面到空格会被自动处理掉。

动态表包含变长字段,记录不是固定长度的,这样存储的优点是占用空间较少,但是频繁到更新删除记录会产生碎片,需要定期执行OPTIMIZE TABLE语句

或myisamchk -r命令来改善性能,并且出现故障的时候恢复相对比较困难。
  压缩表由myisamchk工具创建,占据非常小的空间,因为每条记录都是被单独压缩的,所以只有非常小的访问开支。 


(二)InnoDB
  InnoDB存储引擎提供了具有提交、回滚和崩溃恢复能力的事务安全。
但是对比MyISAM的存储引擎,InnoDB写的处理效率差一些并且会占用更多的磁盘空间以保留数据和索引。
1)自动增长列:
  
InnoDB表的自动增长列可以手工插入,但是插入的如果是空或0,则实际插入到则是自动增长后到值。可以通过"ALTER TABLE...AUTO_INCREMENT=n;"语句强制设置自动

增长值的起始值,默认为1,但是该强制到默认值是保存在内存中,数据库重启后该值将会丢失。可以使用LAST_INSERT_ID()查询当前线程最后插入记录使用的值。如果一次插入多

条记录,那么返回的是第一条记录使用的自动增长值。

  对于InnoDB表,自动增长列必须是索引。如果是组合索引,也必须是组合索引的第一列,但是对于MyISAM表,自动增长列可以是组合索引的其他列,这样插入记录后,自动增长列是按照组合索引到前面几列排序后递增的。

2)外键约束:

  MySQL支持外键的存储引擎只有InnoDB,在创建外键的时候,父表必须有对应的索引,子表在创建外键的时候也会自动创建对应的索引。

在创建索引的时候,可以指定在删除、更新父表时,对子表进行的相应操作,包括restrict、cascade、set null和no action。其中restrict和no action相同,是指限制在子表有关联的情况下,父表不能更新;

casecade表示父表在更新或删除时,更新或者删除子表对应的记录;set null 则表示父表在更新或者删除的时候,子表对应的字段被set null。当某个表被其它表创建了外键参照,那么该表对应的索引或主键被禁止删除。

  可以使用set foreign_key_checks=0;临时关闭外键约束,set foreign_key_checks=1;打开约束。
(三)MEMORY
  
memory使用存在内存中的内容来创建表。每个MEMORY表实际对应一个磁盘文件,格式是.frm。MEMORY类型的表访问非常快,因为它到数据是放在内存中的,并且默认使用HASH索引,但是一旦服务器关闭,

表中的数据就会丢失,但表还会继续存在。 默认情况下,memory数据表使用散列索引,利用这种索引进行“相等比较”非常快,但是对“范围比较”的速度就慢多了。因此,散列索引值适合使用在"="和"<=>"的操作符中,

不适合使用在"<"或">"操作符中,也同样不适合用在order by字句里。如果确实要使用"<"或">"或betwen操作符,可以使用btree索引来加快速度。存储在MEMORY数据表里的数据行使用的是长度不变的格式,

因此加快处理速度,这意味着不能使用BLOB和TEXT这样的长度可变的数据类型。VARCHAR是一种长度可变的类型,但因为它在MySQL内部当作长度固定不变的CHAR类型,所以可以使用

create table tab_memory engine=memory select id,name,age,addr from man order by id;

  使用USING HASH/BTREE来指定特定到索引。

create index mem_hash using hash on tab_memory(city_id);

  在启动MySQL服务的时候使用--init-file选项,把insert into...select或load data infile 这样的语句放入到这个文件中,就可以在服务启动时从持久稳固的数据源中装载表。
  服务器需要足够的内存来维持所在的在同一时间使用的MEMORY表,当不再使用MEMORY表时,要释放MEMORY表所占用的内存,应该执行DELETE FROM或truncate table或者删除整个表。
  每个MEMORY表中放置到数据量的大小,受到max_heap_table_size系统变量的约束,这个系统变量的初始值是16M,同时在创建MEMORY表时可以使用MAX_ROWS子句来指定表中的最大行数。

Mysql 中的锁机制

  Microsoft SQL Server(以下简称SQL Server)作为一种中小型数据库管理系统,已经 
得到了广泛的应用,该系统更强调由系统来管理锁。在用户有SQL请求时,系统分析请求 
,自动在满足锁定条件和系统性能之间为数据库加上适当的锁,同时系统在运行期间常 
常自动进行优化处理,实行动态加锁。对于一般的用户而言,通过系统的自动锁定管理 
机制基本可以满足使用要求,但如果对数据安全、数据库完整性和一致性有特殊要求, 
就必须自己控制数据库的锁定和解锁,这就需要了解SQL Server的锁机制,掌握数据库 
锁定方法。 

锁的多粒度性以及锁升级 

  数据库中的锁是指一种软件机制,用来指示某个用户(也即进程会话,下同)已经占用 
了某种资源,从而防止其他用户做出影响本用户的数据修改或导致数据库数据的非完整 
性和非一致性。这儿所谓资源,主要指用户可以操作的数据行、索引以及数据表等。根 
据资源的不同,锁有多粒度(multigranular)的概念,也就是指可以锁定的资源的层次 
。SQL Server中能够锁定的资源粒度包括:数据库、表、区域、页面、键值(指带有索 
引的行数据)、行标识符(RID,即表中的单行数据)。 

  采用多粒度锁的重要用途是用来支持并发操作和保证数据的完整性。SQL Server根据用 
户的请求,做出分析后自动给数据库加上合适的锁。假设某用户只操作一个表中的部分 
行数据,系统可能会只添加几个行锁(RID)或页面锁,这样可以尽可能多地支持多用户 
的并发操作。但是,如果用户事务中频繁对某个表中的多条记录操作,将导致对该表的 
许多记录行都加上了行级锁,数据库系统中锁的数目会急剧增加,这样就加重了系统负 
荷,影响系统性能。因此,在数据库系统中,一般都支持锁升级(lock escalation)。所 
谓锁升级是指调整锁的粒度,将多个低粒度的锁替换成少数的更高粒度的锁,以此来降 
低系统负荷。在SQL Server中当一个事务中的锁较多,达到锁升级门限时,系统自动将 
行级锁和页面锁升级为表级锁。特别值得注意的是,在SQL Server中,锁的升级门限以 
及锁升级是由系统自动来确定的,不需要用户设置。 

1.共享锁 

  SQL Server中,共享锁用于所有的只读数据操作。共享锁是非独占的,允许多个并发事 
务读取其锁定的资源。默认情况下,数据被读取后,SQL Server立即释放共享锁。例如 
,执行查询“SELECT * FROM my_table”时,首先锁定第一页,读取之后,释放对第一 
页的锁定,然后锁定第二页。这样,就允许在读操作过程中,修改未被锁定的第一页。 
但是,事务隔离级别连接选项设置和SELECT语句中的锁定设置都可以改变SQL Server的 
这种默认设置。例如,“ SELECT * FROM my_table HOLDLOCK”就要求在整个查询过程 
中,保持对表的锁定,直到查询完成才释放锁定。 

2.修改锁 

  修改锁在修改操作的初始化阶段用来锁定可能要被修改的资源,这样可以避免使用共享 
锁造成的死锁现象。因为使用共享锁时,修改数据的操作分为两步,首先获得一个共享 
锁,读取数据,然后将共享锁升级为独占锁,然后再执行修改操作。这样如果同时有两 
个或多个事务同时对一个事务申请了共享锁,在修改数据的时候,这些事务都要将共享 
锁升级为独占锁。这时,这些事务都不会释放共享锁而是一直等待对方释放,这样就造 
成了死锁。如果一个数据在修改前直接申请修改锁,在数据修改的时候再升级为独占锁 
,就可以避免死锁。修改锁与共享锁是兼容的,也就是说一个资源用共享锁锁定后,允 
许再用修改锁锁定。 

3.独占锁 

独占锁是为修改数据而保留的。它所锁定的资源,其他事务不能读取也不能修改。独占 
锁不能和其他锁兼容。 

4.结构锁 

结构锁分为结构修改锁(Sch-M)和结构稳定锁(Sch-S)。执行表定义语言操作时,SQL 
Server采用Sch-M锁,编译查询时,SQL Server采用Sch-S锁。 

5.意向锁 

意向锁说明SQL Server有在资源的低层获得共享锁或独占锁的意向。例如,表级的共享 
意向锁说明事务意图将独占锁释放到表中的页或者行。意向锁又可以分为共享意向锁、 
独占意向锁和共享式独占意向锁。共享意向锁说明事务意图在共享意向锁所锁定的低层 
资源上放置共享锁来读取数据。独占意向锁说明事务意图在共享意向锁所锁定的低层资 
源上放置独占锁来修改数据。共享式独占锁说明事务允许其他事务使用共享锁来读取顶 
层资源,并意图在该资源低层上放置独占锁。 

6.批量修改锁 

批量复制数据时使用批量修改锁。可以通过表的TabLock提示或者使用系统存储过程sp_t 
ableoption的“table lock on bulk load”选项设定批量修改锁。 

2、事务并发会产生什么问题

1)第一类丢失更新:在没有事务隔离的情况下,两个事务都同时更新一行数据,但是第二个事务却中途失败退出, 导致对数据的两个修改都失效了。

例如:

       张三的工资为5000,事务A中获取工资为5000,事务B获取工资为5000,汇入100,并提交数据库,工资变为5100,

       随后

       事务A发生异常,回滚了,恢复张三的工资为5000,这样就导致事务B的更新丢失了。

2)脏读:脏读就是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。
例如:
  张三的工资为5000,事务A中把他的工资改为8000,但事务A尚未提交。
  与此同时,
  事务B正在读取张三的工资,读取到张三的工资为8000。
  随后,
  事务A发生异常,而回滚了事务。张三的工资又回滚为5000。
  最后,
  事务B读取到的张三工资为8000的数据即为脏数据,事务B做了一次脏读。

3)不可重复读:是指在一个事务内,多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的的数据可能是不一样的。这样就发生了在一个事务内两次读到的数据是不一样的,因此称为是不可重复读。
例如:
  在事务A中,读取到张三的工资为5000,操作没有完成,事务还没提交。
  与此同时,
  事务B把张三的工资改为8000,并提交了事务。
  随后,
  在事务A中,再次读取张三的工资,此时工资变为8000。在一个事务中前后两次读取的结果并不致,导致了不可重复读。

4)第二类丢失更新:不可重复读的特例。有两个并发事务同时读取同一行数据,然后其中一个对它进行修改提交,而另一个也进行了修改提交。这就会造成第一次写操作失效。 

例如:

在事务A中,读取到张三的存款为5000,操作没有完成,事务还没提交。
  与此同时,
  事务B,存储1000,把张三的存款改为6000,并提交了事务。
  随后,
  在事务A中,存储500,把张三的存款改为5500,并提交了事务,这样事务A的更新覆盖了事务B的更新。

5)幻读:是指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好象发生了幻觉一样。
例如:
  目前工资为5000的员工有10人,事务A读取所有工资为5000的人数为10人。
  此时,
  事务B插入一条工资也为5000的记录。
  这是,事务A再次读取工资为5000的员工,记录为11人。此时产生了幻读。

提醒:
不可重复读的重点是修改,同样的条件,你读取过的数据,再次读取出来发现值不一样了
幻读的重点在于新增或者删除,同样的条件,第 1 次和第 2 次读出来的记录数不一样

3、事务隔离级别,解决什么并发问题,以及存在什么并发问题

(1)READ_UNCOMMITTED
  这是事务最低的隔离级别,它充许另外一个事务可以看到这个事务未提交的数据。
  解决第一类丢失更新的问题,但是会出现脏读、不可重复读、第二类丢失更新的问题,幻读 。
(2)READ_COMMITTED
  保证一个事务修改的数据提交后才能被另外一个事务读取,即另外一个事务不能读取该事务未提交的数据。
  解决第一类丢失更新和脏读的问题,但会出现不可重复读、第二类丢失更新的问题,幻读问题
(3)REPEATABLE_READ
  保证一个事务相同条件下前后两次获取的数据是一致的

       解决第一类丢失更新,脏读、不可重复读、第二类丢失更新的问题,但会出幻读。
(4)SERIALIZABLE
  事务被处理为顺序执行。
  解决所有问题

提醒:

Mysql默认的事务隔离级别为repeatable_read

原文地址:https://www.cnblogs.com/NeilZhang/p/5723066.html