Mysql事务实现的底层原理*
事务(Transaction)是访问和更新数据库的程序执行单元;事务中可能包含一个或多个sql语句,这些语句要么都执行,要么都不执行。作为一个关系型数据库,MySQL支持事务。
数据库的ACID
原子性(Atomicity)原子性是指一个事务是一个不可分割的工作单位,其中的操作要么都做,要么都不做;如果事务中一个sql语句执行失败,则已执行的语句也必须回滚,数据库退回到事务前的状态。实现原子性的关键,是当事务回滚时能够撤销所有已经成功执行的sql语句。InnoDB实现回滚,靠的是undo log:当事务对数据库进行修改时,InnoDB会生成对应的undo log;如果事务执行失败或调用了rollback,导致事务需要回滚,便可以利用undo log中的信息将数据回滚到修改之前的样子。
一致性(Consistency)数据的一致性通常指关联数据之间的逻辑关系是否正确和完整。数据库中的数据保持在一致状态,虽然数据有可能会变化,但是状态一直是一致的。例如对银行转帐事务,不管事务成功还是失败,应该保证事务结束后ACCOUNTS表中Tom和Jack的存款和不变。
隔离性(Isolation)隔离性是指,事务内部的操作与其他事务是隔离的,并发执行的各个事务之间不能互相干扰。严格的隔离性,对应了事务隔离级别中的Serializable (可串行化)。
持久性(Durability)持久性是指事务一旦提交,它对数据库的改变就应该是永久性的。接下来的其他操作或故障不应该对其有任何影响。InnoDB提供了缓存(Buffer Pool)来提高读写效率,但是为了防止宕机等问题引发的数据丢失,引入了redo log来记录数据修改操作。事务提交时使用redo log刷盘,宕机后可以重读redo log回复。
四种隔离级别的实现方式
针对隔离性遇到的问题如下:
- 脏读(如有事务A和B,A读取了B未提交的数据)
- 不可重复读(如有事务A和B,A负责读取,B负责写入,A连续读的过程中B写入了一次,A前后两次读出来的数据不一样)
- 幻读(如有事务A和B,A修改表内数据的过程中,B向表内插入了一条数据,A修改完后发现数据并没有被全部修改完)
解决方法
未提交读(Read Uncommitted,RU):
允许脏读,其他事务只要修改了数据,即使未提交,本事务也能看到修改后的数据值。也就是在读取时是不会加锁的,但在更新数据时,对其加行级共享锁(其它事务不能更改,但可以读取,导致脏读)。
例子:事务A读取某行记录时,事务B也能对这行记录读取更新,当事务B更新记录时,事务A读取到事务B修改的版本,即使事务B未提交。
提交读(Read Committed,RC):
只能读取到已经提交的数据。解决了脏读问题,解决方式是给写数据加行级排他锁,这样写过程是无法读取的,直到事务处理完毕才释放排他锁,给读的数据加行级共享锁,这样读的时候也是无法写的,但是一旦读完该行就释放共享锁。这种模式下虽然处理了脏读,但是并没有处理丢失更新和不可重复读的问题。Oracle等多数数据库默认都是该级别 (不重复读)。
举例说明:
事务A负责读,事务B负责写,A读完数据后释放共享锁,B更新数据,事务还未结束,A再读,两次得到数据不一样,产生不可重复读的问题。
可重复读(Repeated Read,RR):
MySQL数据库默认的隔离级别
可重复读。无论其他事务是否修改并提交了数据,在这个事务中看到的数据值始终不受其他事务影响。该sql第一次读取到数据后,就将这些数据加锁,其它事务无法修改这些数据,就可以实现可重复读了。但这种方法却无法锁住insert的数据,所以当事务A先前读取了数据,或者修改了全部数据,事务B还是可以insert数据提交,这时事务A就会发现莫名其妙多了一条之前没有的数据,幻读不能通过行锁来避免
举例说明:
事务A负责读,事务B负责写,A读完数据后等事务结束才释放共享锁,B更新数据,直到事务结束,A再读,两次得到数据均为A第一次读到的数据,解决不可重复读的问题。
序列化(Serializable):
完全串行化的读,每次读都需要获得表级共享锁,读写相互都会阻塞。
数据库如何加行锁?
共享锁(Shared Lock,也叫S锁)
共享锁(S)表示对数据进行读操作,因此多个事物可以同时为一个对象加锁。这个事物释放锁之前,其他事物不能对该对象进行更新操作。产生共享锁的语句是:
select * from t_user lock in share mode;
排他锁(Exclusive Lock,也叫X锁)
排他锁表示对数据进行写操作,如果一个事物给一个对象添加了排他锁,其他事物就不能给他加其他锁。产生排他锁的SQL语句如下:
select * from ad_plan for update;
行锁
行锁表示对一条记录加锁,只影响一条记录。通常用在DML语句中,如INSERT, UPDATE, DELETE等。
InnoDB行锁是通过给索引上的索引项加锁来实现的,这一点MySQL与Oracle不同,后者是通过在数据块中对相应数据行加锁来实现的。
InnoDB这种行锁实现特点意味着:只有通过索引条件检索数据,InnoDB才使用行级锁,否则,InnoDB将使用表锁!
如果是普通索引如何查找到对应的数据?
这个操作叫做回表
InnoDB聚集索引的叶子节点存储行记录,因此, InnoDB必须要有,且只有一个聚集索引:
(1)如果表定义了PK,则PK就是聚集索引;
(2)如果表没有定义PK,则第一个not NULL unique列是聚集索引;
(3)否则,InnoDB会创建一个隐藏的row-id作为聚集索引;
InnoDB普通索引的叶子节点存储主键值。
什么是数据库分表分库?
着业务系统的扩大,系统变得越来越复杂,越来越难以维护,开发效率变得越来越低,并且对资源的消耗也变得越来越大,通过硬件提高系统性能的方式带来的成本也越来越高。随着业务规模的增大,访问量的增大,我们不得不对业务进行拆分。每一个模块都使用单独的数据库来进行存储,不同的业务访问不同的数据库。
数据切分根据其切分类型,可以分为两种方式:垂直(纵向)切分和水平(横向)切分
垂直切分常见有垂直分库和垂直分表两种。
垂直分库就是根据业务耦合性,将关联度低的不同表存储在不同的数据库。做法与大系统拆分为多个小系统类似,按业务分类进行独立划分。
垂直分表是基于数据库中的"列"进行,某个表字段较多,可以新建一张扩展表,将不经常用或字段长度较大的字段拆分出去到扩展表中。
水平切分分为库内分表和分库分表
当一个应用难以再细粒度的垂直切分,或切分后数据量行数巨大,存在单库读写、存储性能瓶颈,这时候就需要进行水平切分了。
是根据表内数据内在的逻辑关系,将同一个表按不同的条件分散到多个数据库或多个表中,每个表中只包含一部分数据,从而使得单个表的数据量变小,达到分布式的效果。
MySQL中的redo log和bin log。
redo log
在mysql中存在一个问题,每次更新数据都需要将数据写进磁盘,磁盘需要先找到那条对应的记录,然后再更新,整个过程IO成本都很高。为了解决这个问题,提高更新效率,MySQL用到了WAL技术,WAL技术的全称是Write-Ahead Logging,它的关键点是先写日志,再写磁盘。redo log是InnoDB引擎独有的日志。当有一条记录需要更新的时候,InnoDB 引擎就会先把记录写到 redo log里面,并更新内存,这个时候更新就算完成了。同时,InnoDB 引擎会在适当的时候,将这个操作记录更新到磁盘里面,而这个更新往往是在系统比较空闲的时候做。InnoDb的redo log是固定大小的。从头开始写,写道末尾就又回到开头循环写。有了 redo log,InnoDB 就可以保证即使数据库发生异常重启,之前提交的记录都不会丢失,这个称为crash-safe
binlog
MySQL从整体看一共分为两层:一块是 Server 层,它主要做的是 MySQL 功能层面的事情;还有一块是引擎层,负责存储相关的具体事宜。上面我们聊到的粉板 redo log 是 InnoDB 引擎特有的日志,而 Server 层也有自己的日志,称为 binlog(归档日志)。
两种日志的不同点
1.redo log是InnoDB引擎特有的;binlog是MySQL的Server层实现的,所有引擎都可以使用。
2.redo log是物理日志,记录的是“在某个数据页上做了什么修改”;binlog是逻辑日志,记录的是这个语句的原始逻辑。
3.redo log 是循环写的,固定空间会被消耗完,binlog是可以追加写入的,binlog文件写到一定大小后会切换到下一个,并不会覆盖以前的日志
MySQL语法,写了两个个SQL题目,如果一个表非常大,一个表很小,怎么优化查询效率
优化原则,小表驱动大小,即小的数据集驱动大的数据集。
select * from A where id in(select id from b),
当B表的数据集必须小于A表的数据集时,用in优于exists.
select *from A where exists (select 1 from B where b.id=a.id)。
当A表的数据集小于B表的数据集时,用exists优于in。
注意:A表和B表的ID字段应建立索引。
主键自增的底层是如何实现的?
不同的引擎对于自增值的保存策略不同
1.MyISAM引擎的自增值保存在数据文件中
在MySQL8.0版本,将自增值的变更记录在了redo log中,重启的时候依靠redo log恢复重启之前的值
如果字段id被定义为AUTO_INCREMENT,在插入一行数据的时候,自增值的行为如下:
1.如果插入数据时id字段指定为0、null或未指定值,那么就把这个表当前的AUTO_INCREMENT值填到自增字段
2.如果插入数据时id字段指定了具体的值,就直接使用语句里指定的值
从auto_increment_offset(初始值)开始,以auto_increment_increment(步长)为步长,持续叠加,直到找到第一个大于X的值,作为新的自增值
mysql讲讲rr和rc的区别(readview和mvcc那一套讲讲)
详解:link
RR指的是可重复读,RC指的是读已提交。
版本链机制 , readView
innodb表下存在两个隐藏列:
trx_id : 存放事务id(这里就对应MVCC的DB_TRX_ID)
roll_pointer : 存放一个指向上一事务版本的指针(这里就对应DB_ROLL_PTR
)
版本链的作用:
用一个单链表的结构存放每个事务版本对应的行的数据; 通过我当前的事务id可以获取对应的数据
配合readView的活跃事务数组可以判断当前应该读取哪个节点的数据, 如果我当前事务不在版本链上就向前读取节点直到读取的节点ID不在活跃事务数组中, 那么这个版本的数据就是最新的已提交数据
ReadView中主要包含4个比较重要的内容:
- m_ids:表示在生成ReadView时当前系统中活跃的读写事务的事务id列表。
- min_trx_id:表示在生成ReadView时当前系统中活跃的读写事务中最小的事务id,也就是m_ids中的最小值。
- max_trx_id:表示生成ReadView时系统中应该分配给下一个事务的id值。
- creator_trx_id:表示生成该ReadView的事务的事务id。
有了这个ReadView,这样在访问某条记录时,只需要按照下边的步骤判断记录的某个版本是否可见:
1)如果被访问版本的trx_id属性值与ReadView中的creator_trx_id值相同,意味着当前事务在访问它自己修改过的记录,所以该版本可以被当前事务访问。
2)如果被访问版本的trx_id属性值小于ReadView中的min_trx_id值,表明生成该版本的事务在当前事务生成ReadView前已经提交,所以该版本可以被当前事务访问。
3)如果被访问版本的trx_id属性值大于ReadView中的max_trx_id值,表明生成该版本的事务在当前事务生成ReadView后才开启,所以该版本不可以被当前事务访问。
4)如果被访问版本的trx_id属性值在ReadView的min_trx_id和max_trx_id之间,那就需要判断一下trx_id属性值是不是在m_ids列表中,如果在,说明创建ReadView时生成该版本的事务还是活跃的,该版本不可以被访问;如果不在,说明创建ReadView时生成该版本的事务已经被提交,该版本可以被访问
READ COMMITED实现原理
每次读取数据前都生成一个ReadView
REPEATABLE READ
在第一次读取数据时生成一个ReadView
READ COMMITTD、 REPEATABLE READ这两个隔离级别的一个很大不同就是:生成ReadView的时机不同, READ COMMITTD在每一次进行普通SELECT操作前都会生成一个ReadView,而REPEATABLE READ只在第一次进行普通SELECT操作前生成一个ReadView,之后的查询操作都重复使用这个ReadView就好了
用ReadView怎么解释Repeatable read 隔离级别产生的幻读原因呢?
INSERT操作对应的undo日志没有roll_pointer该属性,因为该记录并没有更早的版本
mysql的两个存储引擎
Innodb可以使用事务,MyISAM不支持事务
Innodb使用行级锁,MyISAM使用表级锁
Innodb无法使用全局索引(也可以了),MyISAM可以使用全局索引
Innodb支持MVCC,MyISAM不支持MVCC
mysql的索引种类4种
主键索引:特殊的唯一索引,一个表只能有一个主键,不允许有空值
普通索引:数据可以重复,无限制
组合索引:在多个字段上创建索引,只有在查询条件中使用了创建索引时的第一个字,索引才会被使用。
唯一索引:索引列的值必须唯一,但允许有空值,如果是组合索引,那么组合必须唯一
Innodb的索引结构
使用的是B+树索引,即每层都有指向下层的索引,所有数据存放在叶子节点上,而且叶子节点之间形成链表结构。有两个指针,一个指向根节点,另一个指向叶子节点中最小的节点
B+树的优缺点
优点
1、单次请求涉及的磁盘IO次数少(出度d大,且非叶子节点不包含表数据,树的高度小);
2、查询效率稳定(任何关键字的查询必须走从根结点到叶子结点,查询路径长度相同);
3、遍历效率高(从符合条件的某个叶子节点开始遍历即可);
为什么是B+树而不是B树呢,因为它内节点不存储data,这样一个节点就可以存储更多的key。
缺点
B+树最大的性能问题在于会产生大量的随机IO,主要存在以下两种情况:
主键不是有序递增的,导致每次插入数据产生大量的数据迁移和空间碎片;
即使主键是有序递增的,大量写请求的分布仍是随机的;
选择联合索引还是多索引,最左前缀的规则
多个单列索引在多条件查询时只会生效第一个索引!所以多条件联合查询时最好建联合索引!
最左前缀的规则
你可以认为联合索引是闯关游戏的设计你必须匹配了第一关,才能匹配第二关,匹配了第一关和第二关,才能匹配第三关。你不能直接到第二关的。索引是因为B+树结构 所以查找快 如果单看第三列 是非排序的。多列索引是先按照第一列进行排序,然后在第一列排好序的基础上再对第二列排序,如果没有第一列的话,直接访问第二列,那第二列肯定是无序的,直接访问后面的列就用不到索引了。
mysql如果abc联合索引,那么b进行查询,走索引吗
不走。因为mysql底层是由B+树实现的,当建立联合索引时,b+树是按照从左到右的顺序来建立搜索树的,这导致b+树中只有a的值是有序的,而b和c的值是无序的,b依靠a来索引,c依靠b来索引。联合索引时,b+树会先比较a来确定下一步的所搜方向,如果a相同再依次比较b和c,这才完成索引的流程。直接用第二列进行查询,b+树就不知道怎么找索引,因此不走索引。直接用a和c索引时,由于缺失b字段,所以只能把所有a匹配全部找到,再匹配c。
CAP理论的关系
CAP 原则又称 CAP 定理,指的是在一个分布式系统中,一致性(Consistency)、可用性(Availability)、分区容错性(Partition tolerance)。CAP 原则表明,这三个要素最多只能同时实现两点,不可能三者兼顾。
CA without P:如果不要求P(不允许分区),则C(强一致性)和A(可用性)是可以保证的。但放弃P的同时也就意味着放弃了系统的扩展性,也就是分布式节点受限,没办法部署子节点,这是违背分布式系统设计的初衷的。
CP without A:如果不要求A(可用),相当于每个请求都需要在服务器之间保持强一致,而P(分区)会导致同步时间无限延长(也就是等待数据同步完才能正常访问服务),一旦发生网络故障或者消息丢失等情况,就要牺牲用户的体验,等待所有数据全部一致了之后再让用户访问系统。设计成CP的系统其实不少,最典型的就是分布式数据库,如Redis、HBase等。对于这些分布式数据库来说,数据的一致性是最基本的要求,因为如果连这个标准都达不到,那么直接采用关系型数据库就好,没必要再浪费资源来部署分布式数据库。
AP wihtout C:要高可用并允许分区,则需放弃一致性。一旦分区发生,节点之间可能会失去联系,为了高可用,每个节点只能用本地数据提供服务,而这样会导致全局数据的不一致性。典型的应用就如某米的抢购手机场景,可能前几秒你浏览商品的时候页面提示是有库存的,当你选择完商品准备下单的时候,系统提示你下单失败,商品已售完。这其实就是先在 A(可用性)方面保证系统可以正常的服务,然后在数据的一致性方面做了些牺牲,虽然多少会影响一些用户体验,但也不至于造成用户购物流程的严重阻塞。
SQL注入问题
SQL注入是比较常见的网络攻击方式之一,它不是利用操作系统的BUG来实现攻击,而是针对程序员编写时的疏忽,通过SQL语句,实现无账号登录,甚至篡改数据库。
1.(简单又有效的方法)PreparedStatement
采用预编译语句集,它内置了处理SQL注入的能力,只要使用它的setXXX方法传值即可。
Mysql如何查看你的sql执行情况。
使用explain关键字,看id,type,key,possible key extra。看是否是用索引,可以看key的值
索引失效问题*
1.全值匹配我最爱
2.最佳左前缀法则,带头大哥不能死,中间兄弟不能少。
3.不要在索引列上做任何操作
4.范围条件后列上索引失效
5.尽量使用覆盖索引减少使用select *
6.使用不等于(!= 或者<>)不能使用索引
7.使用 is null 或者 is not null 也不能使用索引
8.like 已通配符开头(%abc)导致索引失效 (解决方法:使用覆盖索引)
9.少用or,用它来连接索引会失效
数据库如何优化*
1、不要使用游标。
使用游标不仅占用内存,而且还用不可思议的方式锁定表,它们可以使DBA所能做的一切性能优化等于没做。游标里每执行一次fetch就等于执行一次select。
2、创建适当的索引
每当为一个表添加一个索引,select会更快,可insert和delete却大大变慢,因为创建了维护索引需要许多额外的工作。
(1)采用函数处理的字段不能利用索引
(2)条件内包括了多个本表的字段运算时不能进行索引
3、使用事务
对于一些耗时的操作,使用事务可以达到很好的优化效果。
4、小心死锁
按照一定的次序来访问你的表。如果你先锁住表A,再锁住表B,那么在所有的存储过程中都要按照这个顺序来锁定它们。 如果某个存储过程先锁定表B,再锁定表A,这可能会导致一个死锁。
5、不要打开大的数据集
6、不要使用服务器端游标
与服务器端游标比起来,客户端游标可以减少服务器和网络的系统开销,并且还减少锁定时间。
7、不要忽略同时修改同一记录的问题
有时候,两个用户会同时修改同一记录,这样,后一个修改者修改了前一个修改者的操作,某些更新就会丢失。处理这种情况,创建一个timestamp字段,在写入前检查它,如果允许,就合并修改,如果存在冲突,提示用户。
8、尽量不要使用text数据类型
除非使用text处理一个很大的数据,否则不要使用它。因为它不易于查询,速度慢,用的不好还会浪费大量的空间。一般varchar可以更好的处理数据。
9、避免在索引列上使用计算
where子句中,如果索引列是函数的一部分,优化器将不使用索引而使用全表扫描。例如:
(低效)select … from [dept] where [sal]*12>25000;
(高效)select … from [dept] where [sal]>25000/12;
mysql主从复制*
原因:
1.读写分离,使数据库能支撑更大的并发
2.发扬不同表引擎的优点
目前Myisam表的查询速度比innodb略快,而写入并发innodb比myIsam要好。那么,我们可以使用innodb作为master,处理高并发写入,使用master作为slave,接受查询。或在myisam slave中建立全文索引,解决innodb无全文索引的弱点。
做主从复制(读写分离)来减轻主数据库的负载。 而且如果主数据库宕机,可快速将业务系统切换到从数据库上,可避免数据丢失。保证主服务器(Master)和从服务器(Slave)的数据是一致性的,向Master插入数据后,Slave会自动从Master把修改的数据同步过来(有一定的延迟),通过这种方式来保证数据的一致性
步骤
- Master 将改变记录到二进制日志中。
- Slave 将 Master 的二进制日志拷贝到它的中继日志( Relay_log )
- Slave 重做中继日志中的事件,将改变反映它自己的数据
详细步骤 - 在Slave 服务器上执行sart slave命令开启主从复制开关,开始进行主从复制。
- 此时,Slave服务器的IO线程会通过在master上已经授权的复制用户权限请求连接master服务器,并请求从执行binlog日志文件的指定位置之后开始发送binlog日志内容
- Master服务器接收到来自Slave服务器的IO线程的请求后,二进制转储IO线程会根据Slave服务器的IO线程请求的信息分批读取指定binlog日志文件指定位置之后的binlog日志信息,然后返回给Slave端的IO线程。返回的信息中除了binlog日志内容外,还有在master服务器端记录的新的binlog文件名称,以及在新的binlog中的下一个指定更新位置。
- 当Slave服务器的IO线程获取到Master服务器上IO线程发送的日志内容、日志文件及位置点后,会将binlog日志内容依次写到Slave端自身的Relay Log(即中继日志)文件的最末端,并将新的binlog文件名和位置记录到master-info文件中,以便下一次读取master端新binlog日志时能告诉Master服务器从新binlog日志的指定文件及位置开始读取新的binlog日志内容
- Slave服务器端的SQL线程会实时检测本地Relay Log 中IO线程新增的日志内容,然后及时把Relay LOG 文件中的内容解析成sql语句,并在自身Slave服务器上按解析SQL语句的位置顺序执行应用这样sql语句,并在relay-log.info中记录当前应用中继日志的文件名和位置点
DML、DDL、DCL
- DML(data manipulation language)数据操纵语言:
就是我们最经常用到的 SELECT、UPDATE、INSERT、DELETE。 主要用来对数据库的数据进行一些操作。 - DDL(data definition language)数据库定义语言:
其实就是我们在创建表的时候用到的一些sql,比如说:CREATE、ALTER、DROP等。DDL主要是用在定义或改变表的结构,数据类型,表之间的链接和约束等初始化工作上 - DCL(Data Control Language)数据库控制语言:
是用来设置或更改数据库用户或角色权限的语句,包括(grant,deny,revoke等)语句。这个比较少用到。
索引好与不好的地方
创建索引可以大大提高系统的性能:
第一,通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。
第二,可以大大加快数据的检索速度,这也是创建索引的最主要的原因。
第三,可以加速表和表之间的连接,特别是在实现数据的参考完整性方面特别有意义。
第四,在使用分组和排序 子句进行数据检索时,同样可以显著减少查询中分组和排序的时间。
第五,通过使用索引,可以在查询的过程中,使用优化隐藏器,提高系统的性能。
增加索引也有许多不利的方面:
第一,创建索引和维护索引要耗费时间,这种时间随着数据量的增加而增加。
第二,索引需要占物理空间,除了数据表占数据空间之外,每一个索引还要占一定的物理空间,如果要建立聚簇索引,那么需要的空间就会更大。
第三,当对表中的数据进行增加、删除和修改的时候,索引也要动态的维护,这样就降低了数据的维护速度。
如何解决幻读(MVCC和间隙锁)如何两边范围
MVCC能否解决幻读?
“幻读”指,同一个事务里面连续执行两次同样的sql语句,可能导致不同结果的问题,第二次sql语句可能会返回之前不存在的行。
先给出结论:不能笼统的说能不能解决,因为有的情况下可以解决,但是有的情况下解决不了。
可以解决的情况
mysql里面实际上有两种读,一种是“快照读”,比如我们使用select进行查询,就是快照读,在“快照读"的情况下是可以解决“幻读”的问题的。使用的就是MVCC。
不可以解决的情况
1.a事务先select,b事务insert确实会加一个gap锁,但是如果b事务commit,这个gap锁就会释放(释放后a事务可以随意操作),
2.a事务再select出来的结果在MVCC下还和第一次select一样,
3.接着a事务不加条件地update,这个update会作用在所有行上(包括b事务新加的),
4.a事务再次select就会出现b事务中的新行,并且这个新行已经被update修改了.
上面这样,事务2提交之后,事务1再次执行update,因为这个是当前读,他会读取最新的数据,包括别的事务已经提交的,所以就会导致此时前后读取的数据不一致,出现幻读。
解决方法:
1、select * from table where ? lock in share mode; (加共享锁)
2、select * from table where ? for update; (加排它锁)
因此总结一下,下面几个语句都是当前读,都会读取最新的快照数据,都会加锁(除了第一个加共享锁,其他都是互斥锁):
事务1在update后,对该数据加锁,事务B无法插入新的数据,这样事务A在update前后数据保持一致,避免了幻读,可以明确的是,update锁的肯定不只是已查询到的几条数据,因为这样无法阻止insert,有同学会说,那就是锁住了整张表呗,其实不是,其实这里的锁,是next-key locking(就是一个行锁+范围锁)实现的.行锁不必说,就是更新的时候锁住这一行,这样别的事务就不能同时进行修改操作了。范围锁(gap lock)锁则是防止插入。
next-key 锁 (当前读)
next-key 锁包含两部分
记录锁(行锁)
间隙锁
记录锁是加在索引上的锁,间隙锁是加在索引之间的。
原理:将当前数据行与上一条数据和下一条数据之间的间隙锁定,保证此
在mysql的innoDB存储引擎中,如果更新操作是针对一个区间的,那么它会锁住这个区间内所有的记录,比如update xxx where id between a and b那么它会锁住a到b之间所有记录,注意是所有记录,甚至这个记录并不存在也会被锁住,这个时候,如果另外一个连接需要插入一条记录到a到b之间,那么它就必须等到上一个事务结束。
典型的例子就是使用auto_increment id,由于这个id是一直往上分配的,因此两个事务都insert时,会得到两个不同的,但是这两条记录还没有被提交,因此也就不存在,如果这个时候有一个事务进行范围操作,而且恰好要锁住不存在的,就是触发间隙锁问题。
当我们用范围条件而不是相等条件检索数据,并请求共享或排他锁时,InnoDB会给符合条件的已有数据记录的索引项加锁;对于键值在条件范围内但并不存在的记录,叫做“间隙(GAP)”,InnoDB也会对这个“间隙”加锁,这种锁机制就是所谓的间隙锁(Next-Key锁)。
举例来说,假如emp表中只有101条记录,其empid的值分别是 1,2,…,100,101,下面的SQL:
Select * from emp where empid > 100 for update;
是一个范围条件的检索,InnoDB不仅会对符合条件的empid值为101的记录加锁,也会对empid大于101(这些记录并不存在)的“间隙”加锁。
InnoDB使用间隙锁的目的,一方面是为了防止幻读,以满足相关隔离级别的要求,对于上面的例子,要是不使用间隙锁,如果其他事务插入了empid大于100的任何记录,那么本事务如果再次执行上述语句,就会发生幻读
B树和B+树比较**
性能上
- 不同于B树只适合随机检索,B+树同时支持随机检索和顺序检索;
- B+树的磁盘读写代价更低。B+树的内部结点并没有指向关键字具体信息的指针,其内部结点比B树小,盘块能容纳的结点中关键字数量更多,一次性读入内存中可以查找的关键字也就越多,相对的,IO读写次数也就降低了。而IO读写次数是影响索引检索效率的最大因素。
- B+树的查询效率更加稳定。B树搜索有可能会在非叶子结点结束,越靠近根节点的记录查找时间越短,只要找到关键字即可确定记录的存在,其性能等价于在关键字全集内做一次二分查找。而在B+树中,顺序检索比较明显,随机检索时,任何关键字的查找都必须走一条从根节点到叶节点的路,所有关键字的查找路径长度相同,导致每一个关键字的查询效率相当。
- 数据库索引采用B+树的主要原因是,)B-树在提高了磁盘IO性能的同时并没有解决元素遍历的效率低下的问题。B+树的叶子节点使用指针顺序连接在一起,只要遍历叶子节点就可以实现整棵树的遍历。而且在数据库中基于范围的查询是非常频繁的,而B树不支持这样的操作(或者说效率太低)。
为什么查询B树和红黑树效率会慢
因为不平衡,而且无法做到范围查询。红黑树和B+树比呢?多叉,本质原因就是树的高度会低!树的高度过高,高度越高,查找速度越慢
mysql的int多少个字节?例如int(10)和int(5)有什么区别
char:1~255个字符的定长串,它的长度必须在创建时指定,否则MySQL假定为CHAR(1)
VARCHAR:可变长度,最多不超过255字节,如在创建时指定VARCHAR(n),则可存储0~n个字符的变长串
TEXT:最大长度为64K的变长文本
int(4字节)、mediumint(三字节)、smallint(2字节)、tinyint(1字节)
先说结论,对于存储和计算来说,int(10)和int(11)没有区别,10代表数字长度,但是int类型占用4个字节,取值范围是-2147483648~2147483647,也就是最大长度是10,如果写成int(11), 插入11位数的时候是插入不进去的,mysql会默认int最大10位(2147483647),2147483648也将插入不了。
密集索引和稀疏索引的差别,Innodb为什么一定要有主键?
区别
密集索引文件中的每个搜索码值都对应一个索引值
稀疏索引文件只为索引码的某些值建立索引项
myIsam存储引擎,不管是主键索引,唯一键索引还是普通索引都是稀疏索引
innodb存储引擎:有且只有一个密集索引。因为标识一条数据是唯一的,而且它和MyISAM不一样,它的数据是存在一起的,只能通过主键去取得唯一。
密集索引的选取规则如下:
1.若主键被定义,则主键作为密集索引
2.如果没有主键被定义,该表的第一个唯一非空索引则作为密集索引
3.若不满足以上条件,innodb内部会生成一个隐藏主键(密集索引)
4. 非主键索引存储相关键位和其对应的主键值,包含两次查找
数据库三范式:
第一范式:1NF是对属性的原子性约束,要求字段具有原子性,不可再分解;(只要是关系型数据库都满足1NF)
第二范式:2NF是在满足第一范式的前提下,非主键字段不能出现部分依赖主键;解决:消除复合主键就可避免出现部分以来,可增加单列关键字。
第三范式:3NF是在满足第二范式的前提下,非主键字段不能出现传递依赖,比如某个字段a依赖于主键,而一些字段依赖字段a,这就是传递依赖。解决:将一个实体信息的数据放在一个表内实现。
说人话:
第一范式:每个列都不可以再拆分。
第二范式:在第一范式的基础上,非主键列完全依赖于主键,而不能是依赖于主键的一部分。
第三范式:在第二范式的基础上,非主键列只依赖于主键,不依赖于其他非主键。
数据库索引(B+树和hash)B+树和红黑树有什么区别(B+树不能支持范围查询)
正因为文件系统和数据库一般都是存在电脑硬盘上的,如果数据量太大的话不一定能一次性加载到内存中。(一棵树不能一次性加载完怎么查找对吧?)但是B树可以多路存储。也正因为B树的这一个优点,可以在文件查找的时候每次只加载一个节点的内容存入内存来查找。而红黑树在内存中查找非常块,但是如果在数据库和文件系统中,显然B树更优。
B+树是在B树的基础上进行改造的,他的数据都在叶子节点,同时叶子节点之间还加了指针形成链表。但是B+树不能支持范围查询
全文索引
通过数值比较、范围过滤等就可以完成绝大多数我们需要的查询,但是,如果希望通过关键字的匹配来进行查询过滤,那么就需要基于相似度的查询,而不是原来的精确数值比较。全文索引就是为这种场景设计的。
你可能会说,用 like + % 就可以实现模糊匹配了,为什么还要全文索引?like + % 在文本比较少时是合适的,但是对于大量的文本数据检索,是不可想象的。全文索引在大量的数据面前,能比 like + % 快 N 倍,速度不是一个数量级,但是全文索引可能存在精度问题。
你可能没有注意过全文索引,不过至少应该对一种全文索引技术比较熟悉:各种的搜索引擎。虽然搜索引擎的索引对象是超大量的数据,并且通常其背后都不是关系型数据库,不过全文索引的基本原理是一样的。
MySQL 5.6 及以后的版本,MyISAM 和 InnoDB 存储引擎均支持全文索引;只有字段的数据类型为 char、varchar、text 及其系列才可以建全文索引。
和常用的模糊匹配使用 like + % 不同,全文索引有自己的语法格式,使用 match 和 against 关键字,比如
select * from fulltext_test
where match(content,tag) against('xxx xxx');
注意: match() 函数中指定的列必须和全文索引中指定的列完全相同,否则就会报错,无法使用全文索引,这是因为全文索引不会记录关键字来自哪一列。如果想要对某一列使用全文索引,请单独为该列创建全文索引。
索引慢查询
MySQL的慢查询日志是MySQL提供的一种日志记录,它用来记录在MySQL中响应时间超过阀值的语句。这个功能需要开启才能用。
在MySQL的配置文件my.cnf中写上:
long_query_time = 10
log-slow-queries = /var/lib/mysql/mysql-slow.log
默认情况下slow_query_log的值为OFF,表示慢查询日志是禁用的,可以通过设置slow_query_log的值来开启。使用set global slow_query_log=1开启了慢查询日志只对当前数据库生效,如果MySQL重启后则会失效。
如何优化:
不要前置%,使用组合索引而不是多列索引、分库分表、优化Limit分页
我们要优化慢查询的话通常用什么方式
MySQL的慢查询,全名是慢查询日志,是MySQL提供的一种日志记录,用来记录在MySQL中响应时间超过阀值的语句。运行时间超过long_query_time值的SQL语句,则会被记录到慢查询日志中。long_query_time的默认值为10,意思是记录运行10秒以上的语句。默认情况下,MySQL数据库并不启动慢查询日志,需要手动来设置这个参数。当然,如果不是调优需要的话,一般不建议启动该参数,因为开启慢查询日志会或多或少带来一定的性能影响。
left join 和 right join
from 谁谁就是left(right)
Orders表通过外键Id_P和Persons表进行关联。
inner join(内连接),在两张表进行连接查询时,只保留两张表中完全匹配的结果集。
left join,在两张表进行连接查询时,会返回左表所有的行,即使在右表中没有匹配的记录也会显示出来。
right join,在两张表进行连接查询时,会返回右表所有的行,即使在左表中没有匹配的记录。
full join,在两张表进行连接查询时,返回左表和右表中所有没有匹配的行。
单点登录原理
单点登录是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统的保护资源,若用户在某个应用系统中进行注销登录,所有的应用系统都不能再直接访问保护资源,像一些知名的大型网站,如:淘宝与天猫、新浪微博与新浪博客等都用到了这个技术。
原理:
有一个独立的认证中心,只有认证中心才能接受用户的用户名和密码等信息进行认证,其他系统不提供登录入口,只接受认证中心的间接授权。间接授权通过令牌实现,当用户提供的用户名和密码通过认证中心认证后,认证中心会创建授权令牌,在接下来的跳转过程中,授权令牌作为参数发送给各个子系统,子系统拿到令牌即得到了授权,然后创建局部会话。
- 客户端与服务器端的功能客户端:拦截子系统未登录用户请求,跳转至sso认证中心
- 接收并存储sso认证中心发送的令牌
- 与服务器端通信,校验令牌的有效性
- 建立局部会话
- 拦截用户注销请求,向sso认证中心发送注销请求
- 接收sso认证中心发出的注销请求,销毁局部会话
- 服务器端:验证用户的登录信息
- 创建全局会话
- 创建授权令牌
- 与客户端通信发送令牌
- 校验客户端令牌有效性
- 系统注册
- 接收客户端注销请求,注销所有会话