java开发面试笔记

1、hashMap

hashmap源码分析-逐行注释版:

https://www.cnblogs.com/AlwaysSui/p/14791949.html

1.谈一下HashMap的特性?

  1. HashMap存储键值对实现快速存取,允许为null。key值不可重复,若key值重复则覆盖。

  2. 非同步,线程不安全。

  3. 底层是hash表,不保证有序(比如插入的顺序)

2.谈一下HashMap的底层原理是什么?

基于hashing的原理,jdk8后采用数组+链表+红黑树的数据结构。我们通过put和get存储和获取对象。当我们给put()方法传递键和值时,先对键做一个hashCode()的计算来得到它在bucket数组中的位置来存储Entry对象。当获取对象时,通过get获取到bucket的位置,再通过键对象的equals()方法找到正确的键值对,然后在返回值对象。

3.谈一下hashMap中put是如何实现的?

1.计算关于key的hashcode值(与Key.hashCode的高16位做异或运算)

2.如果散列表为空时,调用resize()初始化散列表

3.如果没有发生碰撞,直接添加元素到散列表中去

4.如果发生了碰撞(hashCode值相同),进行三种判断

4.1:若key地址相同或者equals后内容相同,则替换旧值

4.2:如果是红黑树结构,就调用树的插入方法

4.3:链表结构,循环遍历直到链表中某个节点为空,尾插法进行插入,插入之后判断链表个数是否到达变成红黑树的阙值8;也可以遍历到有节点与插入元素的哈希值和内容相同,进行覆盖。

5.如果桶满了大于阀值,则resize进行扩容

4.谈一下hashMap中什么时候需要进行扩容,扩容resize()又是如何实现的?

调用场景:

1.初始化数组table

2.当数组table的size达到阙值时即++size > load factor * capacity 时,也是在putVal函数中

实现过程:(细讲)

1.通过判断旧数组的容量是否大于0来判断数组是否初始化过

否:进行初始化

  • 判断是否调用无参构造器,
    • 是:使用默认的大小和阙值
    • 否:使用构造函数中初始化的容量,当然这个容量是经过tableSizefor计算后的2的次幂数

是,进行扩容,扩容成两倍(小于最大值的情况下),之后在进行将元素重新进行与运算复制到新的散列表中

概括的讲:扩容需要重新分配一个新数组,新数组是老数组的2倍长,然后遍历整个老结构,把所有的元素挨个重新hash分配到新结构中去。

PS:可见底层数据结构用到了数组,到最后会因为容量问题都需要进行扩容操作

5.谈一下hashMap中get是如何实现的?

对key的hashCode进行hashing,与运算计算下标获取bucket位置,如果在桶的首位上就可以找到就直接返回,否则在树中找或者链表中遍历找,如果有hash冲突,则利用equals方法去遍历链表查找节点。

6.谈一下HashMap中hash函数是怎么实现的?还有哪些hash函数的实现方式?

对key的hashCode做hash操作,与高16位做异或运算

还有平方取中法,除留余数法,伪随机数法

7.为什么不直接将key作为哈希值而是与高16位做异或运算?

因为数组位置的确定用的是与运算,仅仅最后四位有效,设计者将key的哈希值与高16为做异或运算使得在做&运算确定数组的插入位置时,此时的低位实际是高位与低位的结合,增加了随机性,减少了哈希碰撞的次数。

HashMap默认初始化长度为16,并且每次自动扩展或者是手动初始化容量时,必须是2的幂。

