服务器面试笔记

目录

JavaSE 基础

1. JDK 1.8 的新特性

3-1. 值传递和引用传递

3-2. 垃圾回收器

4. 空指针异常[补充常见的异常] :

6. new 字符串 与 赋值字符串的区别

10. 抽象方法是否可以被静态的、本地方法和同步修饰

Servlet部分

0.常见错误 🌿

1. 传输数据时 post 和 get 有什么不同

2. Tomcat 在页面中的路径问题

3.JavaWeb -- Servlet 中request 与 response

3-2. 服务器中转发和重定向

4. servlet 的生命周期

5. TCP 和 UDP

3. Session 的问题

3-1. 什么是session? 如何使用?

3-2. 和cookie 的作用对比:

Tomcat / Servlet 相关:

基础知识:

数据库部分

(五) JDBC相关

(六) 数据库优化

1. 数据库连接池:

2. mysql执行过程以及顺序

3. 索引

5. 事务单元的特性?

5-1 什么是数据库事务?

5-2 事务的四重性质ACID :

5-3 脏读、幻读、不可重复读?

5-4 隔离级别:

6. 锁

6-3. 乐观锁 与 悲观锁

7. MVC模型 三层模型

(七) 题目积累:


Task - Accumulate :

  1. 泛型部分的东西,
  2. ajax 相关的项目代码
  3. ajax 实现文件上传的相关知识点 https://blog.csdn.net/qq_42944520/article/details/84572509
  4. IO中使用缓冲读写操作 项目内涵 https://blog.csdn.net/u012989337/article/details/42212819
  5. 分布式 和 集群: http://www.cocoachina.com/articles/31393
  6. RabbitMQ : https://www.cnblogs.com/wudi521/p/11697703.html

  1. 面试网站 - Java基础总结 : https://blog.csdn.net/weixin_35843569/article/details/112081526

JavaSE 基础

1. JDK 1.8 的新特性

source : https://blog.csdn.net/qq_29411737/article/details/80835658

  • Lambda表达式
  • 函数式接口
  • *方法引用和构造器调用
  • Stream API
  • 接口中的默认方法和静态方法
  • 新时间日期API

3-1. 值传递和引用传递

source: https://www.cnblogs.com/wugongzi/p/11297638.html

✅ 值传递 : 在方法被调用时,实参通过形参把它的内容副本传入方法内部,此时形参接收到的内容是实参值的一个拷贝,因此在方法内对形参的任何操作,都仅仅是对这个副本的操作,不影响原始值的内容。

引用传递 : ”引用”也就是指向真实内容的地址值,在方法调用时,实参的地址通过方法调用被传递给相应的形参,在方法体内,形参和实参指向同一块内存地址,对形参的操作会影响的真实内容。

3-2. 垃圾回收器

垃圾回收器是嵌套在JVM中的一个部分, 在空闲时间,以不定时的方式回收「无任何引用对象占据的」内存空间,回收的是堆内存.

「所以,垃圾回收器回收的是对象的内存空间

⚠️ : 垃圾回收回收的是无任何引用的对象占据的「内存空间」而「不是对象本身

System.gc() //程序员建议进行垃圾回收,但是否进行回收不一定

常见的垃圾回收机制♻️: : 分代复制垃圾回收 、 标记垃圾回收 、 增量垃圾回收.


4. 空指针异常[补充常见的异常] :

空指针异常需要掌握.

空指针异常,访问实例相关 [对象相关] 的数据时, 都会出现空指针异常。[2021-07-03 感觉这句话说的不太对]


6. new 字符串 与 赋值字符串的区别

String s="abce" 和 String s = new String("abcd"); 的区别:

        String s="abce". JVM会在常量池中先查找有有没有一个值为"abcd"的对象,如果有,就会把它赋给当前引用.即原来那个引用和现在这个引用指点向了同一对象,如果没有,则在常量池中新创建一个"abcd",下一次如果有String s1 = "abcd";又会将s1指向"abcd"这个对象,即以这形式声明的字符串,只要值相等,任何多个引用都指向同一对象.


  而String s = new String("abcd");和其它任何对象一样.每调用一次就产生一个对象,只要它们调用。


10. 抽象方法是否可以被静态的、本地方法和同步修饰

答案是都不能。

1)抽象方法需要子类重写,而静态的方法是无法被重写的,因此二者是矛盾的。

2)本地方法是由本地代码(如C代码)实现的方法,而抽象方法是没有实现的,也是矛盾的。

3)synchronized 和方法的实现细节有关,抽象方法不涉及实现细节, 因此也是相互矛盾的。

Servlet部分

