c++客户端发送加锁_MySQL语句加锁分析详解

前言

建立一个存储三国英雄的hero表:

CREATE TABLE hero (
number INT,
name VARCHAR(100),
country varchar(100),
PRIMARY KEY (number),
KEY idx_name (name)
) Engine=InnoDB CHARSET=utf8;

然后向这个表里插入几条记录:

INSERT INTO hero VALUES
(1, 'l刘备', '蜀'),
(3, 'z诸葛亮', '蜀'),
(8, 'c曹操', '魏'),
(15, 'x荀彧', '魏'),
(20, 's孙权', '吴');

然后现在hero表就有了两个索引(一个二级索引,一个聚簇索引),示意图如下:

27657467af7c83cd5384a04b037b796d.png

语句加锁分析

其实啊,“XXX语句该加什么锁”本身就是个伪命题,一条语句需要加的锁受到很多条件制约,比方说:

  • 事务的隔离级别

  • 语句执行时使用的索引(比如聚簇索引、唯一二级索引、普通二级索引)

  • 查询条件(比方说==<>=等等)

  • 具体执行的语句类型

在继续详细分析语句的加锁过程前,大家一定要有一个全局概念:加锁只是解决并发事务执行过程中引起的脏写脏读不可重复读幻读这些问题的一种解决方案(MVCC算是一种解决脏读不可重复读幻读这些问题的一种解决方案),一定要意识到加锁的出发点是为了解决这些问题,不同情景下要解决的问题不一样,才导致加的锁不一样,千万不要为了加锁而加锁,容易把自己绕进去。当然,有时候因为MySQL具体的实现而导致一些情景下的加锁有些不太好理解,这就得我们死记硬背了~

我们这里把语句分为3种大类:普通的SELECT语句、锁定读的语句、INSERT语句,我们分别看一下。

普通的SELECT语句

普通的SELECT语句在:

  • READ UNCOMMITTED隔离级别下,不加锁,直接读取记录的最新版本,可能发生脏读不可重复读幻读问题。

  • READ COMMITTED隔离级别下,不加锁,在每次执行普通的SELECT语句时都会生成一个ReadView,这样解决了脏读问题,但没有解决不可重复读幻读问题。

  • REPEATABLE READ隔离级别下,不加锁,只在第一次执行普通的SELECT语句时生成一个ReadView,这样把脏读不可重复读幻读问题都解决了。

    不过这里有一个小插曲:

    # 事务T1,REPEATABLE READ隔离级别下
    mysql> BEGIN;
    Query OK, 0 rows affected (0.00 sec)
    mysql> SELECT * FROM hero WHERE number = 30;
    Empty set (0.01 sec)# 此时事务T2执行了:INSERT INTO hero VALUES(30, 'g关羽', '魏'); 并提交
    mysql> UPDATE hero SET country = '蜀' WHERE number = 30;
    Query OK, 1 row affected (0.01 sec)
    Rows matched: 1 Changed: 1 Warnings: 0
    mysql> SELECT * FROM hero WHERE number = 30;
    +--------+---------+---------+
    | number | name | country |
    +--------+---------+---------+
    | 30 | g关羽 | 蜀 |
    +--------+---------+---------+
    1 row in set (0.01 sec)

    REPEATABLE READ隔离级别下,T1第一次执行普通的SELECT语句时生成了一个ReadView,之后T2hero表中新插入了一条记录便提交了,ReadView并不能阻止T1执行UPDATE或者DELETE语句来对改动这个新插入的记录(因为T2已经提交,改动该记录并不会造成阻塞),但是这样一来这条新记录的trx_id隐藏列就变成了T1事务id,之后T1中再使用普通的SELECT语句去查询这条记录时就可以看到这条记录了,也就把这条记录返回给客户端了。因为这个特殊现象的存在,你也可以认为InnoDB中的MVCC并不能完完全全的禁止幻读。

  • SERIALIZABLE隔离级别下,需要分为两种情况讨论:

    • 在系统变量autocommit=0时,也就是禁用自动提交时,普通的SELECT语句会被转为SELECT ... LOCK IN SHARE MODE这样的语句,也就是在读取记录前需要先获得记录的S锁,具体的加锁情况和REPEATABLE READ隔离级别下一样,我们后边再分析。

    • 在系统变量autocommit=1时,也就是启用自动提交时,普通的SELECT语句并不加锁,只是利用MVCC来生成一个ReadView去读取记录。

      为啥不加锁呢?因为启用自动提交意味着一个事务中只包含一条语句,一条语句也就没有啥不可重复读幻读这样的问题了。

