Junhc

岂止于博客

MySQL开发技巧

目录

1. Mac如何卸载MySQL
$ rm /usr/local/mysql
$ rm -rf /usr/local/mysql*
$ rm -rf /Library/PreferencePanes/My*
$ rm -rf /var/db/receipts/com.mysql.*
2. 错误锦集
Q1: "ERROR 1820 (HY000): You must reset your password using ALTER USER statement before executing this statement."
A1: mysql> set password=password('123456');  

Q2: "2006 - MySQL server has gone away"
A2:
my.ini配置文件,加入以下代码
#通信缓存区最大长度
max_allowed_packet=500M
#服务器关闭非交互连接之前等待活动的秒数
wait_timeout=288000
#服务器关闭交互式连接前等待活动的秒数
interactive_timeout = 288000
  
Q3: "事务没有提交,导致锁等待Lock wait timeout exceeded异常"
A3:
mysql> select * from information_schema.innodb_trx;
+--------+-----------+---------------------+-----------------------+------------------+------------+---------------------+-----------+---------------------+-------------------+-------------------+------------------+-----------------------+-----------------+-------------------+-------------------------+---------------------+-------------------+------------------------+----------------------------+---------------------------+---------------------------+------------------+----------------------------+
| trx_id | trx_state | trx_started         | trx_requested_lock_id | trx_wait_started | trx_weight | trx_mysql_thread_id | trx_query | trx_operation_state | trx_tables_in_use | trx_tables_locked | trx_lock_structs | trx_lock_memory_bytes | trx_rows_locked | trx_rows_modified | trx_concurrency_tickets | trx_isolation_level | trx_unique_checks | trx_foreign_key_checks | trx_last_foreign_key_error | trx_adaptive_hash_latched | trx_adaptive_hash_timeout | trx_is_read_only | trx_autocommit_non_locking |
+--------+-----------+---------------------+-----------------------+------------------+------------+---------------------+-----------+---------------------+-------------------+-------------------+------------------+-----------------------+-----------------+-------------------+-------------------------+---------------------+-------------------+------------------------+----------------------------+---------------------------+---------------------------+------------------+----------------------------+
| 50753  | RUNNING   | 2017-04-28 21:39:26 | NULL                  | NULL             |          3 |                  27 | NULL      | NULL                |                 0 |                 1 |                2 |                  1136 |               1 |                 1 |                       0 | REPEATABLE READ     |                 1 |                      1 | NULL                       |                         0 |                         0 |                0 |                          0 |
+--------+-----------+---------------------+-----------------------+------------------+------------+---------------------+-----------+---------------------+-------------------+-------------------+------------------+-----------------------+-----------------+-------------------+-------------------------+---------------------+-------------------+------------------------+----------------------------+---------------------------+---------------------------+------------------+----------------------------+
找到一直在跑, 没有提交的事务trx_mysql_thread_id, 然后`kill trx_mysql_thread_id`;

Q4: "java.sql.SQLException: Value '0000-00-00 00:00:00' can not be represented as java.sql.Timestamp"  
A4: 解决方法 jdbc:mysql://localhost:3306/op?useUnicode=true&characterEncoding=gbk&`zeroDateTimeBehavior=convertToNull`
3. MySQL事务隔离级别
理解事务的4种隔离级别
//查看事务级别
select @@tx_isolation;
//设置事务级别
set transaction isolation level [read uncommitted][read committed][repeatable read][serializable];

//开始事务
start transaction;
//回滚
rollback;
//提交
commit;

银行应用是解释事务必要性的一个经典的例子。假设一个银行额数据库有两张表:支票(checking)表和储蓄(savings)表。现在要从用户A的支票账户转移200元到她的储蓄账户,那么需要至少三个步骤:
1.检查支票账户的余额高于200元
2.从支票账户余额中减去200元
3.在储蓄账户余额中增加200元
上述三个步骤的操作必须打包在一个事务中,任何一个步骤失败,则必须回滚所有的步骤。