0.常见错误 🌿

  • http 协议请求的状态?404 是什么?500?

200(成功):服务器已成功处理了请求。通常,这表示服务器提供了请求的网页

201(已创建):请求成功并且服务器创建了新的资源

202(已接受):服务器已接受请求,但尚未处理

203(非授权信息):服务器已成功处理了请求,但返回的信息可能来自另一来源

204(无内容):服务器成功处理了请求,但没有返回任何内容

205(重置内容):服务器成功处理了请求,但没有返回任何内容

206 (部分内容):服务器成功处理了部分 GET 请求

404(未找到):服务器找不到请求的网页

500(服务器内部错误):服务器遇到错误,无法完成请求

1. 传输数据时 post 和 get 有什么不同

很棒的网站 source : https://www.cnblogs.com/logsharing/p/8448446.html

Sum :

总的来说, 两者的作用是一样的. 他们是http协议中发送请求的两种方法,

Emm这个http呢,是基于TCP/IP 的关于数据在网络中的通信协议. http 的底层是TCP/IP, 所以 get 和 post 的底层也是TCP/IP

区别:

get请求的数据放在 url 中, post 的请求方法在request body [请求体]中. 所以呦, get 的请求不安全 ,post的请求相对安全.

最直观的区别是: get 把参数放在URL中, post 把参数放在request body 中.

还有一个大区别: get 产生一个TCP数据包, post产生两数据包.

对于GET方式的请求,浏览器会把http header和data一并发送出去,服务器响应200(返回数据) ; 而对于POST,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok(返回数据)。 get请求的数据放在url中, post 的请求放在request body[请求体]中, 所以呀 我们可以得知get请求不安全, post请求是安全的.

也就是说,GET只需要汽车跑一趟就把货送到了,而POST得跑两趟,第一趟,先去和服务器打个招呼“嗨,我等下要送一批货来,你开门迎接我”,然后再回头把货送过去。

 

image.png

  ---------------->>>.                  

image.png

doget 方法里面调用dopost ,或者dopost 里调用doget 方法. 这样在dopost/或者doget 里面写一次方法就可以了.「🍀 补充」

后话:

因为POST需要两步,时间上消耗的要多一点,看起来GET比POST更有效。因此Yahoo团队有推荐用GET替换POST来优化网站性能。但这是一个坑!跳入需谨慎。为什么?

1. GET与POST都有自己的语义,不能随便混用。

2. 据研究,在网络环境好的情况下,发一次包的时间和发两次包的时间差别基本可以无视。而在网络环境差的情况下,两次包的TCP在验证数据包完整性上,有非常大的优点。

3. 并不是所有浏览器都会在POST中发送两次包,Firefox就只发送一次。

2. Tomcat 在页面中的路径问题

在设置路径的时候可以分为三种形式:

(1) 完整路径: 协议://ip:端口号/项目名/资源路径 --- http://localhost:8080/day37/page/login.html

(2)绝对路径: /day37/page/login/html <注意: 绝对路径有“/”斜杠>

(3)相对路径: login.html

3.JavaWeb -- Servlet 中request 与 response

source : https://www.cnblogs.com/MrzhangKk/p/5334296.html

source[包含了对应的常用方法] [备用文稿csdn-2021-07-10]: https://www.cnblogs.com/zhangyinhua/p/7629221.html

  • HttpServletRequest

HttpServletRequest对象代表客户端的请求,当客户端通过HTTP协议访问服务器时,HTTP请求头中的所有信息都封装在这个对象中,开发人员通过这个对象的方法,可以获得客户这些信息。

  request就是将请求文本封装而成的对象,所以通过request能获得请求文本中的所有内容,请求头、请求体、请求行 。

image.png

  • HttpServletResponse

Web服务器收到客户端的http请求,会针对每一次请求,分别创建一个用于代表请求的request对象、和代表响应的response对象。

  request和response对象即然代表请求和响应,那我们要获取客户机提交过来的数据,只需要找request对象就行了。要向容器输出数据,只需要找response对象就行了。HttpServletResponse对象代表服务器的响应。这个对象中封装了向客户端发送数据、发送响应头,发送响应状态码的方法。

image.png

1、response

           属于重定向请求;

           其地址栏的URL会改变;

           会向服务器发送两次请求;

2、 request

           属于请求转发;

           其地址栏的URL不会改变;

           向服务器发送一次请求;

举一个区分它们的简单实例:

     A向B借钱:

           第一种:用response。B没有钱,请求失败,但是B告诉A,C有钱。于是A再次向C借钱,C借给A,请求成功。

           第二种:用request。B没有钱,但是B向C借钱然后给A,请求成功。这次A只发送了一次请求,他并不知道借的钱是C的。