锁定读的语句

我们把下边四种语句放到一起讨论:

  • 语句一:SELECT ... LOCK IN SHARE MODE;

  • 语句二:SELECT ... FOR UPDATE;

  • 语句三:UPDATE ...

  • 语句四:DELETE ...

我们说语句一语句二MySQL中规定的两种锁定读的语法格式,而语句三语句四由于在执行过程需要首先定位到被改动的记录并给记录加锁,也可以被认为是一种锁定读

READ UNCOMMITTED/READ COMMITTED隔离级别下

READ UNCOMMITTED下语句的加锁方式和READ COMMITTED隔离级别下语句的加锁方式基本一致,所以就放到一块儿说了。值得注意的是,采用加锁方式解决并发事务带来的问题时,其实脏读不可重复读在任何一个隔离级别下都不会发生(因为读-写操作需要排队进行)。

对于使用主键进行等值查询的情况
  • 使用SELECT ... LOCK IN SHARE MODE来为记录加锁,比方说:

    SELECT * FROM hero WHERE number = 8 LOCK IN SHARE MODE;

    这个语句执行时只需要访问一下聚簇索引中number值为8的记录,所以只需要给它加一个S型正经记录锁就好了,如图所示:

    fe51383a44638db3c3046b2f0cad7f15.png
  • 使用SELECT ... FOR UPDATE来为记录加锁,比方说:

    SELECT * FROM hero WHERE number = 8 FOR UPDATE;

    这个语句执行时只需要访问一下聚簇索引中number值为8的记录,所以只需要给它加一个X型正经记录锁就好了,如图所示:

    4683a0063cd2fdc8ea6f0398a9b8cbd3.png

    小贴士:为了区分S锁和X锁,我们之后在示意图中就把加了S锁的记录染成蓝色,把加了X锁的记录染成紫色。

  • 使用UPDATE ...来为记录加锁,比方说:

    UPDATE hero SET country = '汉' WHERE number = 8;

    这条UPDATE语句并没有更新二级索引列,加锁方式和上边所说的SELECT ... FOR UPDATE语句一致。

    如果UPDATE语句中更新了二级索引列,比方说:

    UPDATE hero SET name = 'cao曹操' WHERE number = 8;

    该语句的实际执行步骤是首先更新对应的number值为8的聚簇索引记录,再更新对应的二级索引记录,所以加锁的步骤就是:

  1. number值为8的聚簇索引记录加上X型正经记录锁(该记录对应的)。

  2. 为该聚簇索引记录对应的idx_name二级索引记录(也就是name值为'c曹操'number值为8的那条二级索引记录)加上X型正经记录锁

画个图就是这样:

719b7521abb2f111f92098446f604caf.png

小贴士:我们用带圆圈的数字来表示为各条记录加锁的顺序。

使用DELETE ...来为记录加锁,比方说:

DELETE FROM hero WHERE number = 8;

我们平时所说的“DELETE表中的一条记录”其实意味着对聚簇索引和所有的二级索引中对应的记录做DELETE操作,本例子中就是要先把number值为8的聚簇索引记录执行DELETE操作,然后把对应的idx_name二级索引记录删除,所以加锁的步骤和上边更新带有二级索引列的UPDATE语句一致,就不画图了。