一个事务本质上有四个特点ACID:
原子性(Atomicity):原子性任务是一个独立的操作单元,是一种要么全部是,要么全部不是的原子单位性的操作。

一致性(Consistency):一个事务可以封装状态改变(除非它是一个只读的)。事务必须始终保持系统处于一致的状态,不管在任何给定的时间并发事务有多少。

一致性有下面特点:
如果一个操作触发辅助操作(级联,触发器),这些也必须成功,否则交易失败。
如果系统是由多个节点组成,一致性规定所有的变化必须传播到所有节点(多主复制)。如果从站节点是异步更新,那么我们打破一致性规则,系统成为“最终一致性”。

一个事务是数据状态的切换,因此,如果事务是并发多个,系统也必须如同串行事务一样操作。
在现实中,事务系统遭遇并发请求时,这种串行化是有成本的, Amdahl法则描述如下:它是描述序列串行执行和并发之间的关系。
“一个程序在并行计算情况下使用多个处理器所能提升的速度是由这个程序中串行执行部分的时间决定的。”
大多数数据库管理系统选择(默认情况下)是放宽一致性,以达到更好的并发性

隔离性(Isolaction):事务是并发控制机制,他们交错使用时也能提供一致性。隔离让我们隐藏来自外部世界未提交的状态变化,一个失败的事务不应该破坏系统的状态。隔离是通过用悲观或乐观锁机制实现的。

持久性(Durability):一个成功的事务将永久性地改变系统的状态,所以在它结束之前,所有导致状态的变化都记录在一个持久的事务日志中。如果我们的系统突然受到系统崩溃或断电,那么所有未完成已提交的事务可能会重演。

SQL标准规定了四种隔离级别:
+----------------------------------------------------------------+
隔离级别            脏读             非重复读	       Phantom read   
+----------------------------------------------------------------+
READ_UNCOMMITTED   allowed		      allowed		   allowed        
+----------------------------------------------------------------+
READ_COMMITTED     prevented	      allowed		   allowed        
+----------------------------------------------------------------+
REPETABLE_READ     prevented	      prevented	   allowed        
+----------------------------------------------------------------+
SERIALIZABLE       prevented	      prevented	   prevented      
+----------------------------------------------------------------+  

**Read Uncommitted(读取未提交内容)**
在该隔离级别,所有事务都可以看到其他未提交事务的执行结果。本隔离级别很少用于实际应用,因为它的性能也不比其他级别好多。
读取未提交的数据,也被称之为脏读(Dirty Read)。

**Read Committed(读取提交内容)**
这是大多数据库系统的默认隔离级别(但不是MySQL默认的)。它满足了隔离的简单定义:一个事务只能看见已经提交事务所做的改变。
这种隔离级别也支持所谓的不可重复读(Nonrepeatable Read),因为同一事务的其他实例再该实例处理期间可能会有新的Commit,所以同一Select可能返回不同的结果。

**Repeatable Read(可重读)**
这是MySQL默认的事务隔离级别,它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行。不过理论上,这回导致另一个棘手的问题:幻读(Phantom Read)。
简单的说,幻读指当用户读取某一范围的数据行时,另一个事务又在该范围内插入了新行,当用户再读取该范围的数据行时,会发现有新的“幻影”行。InnoDB和Falcon存储引擎,通过多版本并发控制机制解决了该问题。

**Serializable(可串行化)**
这是最高的隔离级别,它通过强制事务排序,使之不可能互相冲突,从而解决幻读问题。简言之,它是在每个读的数据行加共享锁。在这个级别,可能导致大量的超时现象和锁竞争。

`脏读`
脏读发生在:当一个事务允许读取一个被其他事务改变但是未提交的状态时,这是因为并没有锁阻止读取,如上图,你看到第二个事务读取了一个并不一致的值,不一致的意思是,这个值是无效的,因为修改这个值的第一个事务已经回滚,也就是说,第一个事务修改了这个值,但是未提交确认,却被第二个事务读取,第一个事务又放弃修改,悔棋了,而第二个事务就得到一个脏数据。