Tomcat 在说到请求之后, 会先解析请求(获取参数), 将这些解析出来的数据封装到一个HttpServletRequse对象中, tomcat会创建一个HttpServletResponse对象, 如果有数据要返回, 可以放在这个对象中. tomcat 调用对应的方法, 将刚才创建的两个对象当作参数传递到方法中.

3-2. 服务器中转发和重定向

source : https://blog.csdn.net/weixin_40001125/article/details/88663468

HttpServletRequest 和 HttpServletResponse

总述 : request 是客户端 --> 服务器的请求过程, Response 则相反.

  • Request :
  1. HttpServletRequest 可以理解成一个容器, 存放着请求时带过来的数据. 用getParameter(""); 来获取客户端传递的参数, 返回一个字符信息[Tomcat 接收到的信息是整串的, 通过内部解析将其分解成单个的字符信息]
  2. 转发语句 : req.getRequestDispatcher("/pages/register.html").forward(req,resp);
  • Response:
  1. HttpServletResponse 也理解成一个容器, 由Tomcat 提供, 用来存放返回数据的.
  2. 重定向语句 : resp.sendRedirect("/day32/pages/login.html");

⚠️ : 转发 和 重定向 的区别 :

区别

转发 forward()

重定向 sendRedirect()

根目录

包含项目访问地址

没有项目访问地址

地址栏

不变化

变化

跳转位置

服务器端进行跳转

浏览器段进行跳转

请求域中的数据

不丢失

丢失

注意: 转发 / 或重定向后续代码都会继续执行.

3-3. request 方法

关于request.getRequestDispatcher()的两个方法以及request域的问题 :

source : https://blog.csdn.net/h2503652646/article/details/83661347

image.png

3-4. request域

image.png

4. servlet 的生命周期

(1) servlet 的加载和实例化

当 servlet容器启动客户端发送一个请求时, Servlet容器会查找内存中是否实例对象, 如果有则直接读取该实例的响应请求, 若没有实例对象, 则会创建一个servlet对象.

(2) init( ) 初始化

实例化后, Servlet容器将调用Servlet 的init()方法进行初始化操作, 初始化后servlet 处于能相应请求的就绪状态.

(3) service( ) 方法处理客户端的请求

当接收到客户端的请求时, 调用service() 方法来处理客户端的请求, HttpServlet 会根据service()方法的不同请求来调用doget / dopost 方法.

(4) destory( ) 销毁请求

当Servlet 容器关闭时, Servlet 实例也就没了. 此时是Servlet 容器调用Servlet 的destory() 方法进行清理. Servlet 是由 JVM 的垃圾回收器进行垃圾回收的。

image.png

注意 :servlet 是单例的 [一个路径对应一个实例] , 若多个路径对应一个servlet , 那么会创建多个servlet 对象.

                      

5. TCP 和 UDP

图示资源source : https://baijiahao.baidu.com/s?id=1654225744653405133&wfr=spider&for=pc

详细资源source : https://www.cnblogs.com/williamjie/p/9390164.html

高亮资源source : https://blog.csdn.net/weixin_39789689/article/details/82560805

5-1. TCP 和 UDP 的区别:

  1. 是否基于连接 [数据发送之前是否会建立练习]: TCP是面向连接的协议,而UDP是无连接的协议
  2. 可靠性和有序性的 : TCP面向字节流, UDP面向报文.
  3. 工作效率/运行速度+资源占用 : TCP 的工作效率低于UDP 的工作效率.因为有3+4模式,浪费了时间.且TCP占用的资源较多.
  4. 拥塞机制 : UDP没有拥塞控制, 网络拥塞不会使源主机发送的速率降低
  5. 资源开销 : TCP 首部开销20字节, UDP首部开销是8字节.
  6. 应用场景 : TCP适合于数据量较少,对性能有一定要求的场合; UDP适用于传输大量数据,比如说视频通话这种场合.

5-2. TCP的三次握手和四次挥手:

image.png

image.png

image.png

为什么要进行第三次握手?

答案 : 为了防止服务器端开启一些无用的连接增加服务器开销 以及 防止已失效的连接请求报文段突然又传送到了服务端,因而产生错误。

四次挥手?

所谓的四次挥手即TCP连接的释放(解除)。连接的释放必须是一方主动释放,另一方被动释放。

5-3. http 的 传输两个阶段

[ 传输 + 响应 ]

传输:

请求行 : 请求方法 请求地址 协议版本

请求头 : 头字段 : 值 ...

