select * from table where ? lock in share mode;
读取记录加S锁 (共享锁),实际中很少采用,其它的当前读加排他锁(X锁)意向锁是有数据引擎自己维护的,用户无法手动操作意向锁
意向锁分为两种:SELECT column FROM table ... LOCK IN SHARE MODE;
SELECT column FROM table ... FOR UPDATE;
解析:事务是很重要的概念,不管在数据库里还是问到项目框架,都会被问到。简单来说——
事务是逻辑上的一组操作,要么都执行,要么都不执行。
事务最经典也经常被拿出来说例子就是转账了。假如小明要给小红转账1000元,这个转账会涉及到两个关键操作就是:将小明的余额减少1000元,将小红的余额增加1000元。万一在这两个操作之间突然出现错误比如银行系统崩溃,导致小明余额减少而小红的余额没有增加,这样就不对了。事务就是保证这两个关键操作要么都成功,要么都要失败。
四个特性:
原子性(Atomicity):事务是最小的执行单位,不允许分割。事务的原子性确保动作要么全部完成,要么完全不起作用;
一致性(Consistency):执行事务前后,数据保持一致,多个事务对同一个数据读取的结果是相同的;
隔离性(Isolation): 并发访问数据库时,一个用户的事务不被其他事务所干扰,各并发事务之间数据库是独立的;
持久性(Durability): 一个事务被提交之后,它对数据库中数据的改变是持久的,即使数据库发生故障也不应该对其有任何影响。
事务的并发问题有哪些?数据库的隔离级别有哪几种?是不是隔离级别越高越好?我们应该如何选择隔离级别?
并发问题:
隔离级别:
隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也越大。对于多数应用程序,可以优先考虑把数据库系统的隔离级别设为Read Committed,它能够避免脏读取,而且具有较好的并发性能。尽管它会导致不可重复读等并发问题,在可能出现这类问题的个别场合,可以由应用程序采用悲观锁或乐观锁来控制。
不可重复读的重点是修改比如多次读取一条记录发现其中某些列的值被修改,幻读的重点在于新增或者删除比如多次读取一条记录发现记录增多或减少了。
Mysql默认隔离级别是可重复读。
第二问参考:互联网项目中选择Mysql的哪种隔离级别
将无序的数据变成相对有序的数据(就像目录一样)。
有or必全有索引;
复合索引未用左列字段;
like以%开头;
需要类型转换;
where中索引列有运算;
where中索引列使用了函数;
如果mysql觉得全表扫描更快时(数据少);
详细解释查看索引失效的几种情况
聚簇索引:将数据存储与索引放到了一块,找到索引也就找到了数据
非聚簇索引:将数据存储于索引分开结构,索引结构的叶子节点指向了数据的对应行,
澄清一个概念:innodb中,在聚簇索引之上创建的索引称之为辅助索引,辅助索引访问数据总是需要二次查找,非聚簇索引都是辅助索引,像复合索引、前缀索引、唯一索引,辅助索引叶子节点存储的不再是行的物理位置,而是主键值。
详细了解请参照聚簇索引和非聚簇索引
B+tree与B-tree的区别:
在B+Tree中,所有数据记录节点都是按照键值大小顺序存放在同一层的叶子节点上,而非叶子节点上只存储key值信息,这样可以大大加大每个节点存储的key值数量,降低B+Tree的高度。
B+tree更适合做文件索引,因为B+ Tree的内部节点并没有指向关键字具体信息的指针,所以他的内部节点比其他结构的更小,同样大小的盘块能够容纳更多的单位索引结构,一次性读入内存的关键字信息索引就更多,相对来说IO读写次数也就少很多。
如果一个索引包含(或者说覆盖)所有需要查询的字段的值,我们就称之为“覆盖索引”。我们知道在InnoDB存储引擎中,如果不是主键索引,叶子节点存储的是主键和列值。最终还是要“回表”,也就是要通过主键再查找一次。这样就会比较慢。而覆盖索引就是把要查询出的列和索引是对应的,不做回表操作。
举例:创建一个索引(username,age)
在查询数据的时候执行sql
select username,age from user where username='Java' and age = 22;
要查询出的列和索引是对应的,不要做回表操作。
MySQL中的索引可以以一定顺序引用多列,这种索引叫作联合索引。如user表的name和city加联合索引就是(name,city),而最左前缀原则指的是,如果查询的时候查询条件精确匹配索引的左边连续一列或几列,则此列就可以被用到。如下:
select * from user where name=xx and city=xx ; //可以命中索引 select * from user where name=xx ; // 可以命中索引 select * from user where city=xx ; // 无法命中索引
这里需要注意的是,查询的时候如果两个条件都用上了,但是顺序不同,如 city= xx and name =xx
,那么现在的查询引擎会自动优化为匹配联合索引的顺序,这样是能够命中索引的。
由于最左前缀原则,在创建联合索引时,索引字段的顺序需要考虑字段值去重之后的个数,较多的放前面。ORDER BY子句也遵循此规则。
MyISAM是MySQL的默认数据库引擎(5.5版之前)。虽然性能极佳,而且提供了大量的特性,包括全文索引、压缩、空间函数等,但MyISAM不支持事务和行级锁,而且最大的缺陷就是崩溃后无法安全恢复。不过,5.5版本之后,MySQL引入了InnoDB(事务性数据库引擎),MySQL 5.5版本后默认的存储引擎为InnoDB。
大多数时候我们使用的都是 InnoDB 存储引擎,但是在某些情况下使用 MyISAM 也是合适的比如读密集的情况下。(如果你不介意 MyISAM 崩溃恢复问题的话)。
两者的对比:
READ COMMITTED
和 REPEATABLE READ
两个隔离级别下工作;MVCC可以使用 乐观(optimistic)锁 和 悲观(pessimistic)锁来实现;各数据库中MVCC实现并不统一。例如:应尽量避免在 where 子句中使用!=或<>操作符,否则将引擎放弃使用索引而进行全表扫描。
应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描。
互联网项目中,尽量避免使用存储过程和触发器(已经被pass,都是用代码来整合),尽量避免多表关联查询.
where条件里面的必须有1个带索引,不要使用not in或者exists语句,这种情况下会全表扫描,索引不生效,需要优化
尽量不要使用存储过程和触发器,因为这个依赖于数据库去实现,在高并发情况下可能导致数据库瘫痪
explain用来分析sql查询语句的执行效率,desc命令同样的效果。
语法:explain 查询语句
举个栗子:explain select * from news;
输出:
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
下面对各个属性进行了解:
1、id:这是SELECT的查询序列号
2、select_type:select_type就是select的类型,可以有以下几种:
SIMPLE:简单SELECT(不使用UNION或子查询等)
PRIMARY:最外面的SELECT
UNION:UNION中的第二个或后面的SELECT语句
DEPENDENT UNION:UNION中的第二个或后面的SELECT语句,取决于外面的查询
UNION RESULT:UNION的结果。
SUBQUERY:子查询中的第一个SELECT
DEPENDENT SUBQUERY:子查询中的第一个SELECT,取决于外面的查询
DERIVED:导出表的SELECT(FROM子句的子查询)
3、table:显示这一行的数据是关于哪张表的
4、type:这列最重要,显示了连接使用了哪种类别,有无使用索引,是使用Explain命令分析性能瓶颈的关键项之一。
结果值从好到坏依次是:
system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL
一般来说,得保证查询至少达到range级别,最好能达到ref,否则就可能会出现性能问题。
5、possible_keys:列指出MySQL能使用哪个索引在该表中找到行
6、key:显示MySQL实际决定使用的键(索引)。如果没有选择索引,键是NULL
7、key_len:显示MySQL决定使用的键长度。如果键是NULL,则长度为NULL。使用的索引的长度。在不损失精确性的情况下,长度越短越好
8、ref:显示使用哪个列或常数与key一起从表中选择行。
9、rows:显示MySQL认为它执行查询时必须检查的行数。
10、Extra:包含MySQL解决查询的详细信息,也是关键参考项之一。
Distinct
一旦MYSQL找到了与行相联合匹配的行,就不再搜索了Not exists
MYSQL 优化了LEFT JOIN,一旦它找到了匹配LEFT JOIN标准的行,就不再搜索了
Range checked for each
Record(index map:#)
没有找到理想的索引,因此对于从前面表中来的每一个行组合,MYSQL检查使用哪个索引,并用它来从表中返回行。这是使用索引的最慢的连接之一Using filesort
看 到这个的时候,查询就需要优化了。MYSQL需要进行额外的步骤来发现如何对返回的行排序。它根据连接类型以及存储排序键值和匹配条件的全部行的行指针来 排序全部行Using index
列数据是从仅仅使用了索引中的信息而没有读取实际的行动的表返回的,这发生在对表 的全部的请求列都是同一个索引的部分的时候Using temporary
看到这个的时候,查询需要优化了。这 里,MYSQL需要创建一个临时表来存储结果,这通常发生在对不同的列集进行ORDER BY上,而不是GROUP BY上Using where
使用了WHERE从句来限制哪些行将与下一张表匹配或者是返回给用户。如果不想返回表中的全部行,并且连接类型ALL或index, 这就会发生,或者是查询有问题
其他一些Tip:当type 显示为 “index” 时,并且Extra显示为“Using Index”, 表明使用了覆盖索引。
目前,我们已经探讨了许多关于数据库锁的问题,锁能够有效地解决并发的问题,但这也带来了一个严重的缺点,那就是死锁。
死锁在操作系统中指的是两个或两个以上的进程在执行的过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或者系统产生了死锁,这些永远在互相等待的进程称为死锁进程。
在操作系统中,死锁的处理是一个重要的话题,也已经有较为成熟的解决方法,如银行家算法等,在这边我们就不再阐述,只讨论数据库中的死锁。
数据库中常见的死锁原因与解决方案有:
- 事务之间对资源访问顺序的交替
出现原因:
一个用户A 访问表A(锁住了表A),然后又访问表B;另一个用户B 访问表B(锁住了表B),然后企图访问表A;这时用户A由于用户B已经锁住表B,它必须等待用户B释放表B才能继续,同样用户B要等用户A释放表A才能继续,这就死锁就产生了。解决方法:
这种死锁比较常见,是由于程序的BUG产生的,除了调整的程序的逻辑没有其它的办法。仔细分析程序的逻辑,对于数据库的多表操作时,尽量按照相同的顺序进行处理,尽量避免同时锁定两个资源,如操作A和B两张表时,总是按先A后B的顺序处理, 必须同时锁定两个资源时,要保证在任何时刻都应该按照相同的顺序来锁定资源
- 并发修改同一记录
出现原因:主要是由于没有一次性申请够权限的锁导致的。参考:记录一次死锁排查过程
用户A查询一条纪录,然后修改该条纪录;这时用户B修改该条纪录,这时用户A的事务里锁的性质由查询的共享锁企图上升到独占锁,而用户B里的独占锁由于A有共享锁存在所以必须等A释放掉共享锁,而A由于B的独占锁而无法上升的独占锁也就不可能释放共享锁,于是出现了死锁。这种死锁比较隐蔽,但在稍大点的项目中经常发生。
解决方法:
- 乐观锁,实现写-写并发
- 悲观锁:使用悲观锁进行控制。悲观锁大多数情况下依靠数据库的锁机制实现,如Oracle的Select … for update语句,以保证操作最大程度的独占性。但随之而来的就是数据库性能的大量开销,特别是对长事务而言,这样的开销往往无法承受。
- 索引不当导致的死锁
出现原因:
如果在事务中执行了一条不满足条件的语句,执行全表扫描,把行级锁上升为表级锁,多个这样的事务执行后,就很容易产生死锁和阻塞。类似的情况还有当表中的数据量非常庞大而索引建的过少或不合适的时候,使得经常发生全表扫描,最终应用系统会越来越慢,最终发生阻塞或死锁。另外一种情况是由于二级索引的存在,上锁的顺序不同导致的,这部分在讨论索引时会提到。参考:https://www.cnblogs.com/LBSer/p/5183300.html
解决方法:
SQL语句中不要使用太复杂的关联多表的查询;使用“执行计划”对SQL语句进行分析,对于有全表扫描的SQL语句,建立相应的索引进行优化。
那么,如何尽可能的避免死锁呢?
- 以固定的顺序访问表和行。即按顺序申请锁,这样就不会造成互相等待的场面。
- 大事务拆小。大事务更倾向于死锁,如果业务允许,将大事务拆小。
- 在同一个事务中,尽可能做到一次锁定所需要的所有资源,减少死锁概率。
- 降低隔离级别。如果业务允许,将隔离级别调低也是较好的选择,比如将隔离级别从RR调整为RC,可以避免掉很多因为gap锁造成的死锁。
- 为表添加合理的索引。如果不走索引将会为表的每一行记录添加上锁,死锁的概率大大增大。
悲观锁与乐观锁是两种常见的资源并发锁设计思路,也是并发编程中一个非常基础的概念。
悲观锁(Pessimistic Lock), 顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。
乐观锁(Optimistic Lock), 顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库如果提供类似于write_condition机制的其实都是提供的乐观锁。
两种锁各有优缺点,不可认为一种好于另一种,像乐观锁适用于写比较少的情况下,即冲突真的很少发生的时候,这样可以省去了锁的开销,加大了系统的整个吞吐量。但如果经常产生冲突,上层应用会不断的进行retry,这样反倒是降低了性能,所以这种情况下用悲观锁就比较合适。
悲观锁和乐观锁大部分场景下差异不大,一些独特场景下有一些差别,一般我们可以从如下几个方面来判断:
响应速度:如果需要非常高的响应速度,建议采用乐观锁方案,成功就执行,不成功就失败,不需要等待其他并发去释放锁
冲突频率:如果冲突频率非常高,建议采用悲观锁,保证成功率,如果冲突频率大,乐观锁会需要多次重试才能成功,代价比较大
重试代价:如果重试代价大,建议采用悲观锁
以商品扣减为例,悲观锁和乐观锁的实现分别为:
要使用悲观锁,我们必须关闭mysql数据库的自动提交属性,因为MySQL默认使用autocommit模式,也就是说,当你执行一个更新操作后,MySQL会立刻将结果进行提交。我们可以使用命令设置MySQL为非autocommit模式【即使用事务】:
set autocommit=0; //设置完autocommit后,我们就可以执行我们的正常业务了。具体如下: //开始事务 begin;/begin work;/start transaction; (三者选一就可以) //查询出商品信息 select status from items where id=10000 for update; //根据商品信息生成订单 insert into orders (id,item_id) values (null,10000); //修改商品status为2 update items set status=2 where id=10000; //提交事务 commit;
注:上面的begin/commit为事务的开始和结束,因为在前一步我们关闭了mysql的autocommit,所以需要手动控制事务的提交,在这里就不细表了。
乐观锁实现为:
以mysql InnoDB存储引擎为例,还是拿之前的例子商品表items表中有一个字段status,status=1表示该商品未被下单,status=2表示该商品已经被下单,那么我们对每个商品下单前必须确保此商品的status=1。假设有一件商品,其id为10000;
下单操作:
//查询出商品信息 select (status,version) from items where id=#{id} //根据商品信息生成订单 //修改商品status为2 update items set status=2,version=version+1 where id=#{id} and version=#{version};
若挑选以库存数作为乐观锁,则失败率会小很多,如下(用乐观锁方式扣减库存,确保有库存即可):
update item set quantity=quantity-#sub_quantity# where item_id = #id# and quantity-#sub_quantity# > 0
【备注】用户注册,如何防止重复写入相同用户?悲观锁是一种方式,如何实现?另外一种方式是使用唯一索引?
【备注】for Update加锁详解
举个例子: 假设商品表单products 内有一个存放商品数量的quantity ,在订单成立之前必须先确定quantity 商品数量是否足够(quantity>0) ,然后才把数量更新为1。
不安全的做法:
SELECT quantity FROM products WHERE id=3; UPDATE products SET quantity = 1 WHERE id=3;
为什么不安全呢?
少量的状况下或许不会有问题,但是大量的数据存取「铁定」会出问题。
如果我们需要在quantity>0 的情况下才能扣库存,假设程序在第一行SELECT 读到的quantity 是2 ,看起来数字没有错,但是当MySQL 正准备要UPDATE 的时候,可能已经有人把库存扣成0 了,但是程序却浑然不知,将错就错的UPDATE 下去了。
因此必须透过的事务机制来确保读取及提交的数据都是正确的。
于是我们在MySQL 就可以这样测试:
SET AUTOCOMMIT=0; BEGIN WORK; SELECT quantity FROM products WHERE id=3 FOR UPDATE; //此时products 数据中id=3 的数据被锁住(注3),其它事务必须等待此次事务 提交后才能执行 //SELECT * FROM products WHERE id=3 FOR UPDATE 如此可以确保quantity 在别的事务读到的数字是正确的。 //代码如下: UPDATE products SET quantity = '1' WHERE id=3 ; COMMIT;
此处了解即可,mysql内部日志主要区分为:
事务日志:
工作模式:基于InnoDB存储引擎的MySQL之所以可以从崩溃中恢复,正是依赖于事务日志,当数据库实例宕机后,重启时MySQL会自行检查事务日志,然后依次处理;
事务日志分为redo log和undo log两种:
(1)、对于事务日志中未正常提交的事务,则会记录到undo log中,因为事务未正确执行完,因此必须回滚,从而保证数据一致性
(2)、对于事务日志中已正常提交但未同步到持久化存储上时,则会记录到redo log中,因此MySQL会重新执行一遍事务,然后让数据存储到磁盘上,从而保证数据一致性
二进制日志
MySQL中的二进制日志(binary log)是一个二进制文件,主要用于记录可能引起数据库内容更改的SQL语句或数据行记录,例如新增(Insert)、更新(Update)、删除(Delete)、授权信息变更(Grant Change)等,除记录这些外,还会记录变更语句的发生时间、执行时长、操作数据等额外信息,但是它不会记录诸如Select、Show等这些不会引起数据修改的SQL语句。
binary log 主要用于主从复制等集群模式
慢日志与错误日志
(errlog)错误日志,作用:Mysql本身启动,停止,运行期间发生的错误信息
(slow query log)慢查询日志, 作用:记录执行时间过长的sql,时间阈值可以配置,只记录执行成功
更多日志种类后续慢慢了解即可
显示文件目录命令ls 如ls
改变当前目录命令cd 如cd /home
建立子目录mkdir 如mkdir xiong
删除子目录命令rmdir 如rmdir /mnt/cdrom
删除文件命令rm 如rm /ucdos.bat
文件复制命令cp 如cp /ucdos /fox
获取帮助信息命令man 如man ls
显示文件的内容less 如less mwm.lx
重定向与管道type 如type readme>>direct,将文件readme的内容追加到文direct中,例如ps -ef|grep java
linux下如何查看网络端口状态(netstat),如何查看内存使用情况(top)
netstat -an | grep 3306
ps -ef|grep 实际的java进程名称