对于使用主键进行范围查询的情况
  • 使用SELECT ... LOCK IN SHARE MODE来为记录加锁,比方说:

    SELECT * FROM hero WHERE number <= 8 LOCK IN SHARE MODE;

    这个语句看起来十分简单,但它的执行过程还是有一丢丢小复杂的:

  1. 先到聚簇索引中定位到满足number <= 8的第一条记录,也就是number值为1的记录,然后为其加锁。

  2. 判断一下该记录是否符合索引条件下推中的条件。

    我们前边介绍过一个称之为索引条件下推Index Condition Pushdown,简称ICP)的功能,也就是把查询中与被使用索引有关的查询条件下推到存储引擎中判断,而不是返回到server层再判断。不过需要注意的是,索引条件下推只是为了减少回表次数,也就是减少读取完整的聚簇索引记录的次数,从而减少IO操作。而对于聚簇索引而言不需要回表,它本身就包含着全部的列,也起不到减少IO操作的作用,所以设计InnoDB的大叔们规定这个索引条件下推特性只适用于二级索引。也就是说在本例中与被使用索引有关的条件是:number <= 8,而number列又是聚簇索引列,所以本例中并没有符合索引条件下推的查询条件,自然也就不需要判断该记录是否符合索引条件下推中的条件。

  3. 判断一下该记录是否符合范围查询的边界条件

    因为在本例中是利用主键number进行范围查询,设计InnoDB的大叔规定每从聚簇索引中取出一条记录时都要判断一下该记录是否符合范围查询的边界条件,也就是number <= 8这个条件。如果符合的话将其返回给server层继续处理,否则的话需要释放掉在该记录上加的锁,并给server层返回一个查询完毕的信息。

    对于number值为1的记录是符合这个条件的,所以会将其返回到server层继续处理。

  4. 将该记录返回到server层继续判断。

    server层如果收到存储引擎层提供的查询完毕的信息,就结束查询,否则继续判断那些没有进行索引条件下推的条件,在本例中就是继续判断number <= 8这个条件是否成立。噫,不是在第3步中已经判断过了么,怎么在这又判断一回?是的,设计InnoDB的大叔采用的策略就是这么简单粗暴,把凡是没有经过索引条件下推的条件都需要放到server层再判断一遍。如果该记录符合剩余的条件(没有进行索引条件下推的条件),那么就把它发送给客户端,不然的话需要释放掉在该记录上加的锁。

  5. 然后刚刚查询得到的这条记录(也就是number值为1的记录)组成的单向链表继续向后查找,得到了number值为3的记录,然后重复第2345这几个步骤。

小贴士:上述步骤是在MySQL 5.7.21这个版本中验证的,不保证其他版本有无出入。

但是这个过程有个问题,就是当找到number值为8的那条记录的时候,还得向后找一条记录(也就是number值为15的记录),在存储引擎读取这条记录的时候,也就是上述的第1步中,就得为这条记录加锁,然后在第3步时,判断该记录不符合number <= 8这个条件,又要释放掉这条记录的锁,这个过程导致number值为15的记录先被加锁,然后把锁释放掉,过程就是这样:

f6c2586673f69e1e3e6c6191b54daec3.png

这个过程有意思的一点就是,如果你先在事务T1中执行:

# 事务T1
BEGIN;
SELECT * FROM hero WHERE number <= 8 LOCK IN SHARE MODE;

然后再到事务T2中执行:

# 事务T2
BEGIN;
SELECT * FROM hero WHERE number = 15 FOR UPDATE;

是没有问题的,因为在T2执行时,事务T1已经释放掉了number值为15的记录的锁,但是如果你先执行T2,再执行T1,由于T2已经持有了number值为15的记录的锁,事务T1将因为获取不到这个锁而等待。

我们再看一个使用主键进行范围查询的例子:

SELECT * FROM hero WHERE number >= 8 LOCK IN SHARE MODE;

这个语句的执行过程其实和我们举的上一个例子类似。也是先到聚簇索引中定位到满足number >= 8这个条件的第一条记录,也就是number值为8的记录,然后就可以沿着由记录组成的单向链表一路向后找,每找到一条记录,就会为其加上锁,然后判断该记录符不符合范围查询的边界条件,不过这里的边界条件比较特殊:number >= 8,只要记录不小于8就算符合边界条件,所以判断和没判断是一样一样的。最后把这条记录返回给server层server层再判断number >= 8这个条件是否成立,如果成立的话就发送给客户端,否则的话就结束查询。不过InnoDB存储引擎找到索引中的最后一条记录,也就是Supremum伪记录之后,在存储引擎内部就可以立即判断这是一条伪记录,不必要返回给server层处理,也没必要给它也加上锁(也就是说在第1步中就压根儿没给这条记录加锁)。整个过程会给number值为81520这三条记录加上S型正经记录锁,画个图表示一下就是这样:

22765cccf2ca3db875df1f1961a4bc11.png

使用SELECT ... FOR UPDATE语句来为记录加锁:

SELECT ... FOR UPDATE语句类似,只不过加的是X型正经记录锁

使用UPDATE ...来为记录加锁,比方说:

UPDATE hero SET country = '汉' WHERE number >= 8;

这条UPDATE语句并没有更新二级索引列,加锁方式和上边所说的SELECT ... FOR UPDATE语句一致。

如果UPDATE语句中更新了二级索引列,比方说:

UPDATE hero SET name = 'cao曹操' WHERE number >= 8;

这时候会首先更新聚簇索引记录,再更新对应的二级索引记录,所以加锁的步骤就是:

  1. number值为8的聚簇索引记录加上X型正经记录锁

  2. 然后为上一步中的记录索引记录对应的idx_name二级索引记录加上X型正经记录锁

  3. number值为15的聚簇索引记录加上X型正经记录锁

  4. 然后为上一步中的记录索引记录对应的idx_name二级索引记录加上X型正经记录锁

  5. number值为20的聚簇索引记录加上X型正经记录锁

  6. 然后为上一步中的记录索引记录对应的idx_name二级索引记录加上X型正经记录锁

画个图就是这样:

8f1310f1dc53edb5a82e573f88f7141c.png

如果是下边这个语句:

UPDATE hero SET namey = '汉' WHERE number <= 8;

则会对number值为138聚簇索引记录以及它们对应的二级索引记录加X型正经记录锁,加锁顺序和上边语句中的加锁顺序类似,都是先对一条聚簇索引记录加锁后,再给对应的二级索引记录加锁。之后会继续对number值为15的聚簇索引记录加锁,但是随后InnoDB存储引擎判断它不符合边界条件,随即会释放掉该聚簇索引记录上的锁(注意这个过程中没有对number值为15的聚簇索引记录对应的二级索引记录加锁)。具体示意图就不画了。

使用DELETE ...来为记录加锁,比方说:

DELETE FROM hero WHERE number >= 8;

DELETE FROM hero WHERE number <= 8;

这两个语句的加锁情况和更新带有二级索引列的UPDATE语句一致,就不画图了。

对于使用二级索引进行等值查询的情况

小贴士:在READ UNCOMMITTED和READ COMMITTED隔离级别下,使用普通的二级索引和唯一二级索引进行加锁的过程是一样的,所以我们也就不分开讨论了。

  • 使用SELECT ... LOCK IN SHARE MODE来为记录加锁,比方说:

    SELECT * FROM hero WHERE name = 'c曹操' LOCK IN SHARE MODE;

    这个语句的执行过程是先通过二级索引idx_name定位到满足name = 'c曹操'条件的二级索引记录,然后进行回表操作。所以先要对二级索引记录加S型正经记录锁,然后再给对应的聚簇索引记录加S型正经记录锁,示意图如下:

    53f780ec630f6e5469be61b734bd785d.png

    这里需要再次强调一下这个语句的加锁顺序:

  1. 先对name列为'c曹操'二级索引记录进行加锁。

  2. 再对相应的聚簇索引记录进行加锁

小贴士:我们知道idx_name是一个普通的二级索引,到idx_name索引中定位到满足name= 'c曹操'这个条件的第一条记录后,就可以沿着这条记录一路向后找。可是从我们上边的描述中可以看出来,并没有对下一条二级索引记录进行加锁,这是为什么呢?这是因为设计InnoDB的大叔对等值匹配的条件有特殊处理,他们规定在InnoDB存储引擎层查找到当前记录的下一条记录时,在对其加锁前就直接判断该记录是否满足等值匹配的条件,如果不满足直接返回(也就是不加锁了),否则的话需要将其加锁后再返回给server层。所以这里也就不需要对下一条二级索引记录进行加锁了。

现在要介绍一个非常有趣的事情,我们假设上边这个语句在事务T1中运行,然后事务T2中运行下边一个我们之前介绍过的语句:

UPDATE hero SET name = '曹操' WHERE number = 8;

这两个语句都是要对number值为8的聚簇索引记录和对应的二级索引记录加锁,但是不同点是加锁的顺序不一样。这个UPDATE语句是先对聚簇索引记录进行加锁,后对二级索引记录进行加锁,如果在不同事务中运行上述两个语句,可能发生一种贼奇妙的事情 ——

  • 事务T2持有了聚簇索引记录的锁,事务T1持有了二级索引记录的锁。

  • 事务T2在等待获取二级索引记录上的锁,事务T1在等待获取聚簇索引记录上的锁。

两个事务都分别持有一个锁,而且都在等待对方已经持有的那个锁,这种情况就是所谓的死锁,两个事务都无法运行下去,必须选择一个进行回滚,对性能影响比较大。  

使用SELECT ... FOR UPDATE语句时,比如:

SELECT * FROM hero WHERE name = 'c曹操' FOR UPDATE;

这种情况下与SELECT ... LOCK IN SHARE MODE语句的加锁情况类似,都是给访问到的二级索引记录和对应的聚簇索引记录加锁,只不过加的是X型正经记录锁罢了。

使用UPDATE ...来为记录加锁,比方说:

与更新二级索引记录的SELECT ... FOR UPDATE的加锁情况类似,不过如果被更新的列中还有别的二级索引列的话,对应的二级索引记录也会被加锁。

使用DELETE ...来为记录加锁,比方说:

SELECT ... FOR UPDATE的加锁情况类似,不过如果表中还有别的二级索引列的话,对应的二级索引记录也会被加锁。