请求体 : 客户端请求的数据

响应:

状态行 : 协议版本

响应头 : 头字段 : 值 ...

响应正文 : 服务端返回的数据 ,返回的数据放在也只能放在响应正文里.

3. Session 的问题

source : https://www.cnblogs.com/lukelook/p/11095113.html

3-1. 什么是session? 如何使用?

image.png

3-2. 和cookie 的作用对比:

具体来说cookie机制采用的是在客户端保持状态的方案,而session机制采用的是在服务器端保持状态的方案。


Tomcat / Servlet 相关:

基础知识:

1. Servlet 的方法解释

  • init : 初始化
  • destory : 销毁方法
  • getServletInfo : 返回一个代表当前servlet 对象的字符串
  • getServletConfig : 返回一个代表当前servlet 对象的配置对象
  • service : 执行代码的类

2. Session

3. HttpServlet -- 针对Http 的 Servlet 的一个实现类 ---- IDEA 中的生成 : command + N

//用户注册
@WebServlet("/register")
public class Register extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException    {
        doPost(req,resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {       

    }
}

⚠️ : 认为设定的register 类中在使用HttpServlet 方法时,需要继承.

⚠️ : 在第二行代码中的“/register” 斜杠没有会报错,这是使用注释时候的规范. 错误样式 : 《 servlet 映射中的<url pattern>[register]无效 》

image.png

4. 给servlet配置路径: <servlet>标签和<servlat-mapping>标签都是什么?

<servlet>
  <servlet-name>tt</servlet-name>
  <servlet-class>com.qianfeng.part01.TestDemo</servlet-class>
  <init-param>
     <param-name>name</param-name>
     <param-value>李四</param-value>
   </init-param>
</servlet>
<servlet-mapping>
    <servlet-name>tt</servlet-name>
    <url-pattern>/test</url-pattern>
</servlet-mapping>

⚠️ : <servlet> 和 <servlet-mapping> 是同胞修地的关系, 要有都有同时存在, 并且servelt内容下的 <servlet-name> 和 servlet-mapping下的<servlet-name> 是一样的.

5.乱码问题

  • get请求乱码问题
<Connector port="8080" protocol="HTTP/1.1"
           connectionTimeout="20000"
           redirectPort ="8443" URIEncoding="utf-8">
  • post乱码问题
//请求乱码
req.setCharacterEncoding("utf-8");

//返回数据乱码
resp.setCharacterEncoding("utf-8");
//告诉浏览器以以html的格式解析数据, 用utf-8编码转换
resp.setContentType("text/html;charset=utf-8");

几个重要的点:

1. 页面路径的问题

完整路径 : http://ip地址:端口号/项目文件名/具体的资源路径 --- http://localhost:8080/day37/page/login.html

相对路径 : login.html

绝对路径 : 以/开头, /day37/page/login.html

2. http 的 传输两个阶段

[ 传输 + 响应 ]

传输:

请求行 : 请求方法 请求地址 协议版本

请求头 : 头字段 : 值 ...

请求体 : 客户端请求的数据

响应:

状态行 : 协议版本

响应头 : 头字段 : 值 ...

响应正文 : 服务端返回的数据 ,返回的数据放在也只能放在响应正文里.

注意事项: 🍀

http协议除了自身规定的头字段之外, 是允许我们自定义头字段的, 只是这些自定义的字段无法被服务器自动识别而做出相应的处理

http协议本身是短连接[网上有些资源说是无连接, 这个说法是不准确的],无状态(就是服务器端不知道是哪个客户端发来的), 即发起请求, 得到响应,断开连接

  1. 浏览器缓存问题 -

说人话的资源 : https://www.cnblogs.com/skynet/archive/2012/11/28/2792503.html


数据库部分

(五) JDBC相关

1. JDBC 是什么 ?

Java DataBase Connectivity (Java语言连接数据库)

2. JDBC 的本质、通用组件是什么?

JDBC 是一套接口, 接口都有调用者和实现者, 面向接口调用和面向接口写实现类, 这些都属于面向接口编程. 面向接口编程的好处是解耦合,提高程序的扩展力.

通用组件 :