`非重复读`
反复读同一个数据却得到不同的结果,这是因为在反复几次读取的过程中,数据被修改了,这就导致我们使用了stale数据,这可以通过一个共享读锁来避免。这是隔离级别READ_COMMITTED会导致可重复读的原因。设置共享读锁也就是隔离级别提高到REPETABLE_READ。

`幻读`
当第二个事务插入一行记录,而正好之前第一个事务查询了应该包含这个新纪录的数据,那么这个查询事务的结果里肯定没有包含这个刚刚新插入的数据,这时幻影读发生了,通过变化锁和predicate locking避免。
4. MySQL返回自增长ID的四种方法
1.SELECT MAX(_ID) FROM TABLE_NAME;
// 高并发下MAX(_ID)显然不能使用,这时可以使用LAST_INSERT_ID
// 因为LAST_INSERT_ID是基于Connection的,只要每个线程都使用独立的Connection对象,
// LAST_INSERT_ID函数将返回该Connection对AUTO_INCREMENT列最新的INSERT or UPDATE操作生成的第一个RECORD的ID。
// 这个值不能被其它客户端(Connection)影响,保证了你能够找回自己的ID而不用担心其它客户端的活动,而且不需要加锁。
// 使用单INSERT语句插入多条记录, LAST_INSERT_ID返回一个列表。

2.SELECT LAST_INSERT_ID();		
// LAST_INSERT_ID与TABLE无关的,如果向表A插入数据后,再向表B插入数据,LAST_INSERT_ID会改变。

3.SELECT @@IDENTITY;			
// @@IDENTITY表示的是最近一次向具有IDENTITY属性(即自增列)的表插入数据时对应的自增列的值,是系统定义的全局变量。
// 一般系统定义的全局变量都是以@@开头,用户自定义变量以@开头。
// 如有个表A,它的自增列是ID,当向A表插入一行数据后,如果插入数据后自增列的值自动增加至101,则通过SELECT @@IDENTITY得到的值就是101// 使用@@IDENTITY的前提是在进行INSERT操作后,执行SELECT @@IDENTITY的时候连接没有关闭,否则得到的是NULL值。

4.SHOW TABLE STATUS;			
// 得出的结果里边对应表名记录中有个AUTO_INCREMENT字段,里边有下一个自增ID的数值就是当前该表的最大自增ID
MySQL执行顺序
写的顺序:SELECT...FROM...WHERE...GROUP BY...HAVING...ORDER BY...
执行顺序:FROM...WHERE...GROUP BY...HAVING...ORDER BY...

需要注意having和where的用法区别:  
1.having只能用在group by之后,对分组后的结果进行筛选(即使用having的前提条件是分组)2.where肯定在group by 之前  
3.where后的条件表达式里不允许使用聚合函数,而having可以。  
当一个查询语句同时出现了where,group by,having,order by的时候,执行顺序和编写顺序是:  
1.执行where xx对全表数据做筛选,返回第1个结果集。  
2.针对第1个结果集使用group by分组,返回第2个结果集。  
3.针对第2个结果集中的每1组数据执行select xx,有几组就执行几次,返回第3个结果集。  
4.针对第3个结集执行having xx进行筛选,返回第4个结果集。  
5.针对第4个结果集排序。  
外键
// 添加外键
alter table 表名 add constraint 外键名 foreign key (外键字段名) REFERENCES 外表表名 (外表主键字段名)
// 删除外键
alter table 表名 drop foreign key 外键名
// 级联更新或删除
ON DELETE、ON UPDATE表示事件触发限制,可设参数:
RESTRICT(限制外表中的外键改动)
CASCADE(跟随外键改动)
SET NULL(设空值)
SET DEFAULT(设默认值)
NO ACTION(无动作,默认的)
MySQL BLOB类型
类型        大小(单位:字节)
TinyBlob    最大 255
Blob        最大 65K
MediumBlob  最大 16M
LongBlob    最大 4G
5. 获取表的所有字段名
SELECT COLUMN_NAME FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = '数据库名' AND TABLE_NAME = '表名';
6. 深入理解悲观锁与乐观锁
悲观锁