对于使用二级索引进行范围查询的情况
  • 使用SELECT ... LOCK IN SHARE MODE来为记录加锁,比方说:

    SELECT * FROM hero FORCE INDEX(idx_name)  WHERE name >= 'c曹操' LOCK IN SHARE MODE;

    小贴士:因为优化器会计算使用二级索引进行查询的成本,在成本较大时可能选择以全表扫描的方式来执行查询,所以我们这里使用FORCE INDEX(idx_name)来强制使用二级索引idx_name来执行查询。

    这个语句的执行过程其实是先到二级索引中定位到满足name >= 'c曹操'的第一条记录,也就是name值为c曹操的记录,然后就可以沿着这条记录的链表一路向后找,从二级索引idx_name的示意图中可以看出,所有的用户记录都满足name >= 'c曹操'的这个条件,所以所有的二级索引记录都会被加S型正经记录锁,它们对应的聚簇索引记录也会被加S型正经记录锁。不过需要注意一下加锁顺序,对一条二级索引记录加锁完后,会接着对它相应的聚簇索引记录加锁,完后才会对下一条二级索引记录进行加锁,以此类推~ 画个图表示一下就是这样:

    5aa788827a49ad4d24731242c580b9e3.png

    再来看下边这个语句:

    SELECT * FROM hero FORCE INDEX(idx_name) WHERE name <= 'c曹操' LOCK IN SHARE MODE;

    这个语句的加锁情况就有点儿有趣了。前边说在使用number <= 8这个条件的语句中,需要把number值为15的记录也加一个锁,之后又判断它不符合边界条件而把锁释放掉。而对于查询条件name <= 'c曹操'的语句来说,执行该语句需要使用到二级索引,而与二级索引相关的条件是可以使用索引条件下推这个特性的。设计InnoDB的大叔规定,如果一条记录不符合索引条件下推中的条件的话,直接跳到下一条记录(这个过程根本不将其返回到server层),如果这已经是最后一条记录,那么直接向server层报告查询完毕。但是这里头有个问题呀:先对一条记录加了锁,然后再判断该记录是不是符合索引条件下推的条件,如果不符合直接跳到下一条记录或者直接向server层报告查询完毕,这个过程中并没有把那条被加锁的记录上的锁释放掉呀!!!。本例中使用的查询条件是name <= 'c曹操',在为name值为'c曹操'的二级索引记录以及它对应的聚簇索引加锁之后,会接着二级索引中的下一条记录,也就是name值为'l刘备'的那条二级索引记录,由于该记录不符合索引条件下推的条件,而且是范围查询的最后一条记录,会直接向server层报告查询完毕,重点是这个过程中并不会释放name值为'l刘备'的二级索引记录上的锁,也就导致了语句执行完毕时的加锁情况如下所示:

    31ed4791ce193a592c7a3665e572b78c.png

    这样子会造成一个尴尬情况,假如T1执行了上述语句并且尚未提交,T2再执行这个语句:

    SELECT * FROM hero WHERE name = 'l刘备' FOR UPDATE;

    T2中的语句需要获取name值为l刘备的二级索引记录上的X型正经记录锁,而T1中仍然持有name值为l刘备的二级索引记录上的S型正经记录锁,这就造成了T2获取不到锁而进入等待状态。

    小贴士:为啥不能释放不符合索引条件下推中的条件的二级索引记录上的锁呢?这个问题我也没想明白,人家就是这么规定的,如果有明白的小伙伴可以加我微信 xiaohaizi4919 来讨论一下哈~ 再强调一下,我使用的MySQL版本是5.7.21,不保证其他版本中的加锁情景是否完全一致。

  • 使用SELECT ... FOR UPDATE语句时:

    SELECT ... FOR UPDATE语句类似,只不过加的是X型正经记录锁

  • 使用UPDATE ...来为记录加锁,比方说:

    UPDATE hero SET country = '汉' WHERE name >= 'c曹操';

    小贴士:FORCE INDEX只对SELECT语句起作用,UPDATE语句虽然支持该语法,但实质上不起作用,DELETE语句压根儿不支持该语法。

    假设该语句执行时使用了idx_name二级索引来进行锁定读,那么它的加锁方式和上边所说的SELECT ... FOR UPDATE语句一致。如果有其他二级索引列也被更新,那么也会为对应的二级索引记录进行加锁,就不赘述了。不过还有一个有趣的情况,比方说:

    UPDATE hero SET country = '汉' WHERE name <= 'c曹操';

    我们前边说的索引条件下推这个特性只适用于SELECT语句,也就是说UPDATE语句中无法使用,那么这个语句就会为name值为'c曹操''l刘备'的二级索引记录以及它们对应的聚簇索引进行加锁,之后在判断边界条件时发现name值为'l刘备'的二级索引记录不符合name <= 'c曹操'条件,再把该二级索引记录和对应的聚簇索引记录上的锁释放掉。这个过程如下图所示:

    001c29c6957a18c1655191e47cc25c6b.png
  • 使用DELETE ...来为记录加锁,比方说:

    DELETE FROM hero WHERE name >= 'c曹操';

    DELETE FROM hero WHERE name <= 'c曹操';

    如果这两个语句采用二级索引来进行锁定读,那么它们的加锁情况和更新带有二级索引列的UPDATE语句一致,就不画图了。

全表扫描的情况

比方说:

SELECT * FROM hero WHERE country  = '魏' LOCK IN SHARE MODE;

由于country列上未建索引,所以只能采用全表扫描的方式来执行这条查询语句,存储引擎每读取一条聚簇索引记录,就会为这条记录加锁一个S型正常记录锁,然后返回给server层,如果server层判断country = '魏'这个条件是否成立,如果成立则将其发送给客户端,否则会释放掉该记录上的锁,画个图就像这样:

3023aa8d1bf2bacbaf9029bbef6a0d49.png

使用SELECT ... FOR UPDATE进行加锁的情况与上边类似,只不过加的是X型正经记录锁,就不赘述了。

对于UPDATE ...DELETE ...的语句来说,在遍历聚簇索引中的记录,都会为该聚簇索引记录加上X型正经记录锁,然后:

  • 如果该聚簇索引记录不满足条件,直接把该记录上的锁释放掉。

  • 如果该聚簇索引记录满足条件,则会对相应的二级索引记录加上X型正经记录锁(DELETE语句会对所有二级索引列加锁,UPDATE语句只会为更新的二级索引列对应的二级索引记录加锁)。

推荐阅读

1、彻底搞懂StringBuffer和StringBuilder的区别

2、徒手撸一个Spring MVC框架

3、徒手撸一个MyBatis框架

4、B站视频上新, 我又撸了一套干货满满的教程满