  • DriverManager : 是一个类. 管理数据库驱动的
  • Driver : 数据库驱动, 受DriverManager 管理,
  • Connection : 是一个规定的接口, 用于联系数据库所有的方法.
  • Statement : 从此接口创建的对象将SQL 语句提交到数据库, 主要使用executeUpdate() 和executeQuery() 方法.
  • ResultSet : 结果集对象,封装查询结果

3. JDBC 注册驱动的三种方式

source : https://www.cnblogs.com/mharvay/p/12531796.html

https://blog.csdn.net/weixin_45636641/article/details/102909660

// 最常用的方式 -- 加载驱动类Driver的时候,Driver内部静态代码块执行,进行驱动注册
  Class.forName("com.mysql.jdbc.Driver");
  
// 方式二 :  该方式会加载两次驱动,浪费资源,且必须依赖jdbc驱动类才可编译通过,如果找不到jar包,则编译都过不去
 new com.mysql.jdbc.Driver();
 
// 方式三 : 该方式为系统属性设置,优点是可以加载多个驱动,不足之处就是设置参数较为复杂,同时以来需要DriverManager这个驱动管理对象,所以也不推荐使用
 DriverManager.registerDriver(new Driver());

4. JDBC 执行过程中的顺序:

source : https://blog.csdn.net/weixin_45636641/article/details/102909660

//1. 导入驱动jar包
//2.注册驱动
Class.forName("com.mysql.jdbc.Driver");

//3.获取数据库连接对象 
// url的语法: jdbc:mysql://ip地址(域名):端口号/数据库名称 -- (url:"jdbc:mysql://localhost:3306/数据库名称",sql:"用户名",password:"密码");
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/db3", "root", "root");

//5.获取执行sql的对象 Statement
Statement stmt = conn.createStatement();

//6.执行sql--增删改 && 查. 
// 主要执行增删改语句, int i中的i意思是:本次SQL语句影响了几条语句;
int i = stmt.executeUpdate("insert into  users values()");  

// 主要执行查询语句.返回结果集
ResultSet resultSet = statement.executeQuery("select * from users");

//7.处理结果
System.out.println(count);

//8.释放资源 -- 先开后关
resultSet.close();
stmt.close();
conn.close();

5. JDBC 代码优化

source : https://www.cnblogs.com/ysw-go/p/5459330.html

prepareStatement : 使用PreparedStatement:是Statement的子接口,可以传入带占位符的SQL语句,提供了补充占位符变量的方法.可以调用PreparedStatement的setXxx(int index,Object val)设置占位符的值,其中index的值从1开始执行SQl语句:excuteQuery()或者excuteUpdate()就可以完成查询或者数据的更新.【注意】:此时函数的参数位置不需要传入SQL语句,注意同使用Statement的update函数的差别

 Connection connection = null;
 PreparedStatement ps = null;
 
 connection = JdcbUtils.getConnecton();

 //获取操作sql语句的对象
 ps = connection.prepareStatement("insert into users values(default ,?,?)");
 ps.setString(1,"hanchun");
 ps.setString(2,"hanchun123");
 int i = ps.executeUpdate(); //把参数发送过去
 
 ResultSet rs = ps.executeQuery();

ResultSet: 结果集(ResultSet)是数据中查询结果返回的一种对象,可以说结果集是一个存储查询结果的对象,但是结果集并不仅仅具有存储的功能,他同时还具有操纵数据的功能,可能完成对数据的更新等。

image.png


(六) 数据库优化

1. 数据库连接池:

在初始化的时候一次性获取多个数据库连接保存起来, 等我们使用的时候直接从数据库连接吃中获取, 用完之后回到池中, 以便以后再用. 节省了获取数据库连接和关闭数据库的时间.

2. mysql执行过程以及顺序

source: https://www.cnblogs.com/wyq178/p/11576065.html

连接管理器 : 负责管理外部连接, 沟通缓存区

缓存区 : 缓存查询语句的结果, 一条sql对应一个查询结果, 如果有针对这个表的dml语句,当前表的所有缓存,全部清空, 所以mysql取消了这个设定

解析器 : 将sql语句解析成mysql的命令

优化器 : 优化命令的执行顺序

执行器 : 根据我们的设置, 将命令交给不同的存储引擎,每个存储引擎的特点都不同,有的追求效率,有的追求安全

存储引擎 : 执行命令

image.png

3. 索引

3-1. 数据库有哪些类型的索引, 各有什么优缺点?

索引的几种类型分别是普通索引、唯一索引、聚簇索引、主键索引、全文索引几种

<索引数据结构 : 二叉树、红黑树、Hash表、B-Tree>

  • 普通索引 : 基本的索引类型, 没有唯一性的限制, 允许为Null 值.

// 创建普通索引

alter table table_name add index_name(column);  

//创建组合索引

alter table table_name add index_name(column1 , column2, column3);  

  • 主键索引 : 数据列不允许重复, 不允许为null, 一个表只能有一个主键
  • 唯一索引 : 数据列不允许重复, 不允许为null, 一个表允许多个列创建唯一索引

// 创建唯一索引

alter table table_name add unique (column);

// 创建组合索引

alter table table_name add unique (column1 , column2, column3);