在关系数据库管理系统里,悲观并发控制(又名“悲观锁”,Pessimistic Concurrency Control,缩写“PCC”)是一种并发控制的方法。它可以阻止一个事务以影响其他用户的方式来修改数据。如果一个事务执行的操作都某行数据应用了锁,那只有当这个事务把锁释放,其他事务才能够执行与该锁冲突的操作。 悲观并发控制主要用于数据争用激烈的环境,以及发生并发冲突时使用锁保护数据的成本要低于回滚事务的成本的环境中。

MySQL InnoDB中使用悲观锁

要使用悲观锁,我们必须关闭MySQL数据库的自动提交属性,因为MySQL默认使用autocommit模式,也就是说,当你执行一个更新操作后,MySQL会立刻将结果进行提交。

// 关闭自动提交
set autocommit=0;

//0.开始事务
start transaction;
//1.查询出商品信息
select status from t_goods where id=1 for update;
//2.根据商品信息生成订单
insert into t_orders (id,goods_id) values (null,1);
//3.修改商品status为2
update t_goods set status=2;
//4.提交事务
commit;

我们使用了select…for update的方式,这样就通过开启排他锁的方式实现了悲观锁。此时在t_goods表中id为1的这条数据就被锁定了,其它的事务必须等本次事务提交之后才能执行。这样可以保证当前的数据不会被其它事务修改。

特别注意:使用select…for update会把数据给锁住,MySQL InnoDB默认行级锁,行级锁都是基于索引的,如果一条SQL语句用不到索引是不会使用行级锁的,会使用表级锁把整张表锁住!!!

优点与不足

悲观并发控制实际上是“先取锁再访问”的保守策略,为数据处理的安全提供了保证。但是在效率方面,处理加锁的机制会让数据库产生额外的开销,还有增加产生死锁的机会;另外,在只读型事务处理中由于不会产生冲突,也没必要使用锁,这样做只能增加系统负载;还有会降低了并行性,一个事务如果锁定了某行数据,其他事务就必须等待该事务处理完才可以处理那行数

乐观锁

在关系数据库管理系统里,乐观并发控制(又名“乐观锁”,Optimistic Concurrency Control,缩写“OCC”)是一种并发控制的方法。它假设多用户并发的事务在处理时不会彼此互相影响,各事务能够在不产生锁的情况下处理各自影响的那部分数据。在提交数据更新之前,每个事务会先检查在该事务读取数据后,有没有其他事务又修改了该数据。如果其他事务有更新的话,正在提交的事务会进行回滚。乐观事务控制最早是由孔祥重(H.T.Kung)教授提出。

使用版本号实现乐观锁

实现数据版本有两种方式,第一种是使用版本号,第二种是使用时间戳。 为数据增加的一个版本标识。当读取数据时,将版本标识的值一同读出,数据每更新一次,同时对版本标识更新+1。当我们提交更新的时候,判断数据库表对应记录的当前版本信息与第一次取出来的版本标识进行比对,如果数据库表当前版本号与第一次取出来的版本标识值相等,则予以更新,否则认为是过期数据。

//1.查询出商品信息
select (status,status,version) from t_goods where id=#{id}
//2.根据商品信息生成订单
//3.修改商品status为2
update t_goods set status=2,version=version+1 where id=#{id} and version=#{version};
优点与不足

乐观并发控制相信事务之间的数据竞争(data race)的概率是比较小的,因此尽可能直接做下去,直到提交的时候才去锁定,所以不会产生任何锁和死锁。但如果直接简单这么做,还是有可能会遇到不可预期的结果,例如两个事务都读取了数据库的某一行,经过修改以后写回数据库,这时就遇到了问题。