4f29ee991aa9cccda7940a223cf6252b.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/385929.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

大厂面试必问!如何写出高效率的SQL

前言 Spring框架自2002年诞生以来一直备受开发者青睐&#xff0c;它包括SpringMVC、SpringBoot、Spring Cloud、Spring Cloud Dataflow等解决方案。有人亲切的称之为&#xff1a;Spring 全家桶。 很多研发人员把spring看作心目中最好的java项目&#xff0c;没有之一。所以这是…

accsess转成mysql语句_轻松教你SQL转ACCESS

SQL数据库转access数据库步骤&#xff1a; 1. 建立access数据库&#xff1a;在access中建立access数据库和表&#xff0c;access字段类型与sql中字段类型的对应关系。 2. 在Access中建表是应注意它的保留关键字&#xff1a;比如Password 如果表中的某个字段使用了关键字&#x…

大厂首发:2021年Java工作或更难找

前言 提起MySQL&#xff0c;其实网上已经有一大把教程了&#xff0c;为什么我还要写这篇文章呢&#xff0c;大概是因为网上很多网站都是比较零散&#xff0c;而且描述不够直观&#xff0c;不能系统对MySQL相关知识有一个系统的学习&#xff0c;导致不能形成知识体系。为此我撰…

大厂首发:338页网易Java面试真题解析火爆全网

前言 为什么互联网资讯这么发达&#xff0c;但是没有出现技术人才井喷&#xff1f; 为什么会出现应届生薪资倒挂多年老员工的现象&#xff1f; 这个世界有太多的现象都可以用**“二八定律”**来解释。 20%拿着高工资&#xff0c;80%每天挣扎在增删改查边缘。 职场一分钱一…

mysql 导入dbm文件_DBM数据导入到mysql数据库方法

本文章分享一篇关于DBM数据导入到mysql数据库方法&#xff0c;有需要的同学可以参考一下本实例。首先说明一下&#xff0c;我以前使用PERL&#xff0c;保存文件 用DBM&#xff0c;有5万多条记录&#xff0c;每条记录有15个字段。现在想用MYSQL&#xff0c;要吧记录导过来。第一…

大牛手把手教你!2021Java最新大厂面试真题

引言 众所周知&#xff0c;软件系统有三高&#xff1a;**高并发、高性能、高可用。**三者既有区别也有联系&#xff0c;门门道道很多&#xff0c;全面讨论可以大战三天三夜。 高并发对于Java开发者来说都不陌生&#xff0c;每年天猫双十一&#xff0c;秒杀大促等场景阿里都稳…

高通8155车载芯片_WEY来“摩卡”云首秀,搭载高通8155芯片

本期亮点“咖啡”一词源自埃塞俄比亚的一个名叫卡法的古镇&#xff0c;在希腊语中“Kaweh”的意思是“力量与热情“&#xff0c;而摩卡咖啡代表着创新和更多可能性。摩卡咖啡是由浓缩咖啡、牛奶、鲜奶油及巧克力糖浆制成的“巧克力咖啡”&#xff0c;相比于其他制作方法&#x…

大牛深入讲解!高并发你真的理解透彻了吗

前言 最其实不管什么时候&#xff0c;找工作都跑不了面试。目前很多小编都做了面试手册了&#xff0c;那就是别人家的孩子都有糖了&#xff0c;作为一个自觉的小编&#xff0c;必须搞。 容我先絮叨一下&#xff0c;制作这个面试手册差不多花了3个多星期时间&#xff0c;过程还…

太厉害了!2021年互联网大厂Java笔经

开头 设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了重用代码、让代码更容易被他人理解、保证代码可靠性。在项目中合理地运用设计模式可以完美地解决很多问题&#xff0c;每种模式在现实中都有相应的原理来与之对应&…

fritzing导入元件_超屌的 fritzing 新建元件

标签&#xff1a;fritzing 有多好&#xff0c;用了才知道&#xff0c;但是通常会遇到一个问题&#xff0c;他的元件库不一定够用&#xff0c;这时候就得自己做元件了&#xff0c;但是搜了一下网上没有相关的教程啊。算了&#xff0c;去官网看英文吧。。然后官网的帮助见 http:/…