  • 全文索引 :

alter table table_name add fulltext (column);

  • 聚簇索引 : 利用数据库主键来索引. [多条件并列是用and来连接]

3-2. 创建索引的原则

5. 事务单元的特性?

source : https://blog.csdn.net/cherry_vicent/article/details/108791087?utm_medium=distribute.pc_relevant.none-task-blog-baidujs_title-1&spm=1001.2101.3001.4242

5-1 什么是数据库事务?

  • 事务是数据库操作的最小工作单元,是不可再分的操作集合,是作为单个逻辑工作单元执行的一系列操作;也是数据库并发控制的基本单位
  • 其执行结果必须使数据库从一种状态变为另一种状态,
  • 要么都执行、要么都不执行[要么成功, 要么失败,没有第三条路了];

5-2 事务的四重性质ACID :

原子性, 一致性(可见性), 隔离性, 持久性

  1. 原子性(Atomicity):强调事务的不可分割, 同生共死关系

要么全部执行成功,要么全部不执行。只要其中一个指令执行失败,所有的指令都执行失败,数据进行回滚,回到执行指令前的数据状态。

  1. 一致性(Consistency):事务执行前后的数据的完整性保持一致 , 多个事务对同一个数据的读取结果是相同的.

事务的执行使数据从一个状态转换为另一个状态,但是对于整个数据的完整性保持稳定

eg:拿转账来说,假设用户A和用户B两者的钱加起来一共是20000,那么不管A和B之间如何转账,转几次账,事务结束后两个用户的钱相加起来应该还得是20000,这就是事务的一致性。

  1. 隔离性(Isolation):一个事务执行过程中,不受到其他事务的干扰

隔离性是当多个用户并发访问数据库时,比如操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。 即要达到这么一种效果:对于任意两个并发的事务T1和T2,在事务T1看来,T2要么在T1开始之前就已经结束,要么在T1结束之后才开始,这样每个事务都感觉不到有其他事务在并发地执行。

  1. 持久性(Durability):事务一旦结束,数据就持久化到数据库中,不可更改

就像是操作记录, 形成之后就一直持久存在的了

5-3 脏读、幻读、不可重复读?

 第一类丢失更新:撤销一个事务时,把其他事务已提交的更新数据覆盖。

  • 脏读(Dirty) : 脏读是指在一个事务处理过程里读取了另一个未提交的事务中的数据

eg:小明的银行卡余额里有100元。现在他打算用手机点一个外卖饮料,需要付款10元。但是这个时候,他的女朋友看中了一件衣服95元,她正在使用小明的银行卡付款。于是小明在付款的时候,程序后台读取到他的余额只有5块钱了,根本不够10元,所以系统拒绝了他的交易,告诉余额不足。但是小明的女朋友最后因为密码错误,无法进行交易。小明非常郁闷,明明银行卡里还有100元,怎么会余额不足呢?(他女朋友更郁闷。。。)

  • 不可重复读(Non-repeatable) :

  • 幻读(Phantom) : 一个事务的两次查询后的结果不一致

=== 即:一个事务执行两次查询,第二次结果集包含第一次中没有或某些行已经被删除的数据,造成两次结果不一致,只是另一个事务在这两次查询中间插入或删除了数据造成的。幻读是事务非独立执行时发生的一种现象。

区别 与 侧重 :

Tips:

  • 不可重复读和脏读的区别:

脏读是某一事务读取了另一个事务未提交的脏数据,而不可重复读则是读取了前(另)一事务提交了的数据。 

  • 幻读和不可重复读都是读取了另一条已经提交的事务(这点就脏读不同),

所不同的是 : 不可重复读查询的都是同一个数据项,而幻读针对的是一批数据整体(比如数据的个数)。

  • 不可重复读的重点是修改 :

同一事务,两次读取到的数据不一样。

  • 幻读的重点在于新增或者删除

同样的条件 , 第 1 次和第 2 次读出来的记录数不一样