8.为什么是16?为什么必须是2的幂?如果输入值不是2的幂比如10会怎么样?

  1. 为了数据的均匀分布,减少哈希碰撞。因为确定数组位置是用的位运算,(index=e.hash&(tab.length-1)若数据不是2的次幂则会增加哈希碰撞的次数和浪费数组空间。(PS:其实若不考虑效率,求余也可以就不用位运算了也不用长度必需为2的幂次)

  2. 输入数据若不是2的幂,HashMap通过一通位移运算和或运算得到的肯定是2的幂次数,并且是离那个数最近的数字

9.谈一下当两个对象的hashCode相等时会怎么样?

会产生哈希碰撞,若key值相同则替换旧值,不然链接到链表后面,链表长度超过阙值8就转为红黑树存储

10.如果两个键的hashcode相同,你如何获取值对象?

HashCode相同,通过equals比较内容获取值对象

11."如果HashMap的大小超过了负载因子(load factor)定义的容量,怎么办?

超过阙值会进行扩容操作,概括的讲就是扩容后的数组大小是原数组的2倍,将原来的元素重新hashing放入到新的散列表中去。

12.HashMap和HashTable的区别

相同点:都是存储key-value键值对的

不同点:

  • HashMap允许Key-value为null,hashTable不允许;
  • hashMap没有考虑同步,是线程不安全的。hashTable是线程安全的,给api套上了一层synchronized修饰;
  • HashMap继承于AbstractMap类,hashTable继承与Dictionary类。
  • 迭代器(Iterator)。HashMap的迭代器(Iterator)是fail-fast迭代器,而Hashtable的enumerator迭代器不是fail-fast的。所以当有其它线程改变了HashMap的结构(增加或者移除元素),将会抛出ConcurrentModificationException。
  • 容量的初始值和增加方式都不一样:HashMap默认的容量大小是16;增加容量时,每次将容量变为"原始容量x2"。Hashtable默认的容量大小是11;增加容量时,每次将容量变为"原始容量x2 + 1";
  • 添加key-value时的hash值算法不同:HashMap添加元素时,是使用自定义的哈希算法。Hashtable没有自定义哈希算法,而直接采用的key的hashCode()。

13.请解释一下HashMap的参数loadFactor,它的作用是什么?

loadFactor表示HashMap的拥挤程度,影响hash操作到同一个数组位置的概率。默认loadFactor等于0.75,当HashMap里面容纳的元素已经达到HashMap数组长度的75%时,表示HashMap太挤了,需要扩容,在HashMap的构造器中可以定制loadFactor。

14.传统hashMap的缺点(为什么引入红黑树?):

JDK 1.8 以前 HashMap 的实现是 数组+链表,即使哈希函数取得再好,也很难达到元素百分百均匀分布。当 HashMap 中有大量的元素都存放到同一个桶中时,这个桶下有一条长长的链表,这个时候 HashMap 就相当于一个单链表,假如单链表有 n 个元素,遍历的时间复杂度就是 O(n),完全失去了它的优势。针对这种情况,JDK 1.8 中引入了 红黑树(查找时间复杂度为 O(logn))来优化这个问题。

15. 平时在使用HashMap时一般使用什么类型的元素作为Key?

选择Integer,String这种不可变的类型,像对String的一切操作都是新建一个String对象,对新的对象进行拼接分割等,这些类已经很规范的覆写了hashCode()以及equals()方法。作为不可变类天生是线程安全的,

2、线程池

3、MySQL数据库引擎、事务、锁机制

1. 引擎

InnoDB 默认使用

MyISAM 早些年使用

InnoDB MyISAM
事务支持 不支持 支持
数据行锁定 不支持(表锁) 支持
外键约束 不支持 支持
全文索引 支持 不支持
表空间的大小 较小 较大,约为2倍

常规使用操作

  • MyISAM:节约空间,速度较快
  • InnoDB:安全性高,支持事物的处理,多表多用户操作

2. 事务

查看执行计划

  • type
  • key
  • extra

看mysql官网https://dev.mysql.com/doc/refman/8.0/en/explain-output.html#explain-join-types

索引

加快数据访问

数据结构:B+树

为什么使用B+树:

索引系统:内存与磁盘的交互读写,怎么磁盘读写快,IO问题,io是硬件问题,硬件瓶颈改不了,

非得软件层面优化:

  • 读取次数少
  • 读取量少

考虑数据格式,K、V格式的数据,用什么数据格式:hash表、树?

数据库文件有1T,不可能全部放内存读取

数据再进行读取时要分块读取

相似的操作系统原理:1.局部性原理:数据和程序都有聚集成群的倾向-空间局部性,之前被访问过的数据,有可能很快被下一次访问到-时间局部性

2.磁盘预读:内存跟磁盘发生交互时,有一个基本的逻辑单位叫做页,页的大小根操作系统相关,一般是4k或者8k,我们在进行数据交互时可以是页的整数倍。

InnoDB存储引擎每次读取数据读16k


K表示索引的值,V代表数据。

数据结构的选择:

  • hash表:存在的问题:

    • 需要优良的hash算法

    • 无序、无法进行范围查找,效率极低

    • 需要大量的内存空间

      在mysql有没有hash索引:对memory这种存储引擎支持hash索引

      InnoDB支持自适应hash

  • 树:

    • BST>AVL>红黑树>B树>B+树
    • 前三者共同点,都是二叉
    • AVL(严格平衡树)和红黑树(非严格平衡树)都是平衡,
      • 二叉的缺点是:加入数据过多,会导致树很深,进而增加存取IO次数变多
      • 二叉的优点是:有序
    • 改成多叉排序树:使树变成矮胖子,
    • B树:每个节点的孩子小于Degree,
      • image-20210607201019984
    • B+树:只存索引
      • 一般情况下mysql占用几层:3-4层的B+树,足以支撑千万级别的数据量。
        • 因为树里能有多少数据要看索引占用多少字节,一般情况下用int作为索引,因为int一般情况下占用空间更小。
        • int索引为什么要自增:页分裂影响,不会对前面的上层key有影响,涉及表索引维护问题
        • image-20210607200909730
        • image-20210607201254698

3. 锁:

排他索

共享锁

意向锁

间隙锁

临键锁

乐观锁

自增锁

悲观锁

segment簇

乐观悲观

乐观:version

悲观:排它锁

按照锁定的粒度

  • 表所

    • 意向锁
    • 自增锁
  • 行锁

    • 间隙锁
    • 临键锁
    • 记录锁

针对锁的方式

  • 排他:写锁X
  • 共享:读锁S

锁在一般情况下,很少人为去做操作,如果有同学做过的话,基本只有两种,

select for update

select lock in share mode

锁的作用:为了满足事务隔离性,解决并发问题,保证事务的一致性。

锁监控

IX:意向锁

Innodb是给索引加的锁

X:表示间隙锁

set global innodb_status_output_locks=1;

show engine innodb status\G;

set autocommit=0//关闭事务自动提交
commit;
begin;
select * from t1 where id=10 for update;
show engine innodb status\G;//锁监控

如果遇到死锁怎么排查!!!

lock_mode X locks rec but not gap:行锁

lock_mode X locks rec but not gap waiting

LATEST DETECTED DEADLOCK:

image-20210607213504165

profile :性能分析(未来被弃用)

set profitling=1;

show profile for query 1;

image-20210607214013447

自带performance_schema数据库,

里面存储的就是性能的监控信息

4. 调优问题怎么回答?

表达方式问题!

  1. 你参与过哪些mysql的调优?
    1. 大部分mysql的优化都是出现问题之后根据实际情况开始调整,但是在数据库设计的时候,就应该考虑优化的问题,比如表的设计,schema的设计,数据库的分库分表,都是需要提前准备的,但是在准备过程中难免出现一些纰漏,在这种情况下,就需要根据实际情况来进行调整,我之前做mysql调优的时候,基本上都是从以下几个维度开始考虑的,分别是执行计划,索引的使用,sql语句的调整,性能的监控,sql语句的调整,参数的调整,这几个方面开始入手的,就拿我们之前的一个xx项目而言,当时线上环境遇到了XX问题,我通过调整参数,设计索引,来解决了这个问题,性能直接提升了XX倍,当然还有其他场景,比如XXX

image-20210607225450688

“正常”索引列值或列值的索引例如。例如,在下表中,给定t1行的索引条目包括完整 col1值和 col2由其前 10 个字符组成的值的前缀 :

CREATE TABLE t1 (  col1 VARCHAR(10),  col2 VARCHAR(20),  INDEX (col1, col2(10)) );

4、SQL语句

1. MySQL 创建数据表

创建MySQL数据表需要以下信息:

  • 表名
  • 表字段名
  • 定义每个表字段

语法

以下为创建MySQL数据表的SQL通用语法:

CREATE TABLE table_name (column_name column_type);

以下例子中我们将在 RUNOOB 数据库中创建数据表runoob_tbl:

CREATE TABLE IF NOT EXISTS `runoob_tbl`(
   `runoob_id` INT UNSIGNED AUTO_INCREMENT,
   `runoob_title` VARCHAR(100) NOT NULL,
   `runoob_author` VARCHAR(40) NOT NULL,
   `submission_date` DATE,
   PRIMARY KEY ( `runoob_id` )
)ENGINE=InnoDB DEFAULT CHARSET=utf8;

image-20210606123228610

注意列名不是用单引号 ‘ ,而是用 ` 符号框起

2. MySQL 删除数据表

MySQL中删除数据表是非常容易操作的,但是你在进行删除表操作时要非常小心,因为执行删除命令后所有数据都会消失。

语法

以下为删除MySQL数据表的通用语法:

DROP TABLE table_name ;

在命令提示窗口中删除数据表

在mysql>命令提示窗口中删除数据表SQL语句为 DROP TABLE

实例

以下实例删除了数据表runoob_tbl:

root@host# mysql -u root -p
Enter password:*******
mysql> use RUNOOB;
Database changed
mysql> DROP TABLE runoob_tbl
Query OK, 0 rows affected (0.8 sec)
mysql>

3. MySQL 插入数据

MySQL 表中使用 INSERT INTO SQL语句来插入数据。

你可以通过 mysql> 命令提示窗口中向数据表中插入数据,或者通过PHP脚本来插入数据。

语法

以下为向MySQL数据表插入数据通用的 INSERT INTO SQL语法:

INSERT INTO table_name ( field1, field2,...fieldN )
                       VALUES
                       ( value1, value2,...valueN );

如果数据是字符型,必须使用单引号或者双引号,如:"value"。


通过命令提示窗口插入数据

以下我们将使用 SQL INSERT INTO 语句向 MySQL 数据表 runoob_tbl 插入数据

实例

以下实例中我们将向 runoob_tbl 表插入三条数据:

root@host# mysql -u root -p password;
Enter password:*******
mysql> use RUNOOB;
Database changed
mysql> INSERT INTO runoob_tbl 
    -> (runoob_title, runoob_author, submission_date)
    -> VALUES
    -> ("学习 PHP", "菜鸟教程", NOW());
Query OK, 1 rows affected, 1 warnings (0.01 sec)
mysql> INSERT INTO runoob_tbl
    -> (runoob_title, runoob_author, submission_date)
    -> VALUES
    -> ("学习 MySQL", "菜鸟教程", NOW());
Query OK, 1 rows affected, 1 warnings (0.01 sec)
mysql> INSERT INTO runoob_tbl
    -> (runoob_title, runoob_author, submission_date)
    -> VALUES
    -> ("JAVA 教程", "RUNOOB.COM", '2016-05-06');
Query OK, 1 rows affected (0.00 sec)
mysql>

注意: 使用箭头标记 -> 不是 SQL 语句的一部分,它仅仅表示一个新行,如果一条SQL语句太长,我们可以通过回车键来创建一个新行来编写 SQL 语句,SQL 语句的命令结束符为分号 ;

在以上实例中,我们并没有提供 runoob_id 的数据,因为该字段我们在创建表的时候已经设置它为 AUTO_INCREMENT(自动增加) 属性。 所以,该字段会自动递增而不需要我们去设置。实例中 NOW() 是一个 MySQL 函数,该函数返回日期和时间。

4. MySQL 查询数据

MySQL 数据库使用SQL SELECT语句来查询数据。

你可以通过 mysql> 命令提示窗口中在数据库中查询数据,或者通过PHP脚本来查询数据。

语法

以下为在MySQL数据库中查询数据通用的 SELECT 语法:

SELECT column_name,column_name
FROM table_name
[WHERE Clause]
[LIMIT N][ OFFSET M]
  • 查询语句中你可以使用一个或者多个表,表之间使用逗号(,)分割,并使用WHERE语句来设定查询条件。
  • SELECT 命令可以读取一条或者多条记录。
  • 你可以使用星号(*)来代替其他字段,SELECT语句会返回表的所有字段数据
  • 你可以使用 WHERE 语句来包含任何条件。
  • 你可以使用 LIMIT 属性来设定返回的记录数。
  • 你可以通过OFFSET指定SELECT语句开始查询的数据偏移量。默认情况下偏移量为0。
    • 当limit后面跟两个参数的时候,第一个数表示要跳过的数量,后一位表示要取的数量,例如select* from article LIMIT 1,3 就是跳过1条数据,从第2条数据开始取,取3条数据,也就是取2,3,4三条数据
    • 当 limit后面跟一个参数的时候,该参数表示要取的数据的数量例如 select* from article LIMIT 3 表示直接取前三条数据,类似sqlserver里的top语法。
    • 当 limit和offset组合使用的时候,limit后面只能有一个参数,表示要取的的数量,offset表示要跳过的数量 。例如select * from article LIMIT 3 OFFSET 1 表示跳过1条数据,从第2条数据开始取,取3条数据,也就是取2,3,4三条数据

5. MySQL WHERE 子句

我们知道从 MySQL 表中使用 SQL SELECT 语句来读取数据。

如需有条件地从表中选取数据,可将 WHERE 子句添加到 SELECT 语句中。

语法

以下是 SQL SELECT 语句使用 WHERE 子句从数据表中读取数据的通用语法:

SELECT field1, field2,...fieldN FROM table_name1, table_name2...
[WHERE condition1 [AND [OR]] condition2.....
  • 查询语句中你可以使用一个或者多个表,表之间使用逗号, 分割,并使用WHERE语句来设定查询条件。
  • 你可以在 WHERE 子句中指定任何条件。
  • 你可以使用 AND 或者 OR 指定一个或多个条件。
  • WHERE 子句也可以运用于 SQL 的 DELETE 或者 UPDATE 命令。
  • WHERE 子句类似于程序语言中的 if 条件,根据 MySQL 表中的字段值来读取指定的数据。

以下为操作符列表,可用于 WHERE 子句中。

下表中实例假定 A 为 10, B 为 20

操作符 描述 实例
= 等号,检测两个值是否相等,如果相等返回true (A = B) 返回false。
<>, != 不等于,检测两个值是否相等,如果不相等返回true (A != B) 返回 true。
> 大于号,检测左边的值是否大于右边的值, 如果左边的值大于右边的值返回true (A > B) 返回false。
< 小于号,检测左边的值是否小于右边的值, 如果左边的值小于右边的值返回true (A < B) 返回 true。
>= 大于等于号,检测左边的值是否大于或等于右边的值, 如果左边的值大于或等于右边的值返回true (A >= B) 返回false。
<= 小于等于号,检测左边的值是否小于或等于右边的值, 如果左边的值小于或等于右边的值返回true (A <= B) 返回 true。

如果我们想在 MySQL 数据表中读取指定的数据,WHERE 子句是非常有用的。

使用主键来作为 WHERE 子句的条件查询是非常快速的。

如果给定的条件在表中没有任何匹配的记录,那么查询不会返回任何数据。

6. MySQL UPDATE 更新

如果我们需要修改或更新 MySQL 中的数据,我们可以使用 SQL UPDATE 命令来操作。

语法

以下是 UPDATE 命令修改 MySQL 数据表数据的通用 SQL 语法:

UPDATE table_name SET field1=new-value1, field2=new-value2
[WHERE Clause]
  • 你可以同时更新一个或多个字段。
  • 你可以在 WHERE 子句中指定任何条件。
  • 你可以在一个单独表中同时更新数据。

当你需要更新数据表中指定行的数据时 WHERE 子句是非常有用的。

7. MySQL DELETE 语句

你可以使用 SQL 的 DELETE FROM 命令来删除 MySQL 数据表中的记录。

你可以在 mysql> 命令提示符或 PHP 脚本中执行该命令。

语法

以下是 SQL DELETE 语句从 MySQL 数据表中删除数据的通用语法:

DELETE FROM table_name [WHERE Clause]
  • 如果没有指定 WHERE 子句,MySQL 表中的所有记录将被删除。
  • 你可以在 WHERE 子句中指定任何条件
  • 您可以在单个表中一次性删除记录。

当你想删除数据表中指定的记录时 WHERE 子句是非常有用的。

8. MySQL LIKE 子句

我们知道在 MySQL 中使用 SQL SELECT 命令来读取数据, 同时我们可以在 SELECT 语句中使用 WHERE 子句来获取指定的记录。

WHERE 子句中可以使用等号 = 来设定获取数据的条件,如 "runoob_author = 'RUNOOB.COM'"。

但是有时候我们需要获取 runoob_author 字段含有 "COM" 字符的所有记录,这时我们就需要在 WHERE 子句中使用 SQL LIKE 子句。

SQL LIKE 子句中使用百分号 %字符来表示任意字符,类似于UNIX或正则表达式中的星号 *****。

如果没有使用百分号 %, LIKE 子句与等号 = 的效果是一样的。

语法

以下是 SQL SELECT 语句使用 LIKE 子句从数据表中读取数据的通用语法:

SELECT field1, field2,...fieldN 
FROM table_name
WHERE field1 LIKE condition1 [AND [OR]] filed2 = 'somevalue'
  • 你可以在 WHERE 子句中指定任何条件。
  • 你可以在 WHERE 子句中使用LIKE子句。
  • 你可以使用LIKE子句代替等号 =
  • LIKE 通常与 % 一同使用,类似于一个元字符的搜索。
  • 你可以使用 AND 或者 OR 指定一个或多个条件。
  • 你可以在 DELETE 或 UPDATE 命令中使用 WHERE...LIKE 子句来指定条件。

在命令提示符中使用 LIKE 子句

以下我们将在 SQL SELECT 命令中使用 WHERE...LIKE 子句来从MySQL数据表 runoob_tbl 中读取数据。

实例

以下是我们将 runoob_tbl 表中获取 runoob_author 字段中以 COM 为结尾的的所有记录:

SQL LIKE 语句:

mysql> use RUNOOB; Database changed mysql> SELECT * from runoob_tbl  WHERE runoob_author LIKE '%COM';
+-----------+---------------+---------------+-----------------+
| runoob_id | runoob_title  | runoob_author | submission_date | 
+-----------+---------------+---------------+-----------------+ 
| 3         | 学习 Java   | RUNOOB.COM    | 2015-05-01      | 
| 4         | 学习 Python | RUNOOB.COM    | 2016-03-06      | 
+-----------+---------------+---------------+-----------------+ 
2 rows in set (0.01 sec)

9. MySQL UNION 操作符

本教程为大家介绍 MySQL UNION 操作符的语法和实例。

描述

MySQL UNION 操作符用于连接两个以上的 SELECT 语句的结果组合到一个结果集合中。多个 SELECT 语句会删除重复的数据。

语法

MySQL UNION 操作符语法格式:

SELECT expression1, expression2, ... expression_n
FROM tables
[WHERE conditions]
UNION [ALL | DISTINCT]
SELECT expression1, expression2, ... expression_n
FROM tables
[WHERE conditions];

参数

  • expression1, expression2, ... expression_n: 要检索的列。
  • tables: 要检索的数据表。
  • WHERE conditions: 可选, 检索条件。
  • DISTINCT: 可选,删除结果集中重复的数据。默认情况下 UNION 操作符已经删除了重复数据,所以 DISTINCT 修饰符对结果没啥影响。
  • ALL: 可选,返回所有结果集,包含重复数据。

演示数据库

在本教程中,我们将使用 RUNOOB 样本数据库。

下面是选自 "Websites" 表的数据:

mysql> SELECT * FROM Websites;
+----+--------------+---------------------------+-------+---------+
| id | name         | url                       | alexa | country |
+----+--------------+---------------------------+-------+---------+
| 1  | Google       | https://www.google.cm/    | 1     | USA     |
| 2  | 淘宝          | https://www.taobao.com/   | 13    | CN      |
| 3  | 菜鸟教程      | http://www.runoob.com/    | 4689  | CN      |
| 4  | 微博          | http://weibo.com/         | 20    | CN      |
| 5  | Facebook     | https://www.facebook.com/ | 3     | USA     |
| 7  | stackoverflow | http://stackoverflow.com/ |   0 | IND     |
+----+---------------+---------------------------+-------+---------+

下面是 "apps" APP 的数据:

mysql> SELECT * FROM apps;
+----+------------+-------------------------+---------+
| id | app_name   | url                     | country |
+----+------------+-------------------------+---------+
|  1 | QQ APP     | http://im.qq.com/       | CN      |
|  2 | 微博 APP | http://weibo.com/       | CN      |
|  3 | 淘宝 APP | https://www.taobao.com/ | CN      |
+----+------------+-------------------------+---------+
3 rows in set (0.00 sec)

SQL UNION 实例

下面的 SQL 语句从 "Websites" 和 "apps" 表中选取所有不同的country(只有不同的值):

实例

SELECT country FROM Websites
UNION
SELECT country FROM apps
ORDER BY country;

执行以上 SQL 输出结果如下:

img

注释:UNION 不能用于列出两个表中所有的country。如果一些网站和APP来自同一个国家,每个国家只会列出一次。UNION 只会选取不同的值。请使用 UNION ALL 来选取重复的值!


SQL UNION ALL 实例

下面的 SQL 语句使用 UNION ALL 从 "Websites" 和 "apps" 表中选取所有的country(也有重复的值):

实例

SELECT country FROM Websites
UNION ALL
SELECT country FROM apps
ORDER BY country;

执行以上 SQL 输出结果如下:

img


带有 WHERE 的 SQL UNION ALL

下面的 SQL 语句使用 UNION ALL 从 "Websites" 和 "apps" 表中选取所有的中国(CN)的数据(也有重复的值):

实例

SELECT country, name FROM Websites
WHERE country='CN'
UNION ALL
SELECT country, app_name FROM apps
WHERE country='CN'
ORDER BY country;

执行以上 SQL 输出结果如下:

img

重复的是两个都重复的数据

10. MySQL 排序

我们知道从 MySQL 表中使用 SQL SELECT 语句来读取数据。

如果我们需要对读取的数据进行排序,我们就可以使用 MySQL 的 ORDER BY 子句来设定你想按哪个字段哪种方式来进行排序,再返回搜索结果。

语法

以下是 SQL SELECT 语句使用 ORDER BY 子句将查询数据排序后再返回数据:

SELECT field1, field2,...fieldN FROM table_name1, table_name2...
ORDER BY field1 [ASC [DESC][默认 ASC]], [field2...] [ASC [DESC][默认 ASC]]
  • 你可以使用任何字段来作为排序的条件,从而返回排序后的查询结果。
  • 你可以设定多个字段来排序。
  • 你可以使用 ASC 或 DESC 关键字来设置查询结果是按升序或降序排列。 默认情况下,它是按升序排列。
  • 你可以添加 WHERE...LIKE 子句来设置条件。

在命令提示符中使用 ORDER BY 子句

以下将在 SQL SELECT 语句中使用 ORDER BY 子句来读取MySQL 数据表 runoob_tbl 中的数据:

实例

尝试以下实例,结果将按升序及降序排列。

一个参数的SQL 排序

mysql> use RUNOOB; Database changed mysql> SELECT * from runoob_tbl ORDER BY submission_date ASC; 
+-----------+---------------+---------------+-----------------+ 
| runoob_id | runoob_title  | runoob_author | submission_date | +-----------+---------------+---------------+-----------------+ 
| 3         | 学习 Java   | RUNOOB.COM    | 2015-05-01      | 
| 4         | 学习 Python | RUNOOB.COM    | 2016-03-06      | 
| 1         | 学习 PHP    | 菜鸟教程  | 2017-04-12      | 
| 2         | 学习 MySQL  | 菜鸟教程  | 2017-04-12      | 
+-----------+---------------+---------------+-----------------+ 
4 rows in set (0.01 sec)  
mysql> SELECT * from runoob_tbl ORDER BY submission_date DESC; 
+-----------+---------------+---------------+-----------------+ 
| runoob_id | runoob_title  | runoob_author | submission_date | 
+-----------+---------------+---------------+-----------------+ 
| 1         | 学习 PHP    | 菜鸟教程  | 2017-04-12      | 
| 2         | 学习 MySQL  | 菜鸟教程  | 2017-04-12      | 
| 4         | 学习 Python | RUNOOB.COM    | 2016-03-06      | 
| 3         | 学习 Java   | RUNOOB.COM    | 2015-05-01      | 
+-----------+---------------+---------------+-----------------+ 
4 rows in set (0.01 sec)

多个参数的SQL 排序

mysql> select * from users
    -> order by username,
id;
+----+----------+----------+
| id | username | password |
+----+----------+----------+
|  1 | a        | 123      |
|  4 | a        | 333      |
|  3 | c        | 222      |
|  2 | vv       | 234      |
+----+----------+----------+
4 rows in set

11. MySQL GROUP BY 语句

GROUP BY 语句根据一个或多个列对结果集进行分组。

在分组的列上我们可以使用 COUNT, SUM, AVG,等函数。

GROUP BY 语法

SELECT column_name, function(column_name)
FROM table_name
WHERE column_name operator value
GROUP BY column_name;

实例演示

本章节实例使用到了以下表结构及数据,使用前我们可以先将以下数据导入数据库中。

SET NAMES utf8;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
--  Table structure for `employee_tbl`
-- ----------------------------
DROP TABLE IF EXISTS `employee_tbl`;
CREATE TABLE `employee_tbl` (
  `id` int(11) NOT NULL,
  `name` char(10) NOT NULL DEFAULT '',
  `date` datetime NOT NULL,
  `singin` tinyint(4) NOT NULL DEFAULT '0' COMMENT '登录次数',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
--  Records of `employee_tbl`
-- ----------------------------
BEGIN;
INSERT INTO `employee_tbl` VALUES ('1', '小明', '2016-04-22 15:25:33', '1'), ('2', '小王', '2016-04-20 15:25:47', '3'), ('3', '小丽', '2016-04-19 15:26:02', '2'), ('4', '小王', '2016-04-07 15:26:14', '4'), ('5', '小明', '2016-04-11 15:26:40', '4'), ('6', '小明', '2016-04-04 15:26:54', '2');
COMMIT;

SET FOREIGN_KEY_CHECKS = 1;

导入成功后,执行以下 SQL 语句:

mysql> set names utf8;
mysql> SELECT * FROM employee_tbl;
+----+--------+---------------------+--------+
| id | name   | date                | singin |
+----+--------+---------------------+--------+
|  1 | 小明 | 2016-04-22 15:25:33 |      1 |
|  2 | 小王 | 2016-04-20 15:25:47 |      3 |
|  3 | 小丽 | 2016-04-19 15:26:02 |      2 |
|  4 | 小王 | 2016-04-07 15:26:14 |      4 |
|  5 | 小明 | 2016-04-11 15:26:40 |      4 |
|  6 | 小明 | 2016-04-04 15:26:54 |      2 |
+----+--------+---------------------+--------+
6 rows in set (0.00 sec)

接下来我们使用 GROUP BY 语句 将数据表按名字进行分组,并统计每个人有多少条记录:

mysql> SELECT name, COUNT(*) FROM   employee_tbl GROUP BY name;
+--------+----------+
| name   | COUNT(*) |
+--------+----------+
| 小丽 |        1 |
| 小明 |        3 |
| 小王 |        2 |
+--------+----------+
3 rows in set (0.01 sec)

使用 WITH ROLLUP

WITH ROLLUP 可以实现在分组统计数据基础上再进行相同的统计(SUM,AVG,COUNT…)。

例如我们将以上的数据表按名字进行分组,再统计每个人登录的次数:

mysql> SELECT name, SUM(singin) as singin_count FROM  employee_tbl GROUP BY name WITH ROLLUP;
+--------+--------------+
| name   | singin_count |
+--------+--------------+
| 小丽 |            2 |
| 小明 |            7 |
| 小王 |            7 |
| NULL   |           16 |
+--------+--------------+
4 rows in set (0.00 sec)

其中记录 NULL 表示所有人的登录次数。

我们可以使用 coalesce 来设置一个可以取代 NUll 的名称,coalesce 语法:

select coalesce(a,b,c);

参数说明:如果anull,则选择b;如果bnull,则选择c;如果a!=null,则选择a;如果a b c 都为null ,则返回为null(没意义)。

以下实例中如果名字为空我们使用总数代替:

mysql> SELECT coalesce(name, '总数'), SUM(singin) as singin_count FROM  employee_tbl GROUP BY name WITH ROLLUP;
+--------------------------+--------------+
| coalesce(name, '总数') | singin_count |
+--------------------------+--------------+
| 小丽                   |            2 |
| 小明                   |            7 |
| 小王                   |            7 |
| 总数                   |           16 |
+--------------------------+--------------+
4 rows in set (0.01 sec)

12. MySQL 连接的使用

在前几章节中,我们已经学会了如何在一张表中读取数据,这是相对简单的,但是在真正的应用中经常需要从多个数据表中读取数据。

本章节我们将向大家介绍如何使用 MySQL 的 JOIN 在两个或多个表中查询数据。

你可以在 SELECT, UPDATE 和 DELETE 语句中使用 Mysql 的 JOIN 来联合多表查询。

JOIN 按照功能大致分为如下三类:

  • INNER JOIN(内连接,或等值连接):获取两个表中字段匹配关系的记录。
  • LEFT JOIN(左连接):获取左表所有记录,即使右表没有对应匹配的记录。
  • RIGHT JOIN(右连接): 与 LEFT JOIN 相反,用于获取右表所有记录,即使左表没有对应匹配的记录。

本章节使用的数据库结构及数据下载:runoob-mysql-join-test.sql


在命令提示符中使用 INNER JOIN

我们在RUNOOB数据库中有两张表 tcount_tbl 和 runoob_tbl。两张数据表数据如下:

实例

尝试以下实例:

测试实例数据

mysql> use RUNOOB; Database changed mysql> SELECT * FROM tcount_tbl; 
+---------------+--------------+ 
| runoob_author | runoob_count | 
+---------------+--------------+ 
| 菜鸟教程  | 10           | 
| RUNOOB.COM    | 20           | 
| Google        | 22           | 
+---------------+--------------+ 
3 rows in set (0.01 sec)  
mysql> SELECT * from runoob_tbl; 
+-----------+---------------+---------------+-----------------+ 
| runoob_id | runoob_title  | runoob_author | submission_date | 
+-----------+---------------+---------------+-----------------+ 
| 1         | 学习 PHP    | 菜鸟教程  | 2017-04-12      | 
| 2         | 学习 MySQL  | 菜鸟教程  | 2017-04-12      | 
| 3         | 学习 Java   | RUNOOB.COM    | 2015-05-01      | 
| 4         | 学习 Python | RUNOOB.COM    | 2016-03-06      | 
| 5         | 学习 C      | FK            | 2017-04-05      | 
+-----------+---------------+---------------+-----------------+ 
5 rows in set (0.01 sec)

接下来我们就使用MySQL的INNER JOIN(也可以省略 INNER 使用 JOIN,效果一样)来连接以上两张表来读取runoob_tbl表中所有runoob_author字段在tcount_tbl表对应的runoob_count字段值:

INNER JOIN

mysql> SELECT a.runoob_id, a.runoob_author, b.runoob_count FROM runoob_tbl a INNER JOIN tcount_tbl b ON a.runoob_author = b.runoob_author; 
+-------------+-----------------+----------------+ 
| a.runoob_id | a.runoob_author | b.runoob_count | 
+-------------+-----------------+----------------+ 
| 1           | 菜鸟教程    | 10             | 
| 2           | 菜鸟教程    | 10             | 
| 3           | RUNOOB.COM      | 20             | 
| 4           | RUNOOB.COM      | 20             | 
+-------------+-----------------+----------------+ 
4 rows in set (0.00 sec)

以上 SQL 语句等价于:

WHERE 子句

mysql> SELECT a.runoob_id, a.runoob_author, b.runoob_count FROM runoob_tbl a, tcount_tbl b WHERE a.runoob_author = b.runoob_author; 
+-------------+-----------------+----------------+ 
| a.runoob_id | a.runoob_author | b.runoob_count | 
+-------------+-----------------+----------------+ 
| 1           | 菜鸟教程    | 10             |
| 2           | 菜鸟教程    | 10             | 
| 3           | RUNOOB.COM      | 20             | 
| 4           | RUNOOB.COM      | 20             | 
+-------------+-----------------+----------------+ 
4 rows in set (0.01 sec)

img


MySQL LEFT JOIN

MySQL left join 与 join 有所不同。 MySQL LEFT JOIN 会读取左边数据表的全部数据,即便右边表无对应数据。

实例

尝试以下实例,以 runoob_tbl 为左表,tcount_tbl 为右表,理解 MySQL LEFT JOIN 的应用:

LEFT JOIN

mysql> SELECT a.runoob_id, a.runoob_author, b.runoob_count FROM runoob_tbl a LEFT JOIN tcount_tbl b ON a.runoob_author = b.runoob_author; 
+-------------+-----------------+----------------+
| a.runoob_id | a.runoob_author | b.runoob_count |
+-------------+-----------------+----------------+ 
| 1           | 菜鸟教程    | 10             | 
| 2           | 菜鸟教程    | 10             |
| 3           | RUNOOB.COM      | 20             | 
| 4           | RUNOOB.COM      | 20             | 
| 5           | FK              | NULL           | 
+-------------+-----------------+----------------+
5 rows in set (0.01 sec)

以上实例中使用了 LEFT JOIN,该语句会读取左边的数据表 runoob_tbl 的所有选取

的字段数据,即便在右侧表 tcount_tbl中 没有对应的 runoob_author 字段值。

img


MySQL RIGHT JOIN

MySQL RIGHT JOIN 会读取右边数据表的全部数据,即便左边边表无对应数据。

实例

尝试以下实例,以 runoob_tbl 为左表,tcount_tbl 为右表,理解MySQL RIGHT JOIN的应用:

RIGHT JOIN

mysql> SELECT a.runoob_id, a.runoob_author, b.runoob_count FROM runoob_tbl a RIGHT JOIN tcount_tbl b ON a.runoob_author = b.runoob_author; 
+-------------+-----------------+----------------+
| a.runoob_id | a.runoob_author | b.runoob_count | 
+-------------+-----------------+----------------+ 
| 1           | 菜鸟教程    | 10             | 
| 2           | 菜鸟教程    | 10             | 
| 3           | RUNOOB.COM      | 20             | 
| 4           | RUNOOB.COM      | 20             | 
| NULL        | NULL            | 22             | 
+-------------+-----------------+----------------+ 
5 rows in set (0.01 sec)

以上实例中使用了 RIGHT JOIN,该语句会读取右边的数据表 tcount_tbl 的所有选取的字段数据,即便在左侧表 runoob_tbl 中没有对应的runoob_author 字段值。

img

5、TCP协议

image-20210513221952907

image-20210513222218155

帧的类型:识别上层协议

0x0800:上层为IP协议
0x0806:上层为ARP协议
0x代表16进制

协议类型6:tcp,17:udp

TCP/IP协议

TCP/IP协议特点

  • tcp是面向连接(虚连接)的传输层协议
  • 每一条tcp只能有两个端点,所以说tcp协议是点到点的
  • tcp提供可靠交付的服务,无差错,不丢失,不重复,按序到达
  • tcp提供全双工通讯---发送缓存和接收缓存
  • tcp面向字节流--tcp把应用程序交下来的数据看成仅仅是一连串的无结构的字符流(这些字符都是被编上序号的,发送时选取一段字符并添加报文首部信息进行发送)

通常使用的网络(包括互联网)是在 TCP/IP 协议族的基础上运作 的。而 HTTP 属于它内部的一个子集。

TCP/IP协议模型

1.TCP/IP使用的是四层模型

①应用层--->②传输层(tcp协议)--->③网络层(IP协议)--->④数据链路层

1.应用层

应用层决定了向用户提供应用服务时通信的活动。 TCP/IP 协议族内预存了各类通用的应用服务。比如,FTP(File Transfer Protocol,文件传输协议)和 DNS(Domain Name System,域 名系统)服务就是其中两类。 HTTP 协议也处于该层。

2.传输层

传输层对上层应用层,提供处于网络连接中的两台计算机之间的数据 传输。 在传输层有两个性质不同的协议:TCP(Transmission Control Protocol,传输控制协议)和 UDP(User Data Protocol,用户数据报 协议)。

3.网络层(又名网络互连层)

网络层用来处理在网络上流动的数据包。数据包是网络传输的最小数 据单位。该层规定了通过怎样的路径(所谓的传输路线)到达对方计 算机,并把数据包传送给对方。 与对方计算机之间通过多台计算机或网络设备进行传输时,网络层所 起的作用就是在众多的选项内选择一条传输路线。

4.链路层(又名数据链路层,网络接口层)

用来处理连接网络的硬件部分。包括控制操作系统、硬件的设备驱 动、NIC(Network Interface Card,网络适配器,即网卡),及光纤等 物理可见部分(还包括连接器等一切传输媒介)。硬件上的范畴均在 链路层的作用范围之内。

利用 TCP/IP 协议族进行网络通信时,会通过分层顺序与对方进行通 信。发送端从应用层往下走,接收端则往应用层往上走。

我们用 HTTP 举例来说明,首先作为发送端的客户端在应用层 (HTTP 协议)发出一个想看某个 Web 页面的 HTTP 请求。

接着,为了传输方便,在传输层(TCP 协议)把从应用层处收到的数 据(HTTP 请求报文)进行分割,并在各个报文上打上标记序号及端 口号后转发给网络层。

在网络层(IP 协议),增加作为通信目的地的 MAC 地址后转发给链 路层。这样一来,发往网络的通信请求就准备齐全了。

接收端的服务器在链路层接收到数据,按序往上层发送,一直到应用 层。当传输到应用层,才能算真正接收到由客户端发送过来的 HTTP 请求。

img

确保可靠性的TCP协议(包头分析)

按层次分,TCP 位于传输层,提供可靠的字节流服务。

所谓的字节流服务(Byte Stream Service)是指,为了方便传输,将大 块数据分割成以报文段(segment)为单位的数据包进行管理。而可 靠的传输服务是指,能够把数据准确可靠地传给对方。一言以蔽之, TCP 协议为了更容易传送大数据才把数据分割,而且 TCP 协议能够 确认数据最终是否送达到对方。

确保数据能到达目标 为了准确无误地将数据送达目标处,TCP 协议采用了三次握手 (three-way handshaking)策略。用 TCP 协议把数据包送出去后,TCP 不会对传送后的情况置之不理,它一定会向对方确认是否成功送达。

若在握手过程中某个阶段莫名中断,TCP 协议会再次以相同的顺序发 送相同的数据包。

TCP/IP在传送报文的过程中使用了缓存

即发送缓存和接收缓存;

发送方会将发送的数据先放到缓存中,缓存中的数据会被分割成报文段,发送到接收方,如果接收方确认接收到了数据,该数据就会在发送方的缓存里被清除。

同样,接收方也会有接收缓存,当接收方的缓存满了,它就会通知发送方不要再继续发送了,接收方将数据传给应用层,此时接收方缓存里的数据被清空,它会

继续通知发送方继续发

在讲解三次握手协议之前,我们需要知道TCP数据包的格式,之前也说明了TCP协议将大量数据分成许多少量数据并添加首部信息(即数据包) 进行传输的。

这里,我们需要先来分析一下TCP的首部信息里包含了什么

img

首部固定长度为20B,可能会有拓展的一些字段,这些拓展的字段放在选项中(即图中最后一行的选项里),而填充的字段是为了满足这一行是32位。也可以说是为了方便传输

首部信息分析

  1. 第一行(源端口和目的端口)

    即接收端口

  2. 第二行(序号)

    TCP会话的每一端都包含一个32位(bit)的序列号,该序列号被用来跟踪该端发送的数据量。每一个包中都包含序列号,在接收端则通过确认号用来通知发送端数据成功接收

    当某个主机开启一个TCP会话时,他的初始序列号是随机的,可能是0和4,294,967,295之间的任意值

  3. 第三行(确认号)

    表示期望收到发送方下一个报文段的第一个数据字节的序号。若确认号为N,则证明到序号N-1为止的所有数据都已正确收到

  4. 第四行

    第四行由 数据偏移+保留+6个控制位+窗口组成

img

数据偏移:TCP报文段的数据起始处距离报文段的起始处有多远(即首部的长度),以4B为单位,即1个数值是4B

虽然固定的首部是20B,但是有的时候难免会有一些其他的需要去拓展首部 所以说TCP报文的首部是>=20B的 

保留:也就是保留...

然后就是6个**控制位**
  • URG紧急位:URG=1时,标明此报文段中有紧急数据,是高优先级的数据,应尽快传送,不用再缓存里排队,配合紧急指针使用

  • ACK确认位:ACK=1时确认号有效,在连接建立后所有传送的报文都必须把ACK置为1

  • PSH推送位:PSH=1时,接收方尽快交付接收进程,不用等到缓存填满再向上交付

  • RST复位:RST=1时,表明tcp连接中出现了严重差错,必须释放连接,然后再重新建立传输连接

  • SYN同步位:SYN=1时,表明是一个连接请求/连接请求接收报文

  • FIN终止位:FIN=1时,表明此报文发送方数据已发完,要求释放连接

    窗口:指的是发送本报文段的一方的接收窗口(接收缓存),即现在允许对方发送的数据量

  1. 第五行

    校验和+紧急指针

    校验和:校验首部+数据,校验时要加上12B伪首部,第四个字段(协议字段)为6(tcp为6 udp为17)

    紧急指针:URG=1时才有意义,指出本报文段中紧急数据的字节数

  2. 第六行

    选项+填充

    选项:最大报文长度MSS、窗口扩大、时间戳、选择确认....

    现在我们再来分析前面三次握手的协议,理解起来就很简单了

TCP/IP三次握手(TCP连接)

img

在第一次消息:

Client发送连接请求报文段,无应用层的数据

  此时同步标志位SYN=1,seq(序号)=x(随机)

第二次消息

Server为该TCP连接分配缓存和变量,并向客户端返回确认报文段,允许连接,无应用层数据

  此时同步标志位SYN=1,确认标志位ACK=1(有确认标志位,确认号ack才有效,表示Server已经成功接收Client的数据),确认号ack=x+1 (Client发送过来的前x个数据都已接收,现在期望获取到x+1的数据) seq=y(随机)

第三次消息

Client为该TCP连接分配缓存和变量,并向服务器端返回确认的确认,此时可以携带数据了

同步标志位SYN=0(只有在连接请求和连接请求接收时SYN才为1) 确认标志位ACK=1 seq =x+1(在第一次中已经发送了x个,现在从x后面一个发起,故是x+1) ack=y+1(Server端发送了y个,期望收到y+1及以后的数据)

TCP四次挥手(释放连接)

img

第一次消息:

Client发送连接释放报文,停止发送数据,主动关闭TCP连接

  终止位标志位FIN=1,seq=u(此时该报文里无数据,序号仅仅是用来标识该报文段)

第二次消息:

Server返回一个确认报文段,Client到Server这个方向的连接就释放了,此时TCP处于半关闭状态

确认位标志位ACK=1,seq=v(因为之前不知道Server发送到哪里了,故用v表示) 确认号ack=u+1

此时的Client已经不再发送数据了,等待Server发送关闭连接的报文过来

第三次消息:

Server发送完数据,就发出连接释放报文段,主动关闭TCP连接

终止位标志位FIN=1,确认位标志位ACK=1,seq=w,ack=u+1(第二次消息的时候Client已经关闭没有发出确认信息,所以此时确认号与之前相同)

第四次消息:

Client 回送一个确认报文段,再等到时间等待计时器设置的2MSL(最长报文段寿命)后,连接彻底关闭

确认位标志位ACK=1,seq=u+1,ack=w+1

此处需要等待计时器设置的2MSL,因为此次报文可能会丢失,如果不等待那么Server可能永远关闭不了。

此处等待,若报文丢失,Server会重传,Client端接收到Server关闭状态,会更新等待计时器,再发送消息给Server

最后,补充一小点。

在http1.0中,每一次请求都会建立一次连接,而在http1.1中,通过头部keep-alive字段可设置长连接,在一次连接之内请求所有资源。

转载请标明出处:https://www.cnblogs.com/Qiansion/ 转载于:https://www.cnblogs.com/Qiansion/p/10791682.html

Wireshark抓包分析:https://blog.csdn.net/a19881029/article/details/38091243/

dns协议

如"www.sina.com.cn"是一个域名,从严格意义上讲,"sina.com.cn"才被称为域名(全球唯一),而"www"是主机名。

"主机名.域名"称为完全限定域名(FQDN)。一个域名下可以有多个主机,域名全球唯一,那么"主机名.域名"肯定也是全球唯一的。

image-20210513222437135

根域到顶级到一级到二级。二级不值钱了

posted @ 2021-05-13 15:51    阅读(74)  评论(0编辑  收藏  举报