java将字节数组转换成字符串,面试心得体会

1. 前言 大家都知道&#xff0c;Postman是一个非常受欢迎的API接口调试工具&#xff0c;提供有Chrome扩展插件版和独立的APP&#xff0c;不过它的很多高级功能都需要付费才能使用。 如果你连Postman都还没有用过&#xff0c;不妨可以先体验一番。 Postman官网&#xff1a; h…

java将数组中的数据修改,深度集成!

京东Java研发岗一面&#xff08;基础面&#xff0c;约1小时&#xff09; 自我介绍&#xff0c;主要讲讲做了什么和擅长什么 springmvc和spring-boot区别 Autowired的实现原理 Bean的默认作用范围是什么&#xff1f;其他的作用范围&#xff1f; 索引是什么概念有什么作用&am…

java小游戏毕业论文,你不懂还不学?

1. Spring 特点 Spring 主要有如下特点&#xff1a; 轻量级&#xff1a;Spring 是非侵入式&#xff0c;其中的对象不依赖 Spring 的特定类&#xff1b;控制反转&#xff08;IoC&#xff09;&#xff1a;通过 IoC&#xff0c;促进了低耦合&#xff0c;一个对象依赖的其他对象通…

java小游戏源代码资源,一招彻底弄懂!

BATJ大厂面经 阿里七面面经 蚂蚁金服面经及答案 京东面经 百度美团顺丰京东阿里面经 面试题精选 Java面试手册 MySQL55题及答案 多线程面试题及答案 设计模式面试题及答案 消息中间件面试题及答案 并发编程面试题及答案 面试资料整理汇总 最后 总而言之&#xff0c;面试官问…

java小程序小游戏代码贪吃蛇,附高频面试题合集

One&#xff1a;JVM实践思维图&#xff08;完整版&#xff09; Two&#xff1a; 走近Java 概述 Java技术体系Java发展史Java虚拟机家族&#xff1a;&#xff08;Sun Classic/Exact VM、HotSpot VM、Mobile/Embedded VM、BEA JRockit/IBM J9 VM、BEA Liquid VM/Azul VM、Apache…

java小程序开发平台,隔壁都馋哭了

01 阿里面试题之MySQL 之前的阿里面试题都有做总结&#xff0c;具体面试题内容整理成了文档&#xff0c;本文是针对MySQL系列的&#xff0c;所以下面只展示了自己第一次面试阿里时被吊打问到的一些MySQL难题 请解释关系型数据库概念及主要特点&#xff1f;请说出关系型数据库…

java小程序查看器,成功拿到offer

架构筑基 大家都知道&#xff0c;性能一直是让程序员比较头疼的问题。当系统架构变得复杂而庞大之后&#xff0c;性能方面就会下降&#xff0c;如果想成为一名优秀的架构师&#xff0c;性能优化就是你必须思考的问题。 所以性能优化专题从JVM底层原理到内存优化再到各个中间件…

java小程序设计一个国旗点击国旗唱国歌,看这篇足矣了!

工作的前两年 如果你不能拼爹&#xff0c;或者不想拼爹&#xff0c;最好的方法是拼实力。 合抱之木&#xff0c;生于毫末&#xff1b;九层之台&#xff0c;起于垒土&#xff1b;千里之行&#xff0c;始于足下。 所以&#xff0c;你必须要从基层做起。当然&#xff0c;所谓的基…

java小项目实例,成功入职阿里

我听到的一些发声 你们赚的钱已经可以了&#xff1a; 我一个发小是做土木工程的&#xff0c;上海大学博士&#xff0c;参与很多著名建筑的工程&#xff0c;但是从薪资上看&#xff0c;还不如一些稍微像样的公司的6年多的高级开发。为什么&#xff1f;这就是行业的红利&#xf…

java属于什么行业,吐血整理

微服务的发展 微服务倡导将复杂的单体应用拆分为若干个功能简单、松耦合的服务&#xff0c;这样可以降低开发难度、增强扩展性、便于敏捷开发。当前被越来越多的开发者推崇&#xff0c;很多互联网行业巨头、开源社区等都开始了微服务的讨论和实践。 微服务落地存在的问题 虽…