  • 脏读: 强调的是第二个事务读到的不够新。

第二类丢失更新:是不可重复读的特殊情况。如果两个事物都读取同一行,然后两个都进行写操作,并提交,第一个事物所做的改变就会丢失。[第二个后做的操作就把先做的操作给覆盖了].

eg:小明和女朋友一起去逛街。女朋友看中了一支口红,(对,女朋友就是用来表现买买买的)小明大方的掏出了自己的银行卡,告诉女朋友:亲爱的,随便刷,随便买,我坐着等你。然后小明就坐在商城座椅上玩手机,等着女朋友。这个时候,程序员的聊天群里有人推荐了一本书,小明一看,哎呀,真是本好书,还是限量发行呢,我一定更要买到。于是小明赶紧找到购买渠道,进行付款操作。而同时,小明的女朋友也在不亦乐乎的买买买,他们同时进行了一笔交易操作,但是这个时候银行系统出了问题,当他们都付款成功后,却发现,银行只扣了小明的买书钱,却没有扣去女朋友此时交易的钱。哈哈哈,小明真是太开心了!

5-4 隔离级别:

< 5 中涉及链接文档讲述的很清楚. >

隔离级别分为:按照效率由高到低,安全性由低到高的顺序排序 :

  1. 读未提交: 顾名思义,就是一个事务可以读取另一个未提交事务的数据。 写读 , 读写 , 读读 并行

eg:老板要给程序员发工资,程序员的工资是3.6万/月。但是发工资时老板不小心按错了数字,按成3.9万/月,该钱已经打到程序员的户口,但是事务还没有提交,就在这时,程序员去查看自己这个月的工资,发现比往常多了3千元,以为涨工资了非常高兴。但是老板及时发现了不对,马上回滚差点就提交了的事务,将数字改成3.6万再提交。

Analyse:实际程序员这个月的工资还是3.6万,但是程序员看到的是3.9万。他看到的是老板还没提交事务时的数据。这就是脏读。

  1. 读已提交: 就是一个事务要等另一个事务提交后才能读取数据。读写 , 读读 并行

eg:程序员拿着信用卡去享受生活(卡里当然是只有3.6万),当他埋单时(程序员事务开启),收费系统事先检测到他的卡里有3.6万,就在这个时候!!程序员的妻子要把钱全部转出充当家用,并提交。当收费系统准备扣款时,再检测卡里的金额,发现已经没钱了(第二次检测金额当然要等待妻子转出金额事务提交完)。程序员就会很郁闷,明明卡里是有钱的…

Analyse:这就是读提交,若有事务对数据进行更新(UPDATE)操作时,读操作事务要等待这个更新操作事务提交后才能读取数据,可以解决脏读问题。但在这个事例中,出现了一个事务范围内两个相同的查询却返回了不同数据,这就是不可重复读。

  1. 可以重复读: 重复读,就是在开始读取数据(事务开启)时,不再允许修改操作. 读 和 读之间可以并行

eg:程序员拿着信用卡去享受生活(卡里当然是只有3.6万),当他埋单时(事务开启,不允许其他事务的UPDATE修改操作),收费系统事先检测到他的卡里有3.6万。这个时候他的妻子不能转出金额了。接下来收费系统就可以扣款了。

Analyse:重复读可以解决不可重复读问题。写到这里,应该明白的一点就是,不可重复读对应的是修改,即UPDATE操作。但是可能还会有幻读问题。因为幻读问题对应的是插入INSERT操作,而不是UPDATE操作。

  1. 序列化 : Serializable 是最高的事务隔离级别,在该级别下,事务串行化顺序执行,可以避免脏读、不可重复读与幻读。但是这种事务隔离级别效率低下,比较耗数据库性能,一般不使用。所有的事务都是串行的

eg:程序员某一天去消费,花了2千元,然后他的妻子去查看他今天的消费记录(全表扫描FTS,妻子事务开启),看到确实是花了2千元,就在这个时候,程序员花了1万买了一部电脑,即新增INSERT了一条消费记录,并提交。当妻子打印程序员的消费记录清单时(妻子事务提交),发现花了1.2万元,似乎出现了幻觉,这就是幻读。

隔离级别

脏读

不可重复读

幻读

读未提交

读已提交

重复读

序列化

注意: Mysql 默认采用的 重复读 隔离级别 Oracle 默认采用的 读已提交 隔离级别

6. 锁

数据库有并发事务的时候, 可能会产生数据的不一致, 为保证访问的次序,所以来应用了锁.

6-1 隔离级别与锁的关系

在Read Uncommitted级别下,读取数据不需要加共享锁,这样就不会跟被修改的数据上的排他锁冲突

在Read Committed级别下,读操作需要加共享锁,但是在语句执行完以后释放共享锁;

在Repeatable Read级别下,读操作需要加共享锁,但是在事务提交之前并不释放共享锁,也就是必须 等待事务执行完毕以后才释放共享锁。

SERIALIZABLE 是限制性最强的隔离级别,因为该级别锁定整个范围的键,并一直持有锁,直到事务完 成。

6-2 数据库的锁-表锁 与 行锁

MySQL常用引擎有MyISAM和InnoDB,而InnoDB是mysql默认的引擎。MyISAM不支持行锁,而InnoDB支持行锁和表锁。

表锁 : 表锁操作时即是对当前的表进行锁定, 以修改表结构. 表锁时, 表中任何数据都无法操作.不会出现死锁,发生锁冲突几率高,并发低。

MyISAM引擎: 读锁会阻塞写,写锁会阻塞读和写

MyISAM在执行查询语句(select)前,会自动给涉及的所有表加读锁,在执行增删改操作前,会自动给涉及的表加写锁。

MySQL的表级锁有两种模式:表共享读锁 && 表独占写锁

对MyISAM表的读操作,不会阻塞其它进程对同一表的读请求,但会阻塞对同一表的写请求。只有当读锁释放后,才会执行其它进程的写操作。

对MyISAM表的写操作,会阻塞其它进程对同一表的读和写操作,只有当写锁释放后,才会执行其它进程的读写操作。

MyISAM不适合做写为主表的引擎,因为写锁后,其它线程不能做任何操作,大量的更新会使查询很难得到锁,从而造成永远阻塞

行锁 : MySQL的行锁是通过索引加载的,即行锁是加在索引响应的行上的,要是对应的SQL语句没有走索引,则会全表扫描,行锁则无法实现.

  1. insert,delete,update在事务中都会自动默认加上排它锁。
  2. 两个事务不能锁同一个索引。
  3. 行锁必须有索引才能实现,否则会自动锁全表,那么就不是行锁了
  1. 等锁超时 : 超出一定时间, 当前事务结束
  2. 碰撞检测 : 等待中的事务会去主动查询是否使用自己需要的锁, 如果发现对象事务也在等待自己释放锁, 说明陷入了死锁, 终止一个事务
  3. 数据的真正实现方式 : 碰撞检测 辅以等锁超时

6-3. 乐观锁 与 悲观锁

  1. 加锁的态度
  2. 乐观锁就是尽量少加锁, 保证效率
  3. 悲观所就是尽量多加锁, 保证安全性 , 不允许并发.按顺序执行. 语句后面多了个 for updata

7. MVC模型 三层模型

一个完善的系统中应该具有给油壶操作V,控制业务运转的C, 存储数据的M

mvc: 一个完成的项目应该具备 model , view , control

view : 给用户展示数据的视图

control : 对数据进行逻辑运算的模块

model : 按照一定格式存储数据的地方

三层模型 : 是对控制层再次细分精细化控制 .

(七) 题目积累:

1. MySQL数据库中的字段类型varchar和char的主要区别是什么?哪种字段的查找效率要高,为什么?

答 : source: https://www.cda.cn/discuss/post/details/5f295750922c1e31e648d9bb

mySQL 面试题目 : https://blog.csdn.net/cuichen16/article/details/102847846

  • 定长和变长 : char 表示定长,长度固定,varchar表示变长,即长度可变.

当所插入的字符串超出它们的长度时,视情况来处理,如果是严格模式,则会拒绝插入并提示错误信息,如果是宽松模式,则会截取然后插入。如果插入的字符串长度小于定义长度时,则会以不同的方式来处理,如char(10),表示存储的是10个字符,无论你插入的是多少,都是10个,如果少于10个,则用空格填满。而varchar(10),小于10个的话,则插入多少个字符就存多少个。varchar怎么知道所存储字符串的长度呢?实际上,对于varchar 字段来说,需要使用一个(如果字符串长度小于255)或两个字节(长度大于255)来存储字符串的长度。

  • 存储的容量不同对char来说,最多能存放的字符个数 255,和编码无关。varchar最多能存放 65532 个字符

varchar的最大有效长度由最大行大小和使用的字符集确定。整体最大长度是65,532 字节.最大有效长度是 65532 字节,在varchar存字符串的时候,第一个字节是空的,不存任何的数据,然后还需要两个字节来存放字符串的长度。所以有效长度就是 65535 - 1 - 2 = 65532.

char和varchar 后面的长度表示的是字符的个数,而不是字节数。

  • 总结 : 两者相比较,char的效率高,没有碎片,尤其更新比较频繁的时候,方便数据文件指针的操作。但不够灵活,在实际使用时,应根据实际需求来选用合适的数据类型。

计算题目:

若一个表定义为create table t1(c int, c2 char(30), c3 varchar(N)) charset=utf8; 问N的最大值又是多少?

(65535 - 1 - 2 - 4 - 30 * 3 )/3 (字符) ----- 减4的原因是int类型的c占4个字节;减30*3的原因是char(30)占用90个字节,编码是utf8。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值