myibatis执行过程
1读取MyBatis的配置文件。
mybatis-config.xml为MyBatis的全局配置文件,用于配置数据库连接信息。
2加载映射文件。映射文件即SQL映射文件,该文件中配置了操作数据库的SQL语句,需要在MyBatis配置文件mybatis-config.xml中加载。mybatis-config.xml 文件可以加载多个映射文件,每个文件对应数据库中的一张表。
3构造会话工厂。
通过MyBatis的环境配置信息构建会话工厂SqlSessionFactory。
4 创建会话对象。
由会话工厂创建SqlSession对象,该对象中包含了执行SQL语句的所有方法。
5 Executor执行器。
MyBatis底层定义了一个Executor接口来操作数据库,它将根据SqlSession传递的参数动态地生成需要执行的SQL语句,同时负责查询缓存的维护。
6MappedStatement对象。
在Executor接口的执行方法中有一个MappedStatement类型的参数,该参数是对映射信息的封装,用于存储要映射的SQL语句的id、参数等信息。
7 输入参数映射。
输入参数类型可以是Map、List等集合类型,也可以是基本数据类型和POJO类型。输入参数映射过程类似于JDBC对preparedStatement对象设置参数的过程。
8 输出结果映射。
输出结果类型可以是Map、List等集合类型,也可以是基本数据类型和POJO类型。输出结果映射过程类似于JDBC对结果集的解析过程。
mybatis的优缺点有哪些?
1、Mybait的优点:
(1)简单易学,容易上手(相比于Hibernate) 基于SQL编程; (2)JDBC相比,减少了50%以上的代码量,消除了JDBC大量冗余的代码,不需要手动开关连接; (3)很好的与各种数据库兼容(因为MyBatis使用JDBC来连接数据库,所以只要JDBC支持的数据库MyBatis都支持,而JDBC提供了可扩展性,所以只要这个数据库有针对Java的jar包就可以就可以与MyBatis兼容),开发人员不需要考虑数据库的差异性。 (4)提供了很多第三方插件(分页插件 / 逆向工程); (5)能够与Spring很好的集成; (6)MyBatis相当灵活,不会对应用程序或者数据库的现有设计强加任何影响,SQL写在XML里,从程序代码中彻底分离,解除sql与程序代码的耦合,便于统一管理和优化,并可重用。 (7)提供XML标签,支持编写动态SQL语句。 (8)提供映射标签,支持对象与数据库的ORM字段关系映射。 (9)提供对象关系映射标签,支持对象关系组建维护。 2、MyBatis框架的缺点:
(1)SQL语句的编写工作量较大,尤其是字段多、关联表多时,更是如此,对开发人员编写SQL语句的功底有一定要求。 (2)SQL语句依赖于数据库,导致数据库移植性差,不能随意更换数据库。
mybatis和hibernate有什么区别?
Hibernate的优点:
1、hibernate是全自动,hibernate完全可以通过对象关系模型实现对数据库的操作,拥有完整的JavaBean对象与数据库的映射结构来自动生成sql。
2、功能强大,数据库无关性好,O/R映射能力强,需要写的代码很少,开发速度很快。
3、有更好的二级缓存机制,可以使用第三方缓存。
4、数据库移植性良好。
5、hibernate拥有完整的日志系统,hibernate日志系统非常健全,涉及广泛,包括sql记录、关系异常、优化警告、缓存提示、脏数据警告等
Hibernate的缺点:
1、学习门槛高,精通门槛更高,程序员如何设计O/R映射,在性能和对象模型之间如何取得平衡,以及怎样用好Hibernate方面需要的经验和能力都很强才行
2、hibernate的sql很多都是自动生成的,无法直接维护sql;虽然有hql查询,但功能还是不及sql强大,见到报表等变态需求时,hql查询要虚,也就是说hql查询是有局限的;hibernate虽然也支持原生sql查询,但开发模式上却与orm不同,需要转换思维,因此使用上有些不方便。总之写sql的灵活度上hibernate不及mybatis。
Mybatis的优点:
1、易于上手和掌握,提供了数据库查询的自动对象绑定功能,而且延续了很好的SQL使用经验,对于没有那么高的对象模型要求的项目来说,相当完美。
2、sql写在xml里,便于统一管理和优化, 解除sql与程序代码的耦合。
3、提供映射标签,支持对象与数据库的orm字段关系映射
4、 提供对象关系映射标签,支持对象关系组建维护
5、提供xml标签,支持编写动态sql。
6、速度相对于Hibernate的速度较快
Mybatis的缺点:
1、关联表多时,字段多的时候,sql工作量很大。
2、sql依赖于数据库,导致数据库移植性差。
3、由于xml里标签id必须唯一,导致DAO中方法不支持方法重载。
4、对象关系映射标签和字段映射标签仅仅是对映射关系的描述,具体实现仍然依赖于sql。
5、DAO层过于简单,对象组装的工作量较大。
6、不支持级联更新、级联删除。
7、Mybatis的日志除了基本记录功能外,其它功能薄弱很多。
8、编写动态sql时,不方便调试,尤其逻辑复杂时。
9、提供的写动态sql的xml标签功能简单,编写动态sql仍然受限,且可读性低。
mybatis中#{}和${}的区别是什么?
a、#{}是预编译处理,${}是字符串替换。 b、Mybatis 在处理#{}时,会将 sql 中的#{}替换为?号,调用 PreparedStatement 的 set 方法来赋值; c、Mybatis 在处理${}时,就是把${}替换成变量的值。 d、使用#{}可以有效的防止 SQL 注入,提高系统安全性
简述一下mybatis插件运行原理及开发流程?(插件四大天王)
mybatis只支持针对ParameterHandler、ResultSetHandler、StatementHandler、Executor这四种接口的插件,mybatis使用jdk的动态代理,为需要拦截的接口生成代理对象以实现接口方法拦截功能,每当执行这四种接口对象的方法时,就会进入拦截方法,具体就是InvocationHandler的invoke方法,拦截那些你指定需要拦截的方法。
编写插件:实现Mybatis的Interceptor接口并复写intercept方法啊,然后给插件编写注解,指定要拦截哪一个接口的哪些方法,在配置文件中配置编写的插件即可。
@Intercepts({@Signature(type = StatementHandler.class,method = "parameterize",args = Statement.class)})
插件开发流程:
-
创建自定义拦截器类,实现
org.apache.ibatis.plugin.Interceptor
接口,并重写其中的intercept()
和plugin()
方法。intercept()
方法用于定义自定义逻辑,plugin()
方法用于生成代理对象。 -
在拦截器类上使用
@Intercepts
注解,寻找需要拦截的方法。 -
在MyBatis的配置文件(通常是
mybatis-config.xml
)中,配置自定义拦截器。 -
创建插件类,继承
org.apache.ibatis.plugin.PluginAdapter
类,并实现其中的setProperties()
方法。 -
在插件类上使用
@Signature
注解,指定要拦截的方法签名。 -
编写一个类,实现
org.apache.ibatis.session.Configuration
接口,重写其中的addInterceptor()
方法,在方法中添加自定义插件。 -
在MyBatis的配置文件中,使用
<configuration>
标签的<settings>
子标签,并指定使用自定义的配置类。 -
调用MyBatis的API进行数据库操作时,插件将被自动执行。
mybatis的mapper没有实现类
在 MyBatis 中,Mapper 接口可以不需要对应的实现类,因为 MyBatis 会自动为 Mapper 接口生成代理对象,并实现接口中定义的方法。这种方式称为 Mapper 接口的动态代理。
当 Mapper 接口没有对应的实现类时,MyBatis 在运行时会动态生成一个代理对象,该代理对象会拦截接口方法的调用,并根据接口方法的定义将对应的 SQL 语句进行执行。这样就实现了 Mapper 接口与 SQL 语句的绑定。
在 Mapper 接口中,通常会使用注解或 XML 文件来定义 SQL 语句。例如,使用注解方式定义 SQL 语句的 Mapper 接口示例:
```java
public interface UserMapper {
@Select("SELECT * FROM users WHERE id = #{id}")
User selectUserById(Long id);
}
```
而使用 XML 文件定义 SQL 语句的 Mapper 接口示例:
```java
public interface UserMapper {
User selectUserById(Long id);
}
```
```xml
<!-- UserMapper.xml -->
<mapper namespace="com.example.UserMapper">
<select id="selectUserById" resultType="com.example.User">
SELECT * FROM users WHERE id = #{id}
</select>
</mapper>
```
当应用程序调用 Mapper 接口中的方法时,MyBatis 会根据方法的定义(注解或 XML 文件中的配置)来执行对应的 SQL 语句,将查询结果映射为 Java 对象并返回给调用方。
总结来说,即使 Mapper 接口没有对应的实现类,MyBatis 也能通过动态代理的方式实现接口与 SQL 语句的绑定,使开发人员可以方便地以接口的方式来操作数据库,提高了开发效率和代码的可维护性。
MyBatis 与 Spring 框架整合
在将 MyBatis 与 Spring 框架整合时,需要配置 Spring 的 ApplicationContext,并将 MyBatis 的 SqlSessionFactory 注入到 Spring 的容器中,以便让 Spring 管理 MyBatis 的 SqlSessionFactory,并能够进行事务管理、异常处理等操作。下面是将 MyBatis 与 Spring 整合的一般步骤:
1. **配置 MyBatis 的 SqlSessionFactoryBean:** 在 Spring 的配置文件中配置 MyBatis 的 SqlSessionFactoryBean,用于创建 MyBatis 的 SqlSessionFactory 实例,并配置数据源、Mapper 接口扫描等信息。
```xml
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="mapperLocations" value="classpath:com/example/mappers/*.xml" />
</bean>
```
2. **配置 MyBatis 的 MapperScannerConfigurer:** 使用 MapperScannerConfigurer 配置自动扫描 Mapper 接口,并将其注册为 Spring 的 Bean。
```xml
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.example.mappers" />
</bean>
```
3. **配置事务管理器和动态代理**:配置事务管理器(如 DataSourceTransactionManager)以及 MyBatis 的 Mapper Bean 动态代理。
```xml
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<bean class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="sqlSessionFactory" ref="sqlSessionFactory" />
<property name="mapperInterface" value="com.example.UserMapper" />
</bean>
```
4. **在业务逻辑中使用 Mapper 接口:** 在业务逻辑的 Spring Bean 中注入 Mapper 接口,然后就可以通过 Mapper 接口来执行 SQL 操作。
```java
@Autowired
private UserMapper userMapper;
```
5. **配置数据源和其他必要的 Bean:** 还需要配置数据源、事务管理器等其它必要的 Bean,用于 MyBatis 与数据库的交互和事务管理。
通过以上步骤,将 MyBatis 与 Spring 整合后,就可以在 Spring 的应用程序中方便地使用 MyBatis 来操作数据库,实现数据访问层的功能。整合的过程主要是通过配置 Spring 的配置文件来管理 MyBatis 的相关 Bean,并利用 Spring 的依赖注入来获取需要的组件,从而实现 MyBatis 与 Spring 的无缝整合。
MyBatis 缓存
MyBatis 是一个流行的持久层框架,用于对数据库进行操作。MyBatis 提供了一级缓存和二级缓存来优化数据库访问,提高系统性能。
一级缓存: 一级缓存是 MyBatis 中默认开启的缓存,它指的是 SqlSession 级别的缓存。当查询完数据后,数据将会保存在 SqlSession 的缓存中,后续对同一条数据的查询将不会再访问数据库,而是直接从缓存中获取。一级缓存的作用域是当前 SqlSession,在同一个 SqlSession 中进行的多次查询可以共享一级缓存。
二级缓存: 二级缓存是 MyBatis 的全局缓存,它作用于 SqlSessionFactory 级别,可以被同一个数据库的不同 SqlSession 所共享。这意味着当不同的 SqlSession 执行相同的 SQL 查询时,可以从二级缓存中获取数据,而不必再次访问数据库。需要注意的是,二级缓存是跨 SqlSession 的,如果在一个 SqlSession 中更新了数据,会清空二级缓存中与该数据相关的数据,保证缓存的一致性。
启用二级缓存的步骤包括:
-
在 MyBatis 的配置文件中开启二级缓存:
<settings> <setting name="cacheEnabled" value="true"/> </settings>
-
针对需要使用二级缓存的映射文件,添加
<cache/>
标签:<mapper namespace="com.example.UserMapper"> <cache/> <!-- 其他映射配置 --> </mapper>
需要注意的是,当配置了二级缓存后,需要在需要共享缓存的实体类中通过 <cache/>
标签明确指定是否开启二级缓存,因为默认情况下是关闭的。并且需要确保相关的实体类实现了 Serializable 接口。因为二级缓存默认使用 Java 的序列化机制进行对象的序列化和反序列化。
总的来说,一级缓存和二级缓存在 MyBatis 中是用来提升查询性能的重要机制。一级缓存作用于 SqlSession 级别,适用于短生命周期的对象,而二级缓存作用于 SqlSessionFactory 级别,适用于长生命周期的对象,可以在不同 SqlSession 之间共享缓存,但需要注意数据的一致性。
Mybatis是如何进行分页的?分页插件的原理是什么?
Mybatis使用RowBounds对象进行分页,它是针对ResultSet结果集执行的内存分页,而非物理分页,可以在sql内直接书写带有物理分页的参数来完成物理分页功能,也可以使用分页插件来完成物理分页。
分页插件的基本原理是使用Mybatis提供的插件接口,实现自定义插件,在插件的拦截方法内拦截待执行的sql,然后重写sql,根据dialect方言,添加对应的物理分页语句和物理分页参数。
举例:select * from student,拦截sql后重写为:select t.* from (select * from student) t limit 0, 10
MyBatis编程步骤是什么样的?
1、 创建SqlSessionFactory
2、 通过SqlSessionFactory创建SqlSession
3、 通过sqlsession执行数据库操作
4、 调用session.commit()提交事务
5、 调用session.close()关闭会话
Mybatis都有哪些Executor执行器?它们之间的区别是什么?
Mybatis有三种基本的Executor执行器,SimpleExecutor、ReuseExecutor、BatchExecutor。
SimpleExecutor:每执行一次update或select,就开启一个Statement对象,用完立刻关闭Statement对象。
ReuseExecutor:执行update或select,以sql作为key查找Statement对象,存在就使用,不存在就创建,用完后,不关闭Statement对象,而是放置于Map<String, Statement>内,供下一次使用。简言之,就是重复使用Statement对象。
BatchExecutor:执行update(没有select,JDBC批处理不支持select),将所有sql都添加到批处理中(addBatch()),等待统一执行(executeBatch()),它缓存了多个Statement对象,每个Statement对象都是addBatch()完毕后,等待逐一执行executeBatch()批处理。与JDBC批处理相同。
作用范围:Executor的这些特点,都严格限制在SqlSession生命周期范围内。
模糊查询like语句该怎么写
(1)’%${question}%’ 可能引起SQL注入,不推荐
(2)"%"#{question}"%" 注意:因为#{…}解析成sql语句时候,会在变量外侧自动加单引号’ ',所以这里 % 需要使用双引号" ",不能使用单引号 ’ ',不然会查不到任何结果。
(3)CONCAT(’%’,#{question},’%’) 使用CONCAT()函数,推荐
(4)使用bind标签
<select id="listUserLikeUsername" resultType="com.jourwon.pojo.User">
<bind name="pattern" value="'%' + username + '%'" />
select id,sex,age,username,password from person where username LIKE #{pattern}
</select>
在mapper中如何传递多个参数
方法1:顺序传参法
public User selectUser(String name, int deptId);
<select id="selectUser" resultMap="UserResultMap">
select * from user
where user_name = #{0} and dept_id = #{1}
</select>
#{}里面的数字代表传入参数的顺序。
这种方法不建议使用,sql层表达不直观,且一旦顺序调整容易出错。
方法2:@Param注解传参法
public User selectUser(@Param("userName") String name, int @Param("deptId") deptId);
<select id="selectUser" resultMap="UserResultMap">
select * from user
where user_name = #{userName} and dept_id = #{deptId}
</select>
#{}里面的名称对应的是注解@Param括号里面修饰的名称。
这种方法在参数不多的情况还是比较直观的,推荐使用。
方法3:Map传参法
public User selectUser(Map<String, Object> params);
<select id="selectUser" parameterType="java.util.Map" resultMap="UserResultMap">
select * from user
where user_name = #{userName} and dept_id = #{deptId}
</select>
#{}里面的名称对应的是Map里面的key名称。
这种方法适合传递多个参数,且参数易变能灵活传递的情况。
方法4:Java Bean传参法
public User selectUser(User user);
<select id="selectUser" parameterType="com.jourwon.pojo.User" resultMap="UserResultMap">
select * from user
where user_name = #{userName} and dept_id = #{deptId}
</select>
#{}里面的名称对应的是User类里面的成员属性。
这种方法直观,需要建一个实体类,扩展不容易,需要加属性,但代码可读性强,业务逻辑处理方便,推荐使用。
Mybatis如何执行批量操作
使用foreach标签
foreach的主要用在构建in条件中,它可以在SQL语句中进行迭代一个集合。foreach标签的属性主要有item,index,collection,open,separator,close。
item 表示集合中每一个元素进行迭代时的别名,随便起的变量名;
index 指定一个名字,用于表示在迭代过程中,每次迭代到的位置,不常用;
open 表示该语句以什么开始,常用“(”;
separator表示在每次进行迭代之间以什么符号作为分隔符,常用“,”;
close 表示以什么结束,常用“)”。
在使用foreach的时候最关键的也是最容易出错的就是collection属性,该属性是必须指定的,但是在不同情况下,该属性的值是不一样的,主要有一下3种情况:
如果传入的是单参数且参数类型是一个List的时候,collection属性值为list
如果传入的是单参数且参数类型是一个array数组的时候,collection的属性值为array
如果传入的参数是多个的时候,我们就需要把它们封装成一个Map了,当然单参数也可以封装成map,实际上如果你在传入参数的时候,在MyBatis里面也是会把它封装成一个Map的,
map的key就是参数名,所以这个时候collection属性值就是传入的List或array对象在自己封装的map里面的key
具体用法如下:
<!-- 批量保存(foreach插入多条数据两种方法)
int addEmpsBatch(@Param("emps") List<Employee> emps); -->
<!-- MySQL下批量保存,可以foreach遍历 mysql支持values(),(),()语法 --> //推荐使用
<insert id="addEmpsBatch">
INSERT INTO emp(ename,gender,email,did)
VALUES
<foreach collection="emps" item="emp" separator=",">
(#{emp.eName},#{emp.gender},#{emp.email},#{emp.dept.id})
</foreach>
</insert>
<!-- 这种方式需要数据库连接属性allowMutiQueries=true的支持
如jdbc.url=jdbc:mysql://localhost:3306/mybatis?allowMultiQueries=true -->
<insert id="addEmpsBatch">
<foreach collection="emps" item="emp" separator=";">
INSERT INTO emp(ename,gender,email,did)
VALUES(#{emp.eName},#{emp.gender},#{emp.email},#{emp.dept.id})
</foreach>
</insert>
在 MyBatis 中进行一对一和一对多的关联查询
在 MyBatis 中实现一对一和一对多的关联查询,通常有以下几种方式:
### 一对一关联查询的实现方式:
1. **嵌套查询(Nested Queries):** 在父查询中通过嵌套查询子查询来获取关联对象的信息。
2. **嵌套结果(Nested Results):** 在 SQL 查询语句中通过使用嵌套 Select 语句实现一对一关联查询。
3. **嵌套映射(Nested Mapping):** 通过在 ResultMap 中定义嵌套的 ResultMap 来实现一对一关联查询。
下面是一种常见的嵌套查询方式的操作步骤:
1. 在 SQL Mapper XML 文件中配置:
<!-- 查询订单信息,并关联查询用户信息 -->
<select id="selectOrderWithUser" resultMap="orderUserResultMap">
SELECT o.*, u.id as user_id, u.username as user_username, u.email as user_email
FROM orders o
INNER JOIN users u ON o.user_id = u.id
WHERE o.id = #{orderId}
</select>
<!-- 定义 ResultMap -->
<resultMap id="orderUserResultMap" type="Order">
<id property="id" column="id" />
<association property="user" resultMap="userResultMap" />
</resultMap>
<resultMap id="userResultMap" type="User">
<id property="id" column="user_id"/>
<result property="username" column="user_username" />
<result property="email" column="user_email" />
</resultMap>
2. 在 Mapper 接口中定义方法:
Order selectOrderWithUser(Long orderId);
3. 在 Service 层调用 Mapper 中定义的方法:
Order order = orderMapper.selectOrderWithUser(orderId);
一对多关联查询的实现方式:
1. **嵌套查询:** 使用多条 SQL 查询语句来获取一对多关联的数据。
2. **嵌套结果:** 在 SQL 查询语句中使用 Join 等方式,将一对多关联的数据合并在查询结果中。
3. **嵌套映射:** 通过在 ResultMap 中定义 Collection 来实现一对多关联查询。
下面是一种常见的嵌套查询方式的操作步骤:
1. 在 SQL Mapper XML 文件中配置:
<!-- 查询部门信息,并关联查询所有员工信息 -->
<select id="selectDepartmentWithEmployees" resultMap="departmentEmployeesResultMap">
SELECT d.id as department_id, d.name as department_name,
e.id as employee_id, e.name as employee_name, e.email as employee_email
FROM departments d
LEFT JOIN employees e ON d.id = e.department_id
WHERE d.id = #{departmentId}
</select>
<!-- 定义 ResultMap -->
<resultMap id="departmentEmployeesResultMap" type="Department">
<id property="id" column="department_id" />
<result property="name" column="department_name" />
<collection property="employees" ofType="Employee">
<id property="id" column="employee_id"/>
<result property="name" column="employee_name"/>
<result property="email" column="employee_email"/>
</collection>
</resultMap>
2. 在 Mapper 接口中定义方法:
Department selectDepartmentWithEmployees(Long departmentId);
3. 在 Service 层调用 Mapper 中定义的方法:
Department department = departmentMapper.selectDepartmentWithEmployees(departmentId);
通过以上方式,可以实现在 MyBatis 中进行一对一和一对多的关联查询,具体的方式可以根据实际业务需求和数据结构选择最适合的方式进行操作。
Select * 的缺点
1. 不需要的列会增加数据传输时间和网络开销
用“SELECT * ”数据库需要解析更多的对象、字段、权限、属性等相关内容,在 SQL 语句复杂,硬解析较多的情况下,会对数据库造成沉重的负担。
增大网络开销;* 有时会误带上如log、IconMD5之类的无用且大文本字段,数据传输size会几何增涨。如果DB和应用程序不在同一台机器,这种开销非常明显
即使 mysql 服务器和客户端是在同一台机器上,使用的协议还是 tcp,通信也是需要额外的时间。
2. 对于无用的大字段,如 varchar、blob、text,会增加 io 操作
准确来说,长度超过 728 字节的时候,会先把超出的数据序列化到另外一个地方,因此读取这条记录会增加一次 io 操作。(MySQL InnoDB)
3. 失去MySQL优化器“覆盖索引”策略优化的可能性
SELECT * 杜绝了覆盖索引的可能性,而基于MySQL优化器的“覆盖索引”策略又是速度极快,效率极高,业界极为推荐的查询优化方式
select count(*)和select count(1)的区别
1、 一般情况下,Select Count (*)和Select Count(1)两着返回结果是一样的
2、 假如表沒有主键(Primary key), 那么count(1)比count(*)快,
3、 如果有主键的話,那主键作为count的条件时候count(主键)最快
4、 如果你的表只有一个字段的话那count(*)就是最快的
5、count(*) 跟 count(1) 的结果一样,都包括对NULL的统计,而count(column) 是不包括NULL的统计
左连接 left Join 右连接,内连接
在 MySQL 中,左连接(LEFT JOIN)、右连接(RIGHT JOIN)和内连接(INNER JOIN)是 SQL 中常用的连接类型,它们的定义与区别如下:
### 1. 左连接(LEFT JOIN):
- 左连接是从左表(左边)返回所有匹配左表的行,并返回右表中与左表匹配的行,如果右表中没有匹配的行,则显示为 NULL 值。
- 语法:`SELECT * FROM 左表 LEFT JOIN 右表 ON 左表.列 = 右表.列;`
- 左表中的所有行都会显示在结果集中,右表中没有匹配的行会被填充为 NULL 值。
### 2. 右连接(RIGHT JOIN):
- 右连接是从右表(右边)返回所有匹配右表的行,并返回左表中与右表匹配的行,如果左表中没有匹配的行,则显示为 NULL 值。
- 语法:`SELECT * FROM 左表 RIGHT JOIN 右表 ON 左表.列 = 右表.列;`
- 右表中的所有行都会显示在结果集中,左表中没有匹配的行会被填充为 NULL 值。
### 3. 内连接(INNER JOIN):
- 内连接是返回匹配两个表中行的行,并丢弃不匹配的行,即两个表中连接条件不符合的行会被过滤掉。
- 语法:`SELECT * FROM 表1 INNER JOIN 表2 ON 表1.列 = 表2.列;`
- 只返回两个表中连接条件符合的行,不匹配的行不会显示在结果集中。
### 区别总结:
- 左连接返回左表中所有行以及与左表匹配的右表行,右连接返回右表中所有行以及与右表匹配的左表行,内连接只返回两个表中连接条件符合的行。
- 三种连接方式都可以通过 `ON` 子句中指定连接条件,根据实际需求选择合适的连接方式来获取所需数据。
在实际应用中,根据具体业务需求和数据关系选择合适的连接方式,以获取正确的结果集。
数据库的五大范式
具体内容如下:
第一范式:无重复列
即在一行中的每一列仅有唯一的值并且具有原子性
第二范式:任意一个字段都只依赖表中的同一字段,或者说,属性完全依赖于主键
即非主键列的活动必须完全依赖整个主键,主键必须有唯一性的元素
第三范式:属性不能传递依赖于主属性,或者说,属性不依赖其他非主属性
即非主键列互不依赖
第四范式:禁止主键列和非主键列一对多关系不受约束
第五范式:将表分割成尽可能小的块,为了排序在表中所有的冗余
MySql的三种日志及作用
这三种日志在 MySQL 数据库中扮演着关键的角色,它们分别支撑着数据库的事务持久性、数据一致性和可靠性。
-
Redo Log(重做日志):
-
作用:记录事务对数据页所做的修改,在事务提交前就将相关的 Redo log 记录持久化到磁盘。这样,在事务提交后即使数据页还未写入磁盘,数据库也能通过 Redo log 进行数据的恢复,保证了事务的持久性。
-
数据一致性和可靠性:重做日志的存在保证了即使在数据库崩溃或断电的情况下,提交的事务对数据的修改也不会丢失,从而确保了数据的一致性和可靠性。
-
-
Undo Log(回滚日志):
-
作用:记录事务执行过程中对数据的旧值,支持事务的回滚和读取操作的多版本并发控制。当事务执行中需要回滚或读取之前的版本时,可以通过 Undo log 进行数据的恢复操作。
-
数据一致性和可靠性:Undo log 保证了事务的原子性和一致性,即使事务执行过程中出现错误或需要回滚,也能确保数据的一致性和可靠性。
-
-
Binlog(二进制日志):
-
作用:记录了所有的数据库修改操作,包括对表的插入、更新、删除等操作,以及对数据库结构的变更。Binlog 主要用于数据库的主从复制、数据恢复、数据迁移等场景。
-
数据一致性和可靠性:通过 Binlog,数据库可以在分布式系统中实现主从复制,确保不同节点之间的数据一致性和可靠性;同时,通过恢复 Binlog 也能实现对数据库的数据恢复和数据迁移,保证了数据的可靠性和一致性。
-
综合来看,通过 Redo log 的持久化、Undo log 的回滚支持和 Binlog 的记录和传播,MySQL 服务器保证了数据的一致性和可靠性。这些日志机制都是数据库引擎和服务器核心的组成部分,确保了数据库的 ACID 特性,即原子性、一致性、隔离性和持久性。
redo log和binlog区别
-
redo log是属于innoDB层面,binlog属于MySQL Server层面的,这样在数据库用别的存储引擎时可以达到一致性的要求。
-
redo log是物理日志,记录该数据页更新的内容;binlog是逻辑日志,记录的是这个更新语句的原始逻辑
-
redo log是循环写,日志空间大小固定;binlog是追加写,是指一份写到一定大小的时候会更换下一个文件,不会覆盖。
-
binlog可以作为恢复数据使用,主从复制搭建,redo log作为异常宕机或者介质故障后的数据恢复使用。
索引的基本原理
1、为什么要有索引? 一般的应用系统,读写比例在10:1左右,而且插入操作和一般的更新操作很少出现性能问题,在生产环境中,我们遇到最多的,也是最容易出问题的,还是一些复杂的查询操作,因此对查询语句的优化显然是重中之重。说起加速查询,就不得不提到索引了。 2、什么是索引? 索引在MySQL中也叫是一种“键”,是存储引擎用于快速找到记录的一种数据结构。索引对于良好的性能 非常关键,尤其是当表中的数据量越来越大时,索引对于性能的影响愈发重要。 索引优化应该是对查询性能优化最有效的手段了。索引能够轻易将查询性能提高好几个数量级。 索引相当于字典的音序表,如果要查某个字,如果不使用音序表,则需要从几百页中逐页去查。
3、索引的原理
索引的目的在于提高查询效率,与我们查阅图书所用的目录是一个道理:先定位到章,然后定位到该章下的一个小节,然后找到页数。相似的例子还有:查字典,查火车车次,飞机航班等
本质都是:通过不断地缩小想要获取数据的范围来筛选出最终想要的结果,同时把随机的事件变成顺序的事件,也就是说,有了这种索引机制,我们可以总是用同一种查找方式来锁定数据。
数据库也是一样,但显然要复杂的多,因为不仅面临着等值查询,还有范围查询(>、<、between、in)、模糊查询(like)、并集查询(or)等等。数据库应该选择怎么样的方式来应对所有的问题呢?我们回想字典的例子,能不能把数据分成段,然后分段查询呢?最简单的如果1000条数据,1到100分成第一段,101到200分成第二段,201到300分成第三段…这样查第250条数据,只要找第三段就可以了,一下子去除了90%的无效数据。但如果是1千万的记录呢,分成几段比较好?按照搜索树的模型,其平均复杂度是lgN,具有不错的查询性能。但这里我们忽略了一个关键的问题,复杂度模型是基于每次相同的操作成本来考虑的。而数据库实现比较复杂,一方面数据是保存在磁盘上的,另外一方面为了提高性能,每次又可以把部分数据读入内存来计算,因为我们知道访问磁盘的成本大概是访问内存的十万倍左右,所以简单的搜索树难以满足复杂的应用场景。
4、索引的数据结构
MySQL主要用到两种结构:B+ Tree索引和Hash索引 Inodb存储引擎 默认是 B+Tree索引 Memory 存储引擎 默认 Hash索引; MySQL中,只有Memory(Memory表只存在内存中,断电会消失,适用于临时表)存储引擎显示支持Hash索引,是Memory表的默认索引类型,尽管Memory表也可以使用B+Tree索引。Hash索引把数据以hash形式组织起来,因此当查找某一条记录的时候,速度非常快。但是因为hash结构,每个键只对应一个值,而且是散列的方式分布。所以它并不支持范围查找和排序等功能。 B+Tree是mysql使用最频繁的一个索引数据结构,是InnoDB和MyISAM存储引擎模式的索引类型。相对Hash索引,B+Tree在查找单条记录的速度比不上Hash索引,但是因为更适合排序等操作,所以它更受欢迎。毕竟不可能只对数据库进行单条记录的操作。 对比: hash类型的索引:查询单条快,范围查询慢 btree类型的索引:b+树,层数越多,数据量指数级增长(我们就用它,因为innodb默认支持它)
B+ 树索引结构
在 MySQL 中,B+ 树(B+ Tree)是一种常用的索引结构,用于加速数据库表的数据查找操作。下面详细讲解 MySQL 中的 B+ 树索引结构以及选择它的原因:
### 1. B+ 树索引结构:
- **结构特点:**
- B+ 树是一种平衡多路搜索树,具有树形结构,每个节点可以包含多个子节点。
- 每个非叶子节点存储索引字段和指向子节点的指针,叶子节点存储索引字段和指向数据行的指针。
- 叶子节点按顺序连接形成一个有序链表,方便范围查询操作。
- **查找过程:**
- 从根节点开始,根据节点中的索引值进行比较,沿着合适的路径向下查找,直到找到叶子节点。
- 在叶子节点中进行精确查找或范围查找,找到对应的数据行。
### 2. 为什么选择 B+ 树索引结构:
- **高效的检索性能:** B+ 树是一种高效的索引结构,其查询的时间复杂度为 O(logN),可以快速定位到指定数据行,适用于大数据量数据库表的索引。
- **有序性和范围查询:** B+ 树的叶子节点形成有序链表,适合范围查询操作,能够快速定位到指定范围的数据行。
- **支持高效的插入和删除操作:** B+ 树的平衡性和节点分裂合并策略可以保持树的平衡,保证高效的插入和删除操作。
- **适合磁盘存储:** B+ 树的节点通常比较大,可以减少磁盘 I/O 次数,适合磁盘存储的数据库系统。
- **支持索引顺序遍历:** B+ 树的叶子节点形成有序链表,可以支持索引的顺序遍历,提高查询性能。
- **适合范围查询和排序:** B+ 树的有序性和范围查询优势,使其适合在 MySQL 数据库中用于支持范围查询、排序操作。
综上所述,MySQL 选择 B+ 树作为索引结构的原因是因为它能够提供高效的检索性能、有序性以支持范围查询、适合磁盘存储、支持高效的插入和删除操作等特点,能够满足数据库系统对索引结构的需求,提高数据库表的查询性能和效率。
索引的最左匹配原则
最左匹配原则是指在复合索引中,MySQL会按照索引的最左列开始匹配查询条件,然后逐渐向右匹配。只有当查询条件中连续的列满足了索引的最左前缀时,索引才能被充分利用。以下是一些例子来说明最左匹配原则:
-
假设有一个复合索引
(column1, column2, column3)
。-
有效的查询条件示例:
WHERE column1 = 'A'
-
无效的查询条件示例:
WHERE column2 = 'B'
,因为没有匹配到索引的最左列column1
-
-
假设有一个复合索引
(column1, column2)
。-
有效的查询条件示例:
WHERE column1 = 'A'
-
有效的查询条件示例:
WHERE column1 = 'A' AND column2 = 'B'
-
无效的查询条件示例:
WHERE column2 = 'B'
,因为没有匹配到索引的最左列column1
-
-
假设有一个复合索引
(column1, column2)
。-
有效的查询条件示例:
WHERE column1 > 'A'
-
无效的查询条件示例:
WHERE column2 > 'B'
,因为没有匹配到索引的最左列column1
-
-
最左前缀匹配原则,非常重要的原则,mysql会一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配,比如a = 1 and b = 2 and c > 3 and d = 4 如果建立(a,b,c,d)顺序的索引,d是用不到索引的,如果建立(a,b,d,c)的索引则都可以用到,a,b,d的顺序可以任意调整。 =和in可以乱序,比如a = 1 and b = 2 and c = 3 建立(a,b,c)索引可以任意顺序,mysql的查询优化器会帮你优化成索引可以识别的形式
总结起来,最左匹配原则适用于以下情况:
-
查询条件从索引的最左列开始,连续向右匹配索引列。
-
查询条件中的列满足了索引的最左前缀。
如果查询条件不符合以上两个条件,那么最左匹配原则将失效,MySQL将无法充分利用复合索引。
在设计和优化索引时,了解最左匹配原则是很有帮助的。它可以帮助你合理地选择索引列的顺序,以提高查询性能和索引的利用率。
mysql聚簇和非聚簇索引的区别是什么?
mysql的索引类型跟存储引擎是相关的,innodb存储引擎数据文件跟索引文件全部放在ibd文件中,而myisam的数据文件放在myd文件中,索引放在myi文件中,其实区分聚簇索引和非聚簇索引非常简单,只要判断数据跟索引是否存储在一起就可以了。
innodb存储引擎在进行数据插入的时候,数据必须要跟索引放在一起,如果有主键就使用主键,没有主键就使用唯一键,没有唯一键就使用6字节的rowid,因此跟数据绑定在一起的就是聚簇索引,而为了避免数据冗余存储,其他的索引的叶子节点中存储的都是聚簇索引的key值,因此innodb中既有聚簇索引也有非聚簇索引,而myisam中只有非聚簇索引。
mysql索引结构有哪些,各自的优劣是什么?
索引的数据结构和具体存储引擎的实现有关,mysql中使用较多的索引有hash索引,B+树索引,innodb的索引实现为B+树,memory存储引擎为hash索引。
B+树是一个平衡的多叉树,从根节点到每个叶子节点的高度差值不超过1,而且同层级的二节点间有指针相关连接,在B+树上的常规检索,从根节点到叶子节点的搜索效率基本相当,不会出现大幅波动,而且基于索引的顺序扫描时,也可以利用双向指针快速左右移动,效率非常高。因为,B+树索引被广泛应用于数据库、文件系统等场景。
哈希索引就是采用一定的哈希算法,把键值换算成新的哈希值,检索时不需要类似B+树那样从根节点到叶子节点逐级查找,只需一次哈希算法即可立刻定位到相应的位置,速度非常快。
如果是等值查询,那么哈希索引明显有绝对优势,因为只需要经过一次算法即可找到相应的键值,前提是键值都是唯一的。如果键值不是唯一的,就需要先找到该键所在位置,然后再根据链表往后扫描,知道找到对应的数据
如果是范围查询检索,这时候哈徐索引就毫无用武之地了,因为原先是有序的键值,经过哈希算法后,有可能变成不连续的了,就没办法再利用索引完成范围查询检索
哈希所有也没办法利用索引完成排序,以及like这样的部分模糊查询
哈希索引也不支持多列联合索引的最左匹配规则
B+树索引的关键字检索效率比较平均,不像B树那样波动大,在有大量重复键值情况下,哈希索引的效率也是极低的,因此存在哈希碰撞问题。
索引的设计原则有哪些?
在数据库中,创建索引是提高查询效率和性能的重要手段之一。以下是一些创建索引的原则:
-
选择合适的列:选择那些经常用于查询和连接的列来创建索引。通常包括经常出现在WHERE、JOIN、ORDER BY和GROUP BY子句中的列。
-
避免过度索引:不要为每个列都创建索引,因为索引的维护和存储会带来额外的开销。过多的索引不仅会占用磁盘空间,还可能导致索引失效、降低写操作的性能等问题。
-
考虑列的选择性:选择具有较高选择性(不重复值较多)的列来创建索引,这样可以减少索引的大小和搜索的区间。
-
考虑查询的频率和重要性:根据查询的频率和重要性来选择创建索引的列。如果某个查询非常频繁且对性能要求较高,那么为该查询常用的列创建索引是很有必要的。
-
综合考虑查询和更新的平衡:索引会提高查询的速度,但会降低更新操作的速度。因此,在创建索引时需要综合考虑查询和更新的平衡,选择适当的索引策略。
-
考虑数据量和存储空间:索引会占用额外的存储空间,因此在创建索引时需要考虑数据量的大小。对于较大的表和数据量庞大的表,需要谨慎选择创建索引的列,以避免过度的存储开销。
-
定期维护和更新索引:索引的性能和效果是需要定期维护和更新的。当表的数据发生变化时,如插入、更新或删除操作,需要及时更新索引以保持索引的有效性。
-
使用复合索引:对于经常同时查询多个列的情况,可以考虑创建复合索引,即包含多个列的联合索引。复合索引可以提高查询效率,并且可以减少索引的数量。
除了以上原则外,创建索引还需要根据具体的数据库系统和查询需求做进一步的优化和调整。在进行索引设计时,需要综合考虑数据库结构、查询模式、数据量等因素,以达到提高查询性能的目的。
在进行索引设计的时候,应该保证索引字段占用的空间越小越好,这只是一个大的方向,还有一些细节点需要注意下:
1、适合索引的列是出现在where字句中的列,或者连接子句中指定的列
2、基数较小的表,索引效果差,没必要创建索引
3、在选择索引列的时候,越短越好,可以指定某些列的一部分,没必要用全部字段的值
4、不要给表中的每一个字段都创建索引,并不是索引越多越好
5、定义有外键的数据列一定要创建索引
6、更新频繁的字段不要有索引
7、创建索引的列不要过多,可以创建组合索引,但是组合索引的列的个数不建议太多
8、大文本、大对象不要创建索引
索引失效的十大原因
索引失效的场景
1、不满足最左匹配原则
2、索引列上有计算 如:where id+1=2
3、索引列上使用了函数 如:where SUBSTR(name,1,2)=17
4、字段类型不同,如本来是数字类型,结果加上了引号变成了字符串
5、like 左边加上了%号6、列对比,两个列在where后面进行比较
7、使用or,如果使用or,前面和后面的列都要加上索引才会生效
8、not in和not exists,如果主键索引会走索引,其他不会
9、!= 慎重使用,基本会走全表,Extral中有using where=全表扫描
10、排序列和联合索引顺序不一致
11、排序列包含非一个索引的列
https://www.cnblogs.com/wushaopei/p/12283647.html
什么是回表查询?
通俗的讲就是,如果索引的列在 select 所需获得的列中(因为在 mysql 中索引是根据索引列的值进行排序的,所以索引节点中存在该列中的部分值)或者根据一次索引查询就能获得记录就不需要回表,如果 select 所需获得列中有大量的非索引列,索引就需要到表中找到相应的列的信息,这就叫回表。
回表查询,先定位主键值,再定位行记录,它的性能较扫一遍索引树更低。
使用聚集索引(主键或第一个唯一索引)就不会回表,普通索引就会回表
什么是覆盖索引?***
通常开发人员会根据查询的where条件来创建合适的索引,但是优秀的索引设计应该考虑到整个查询。其实mysql可以使用索引来直接获取列的数据。如果索引的叶子节点包含了要查询的数据,那么就不用回表查询了,也就是说这种索引包含(亦称覆盖)所有需要查询的字段的值,我们称这种索引为覆盖索引。
1、索引项通常比记录要小,所以MySQL访问更少的数据。
2、索引都按值得大小存储,相对于随机访问记录,需要更少的I/O。
3、数据引擎能更好的缓存索引,比如MyISAM只缓存索引。
4、覆盖索引对InnoDB尤其有用,因为InnoDB使用聚集索引组织数据,如果二级索引包含查询所需的数据,就不再需要在聚集索引中查找了。
限制:
1、覆盖索引也并不适用于任意的索引类型,索引必须存储列的值。
2、Hash和full-text索引不存储值,因此MySQL只能使用BTree。
3、不同的存储引擎实现覆盖索引都是不同的,并不是所有的存储引擎都支持覆盖索引。
4、如果要使用覆盖索引,一定要注意SELECT列表值取出需要的列,不可以SELECT * ,因为如果将所有字段一起做索引会导致索引文件过大,查询性能下降。
索引下推
如果没有索引下推优化(或称ICP优化),当进行索引查询时,首先根据索引来查找记录,然后再根据where条件来过滤记录;在支持ICP优化后,MySQL会在取出索引的同时,判断是否可以进行where条件过滤再进行索引查询,也就是说提前执行where的部分过滤操作,在某些场景下,可以大大减少回表次数,从而提升整体性能。
mysql锁的类型有哪些?
基于锁的属性分类:共享锁、排他锁。
基于锁的粒度分类:行级锁(innodb )、表级锁( innodb 、myisam)、页级锁( innodb引擎)、记录锁、间隙锁、临键锁。
基于锁的状态分类:意向共享锁、意向排它锁。
共享锁(share lock): 共享锁又称读锁,简称 S 锁;当一个事务为数据加上读锁之后,其他事务只能对该数据加读锁,而不能对数据加写锁,直到所有的读锁释放之后其他事务才能对其进行加持写锁。共享锁的特性主要是为了支持并发的读取数据,读取数据的时候不支持修改,避免出现重复读的问题。
排他锁(exclusive lock):排他锁又称写锁,简称 X 锁;当一个事务为数据加上写锁时,其他请求将不能再为数据加任何锁,直到该锁释放之后,其他事务才能对数据进行加锁。排他锁的目的是在数据修改时候,不允许其他人同时修改,也不允许其他人读取,避免了出现脏数据和脏读的问题。
表锁(table lock):表锁是指上锁的时候锁住的是整个表,当下一个事务访问该表的时候,必须等前一个事务释放了锁才能进行对表进行访问;特点:粒度大,加锁简单,容易冲突;
行锁:行锁是指上锁的时候锁住的是表的某一行或多行记录,其他事务访问同一张表时,只有被锁住的记录不能访问,其他的记录可正常访问,特点:粒度小,加锁比表锁麻烦,不容易冲突,相比表锁支持的并发要高
记录锁(Record lock):记录锁也属于行锁中的一种,只不过记录锁的范围只是表中的某一条记录,记录锁是说事务在加锁后锁住的只是表的某一条记录,加了记录锁之后数据可以避免数据在查询的时候被修改的重复读问题,也避免了在修改的事务未提交前被其他事务读取的脏读问题
页锁:页级锁是 MysQL 中锁定粒度介于行级锁和表级锁中间的一种锁.表级锁速度快,但冲突多,行级冲突少,但速度慢。所以取了折衷的页级,一次锁定相邻的一组记录。特点:开销和加锁时间界于表锁和行锁之间,会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般。
间隙锁:是属于行锁的一种,间隙锁是在事务加锁后其锁住的是表记录的某一个区间,当表的相邻ID之间出现空隙则会形成一个区间,遵循左开右闭原则。范围查询并且查询未命中记录,查询条件必须命中索引、间隙锁只会出现在REPEATABLE_READ(重复读)的事务级别中。
临键锁(Next-Key lock):也属于行锁的一种,并且它是INNODB的行锁默认算法,总结来说它就是记录锁和间隙锁的组合,临键锁会把查询出来的记录锁住,同时也会把该范围查询内的所有间隙空间也会锁住,再之它会把相邻的下一个区间也会锁住。
mysql执行计划怎么看?
在 MySQL 中,可以通过使用 `EXPLAIN` 关键字来查看 SQL 语句的执行计划,帮助优化查询性能。下面是使用 `EXPLAIN` 查看 MySQL 执行计划的方法:
### 1. 基本语法:
```sql
EXPLAIN SELECT * FROM your_table WHERE your_condition;
```
2. 实际操作步骤:
a. 执行查询前加上 `EXPLAIN` 关键字:
```sql
EXPLAIN SELECT * FROM your_table WHERE your_condition;
```
b. 查看执行计划结果:
执行以上 SQL 语句后,会返回一张表格,其中包含了关于查询执行的各种重要信息,例如查询的优化方式、表的读取顺序、使用的索引、行数估算等。
c. 解读执行计划信息:
在执行计划结果中,可以注意以下一些重要的字段信息:
- **id:** 查询的顺序,如果是子查询会有多个执行顺序。
- **select_type:** 查询类型,如 SIMPLE、PRIMARY、DERIVED、SUBQUERY 等。
- **table:** 显示查询操作涉及的表。
- **type:** 表示 MySQL 在表中找到所需行的方式,常见的类型有 const、eq_ref、ref、range、index、all 等,通常从最好到最差的顺序排列。
- **key:** 显示 MySQL 决定使用哪个索引来优化查询。
- **rows:** 评估在找到所需行之前要检查的行数。
- **Extra:** 包含关于查询执行的额外信息,可能包括使用了索引、临时表等信息。
3. 根据执行计划优化 SQL:
通过查看执行计划,可以了解 MySQL 数据库是如何执行你的查询的,并可以根据执行计划的信息进行 SQL 语句的调优,例如添加合适的索引、优化表结构、改变查询方式等,以提高查询性能和效率。
通过上述方法,你可以使用 `EXPLAIN` 关键字来查看 MySQL 的执行计划,帮助分析和优化查询语句的执行效率。
事务的基本特性是什么?
事务四大特征:原子性,一致性,隔离性和持久性。
-
原子性(Atomicity) 一个原子事务要么完整执行,要么干脆不执行。这意味着,工作单元中的每项任务都必须正确执行。如果有任一任务执行失败,则整个工作单元或事务就会被终止。即此前对数据所作的任何修改都将被撤销。如果所有任务都被成功执行,事务就会被提交,即对数据所作的修改将会是永久性的。
-
一致性(Consistency) 一致性代表了底层数据存储的完整性。它必须由事务系统和应用开发人员共同来保证。事务系统通过保证事务的原子性,隔离性和持久性来满足这一要求; 应用开发人员则需要保证数据库有适当的约束(主键,引用完整性等),并且工作单元中所实现的业务逻辑不会导致数据的不一致(即,数据预期所表达的现实业务情况不相一致)。例如,在一次转账过程中,从某一账户中扣除的金额必须与另一账户中存入的金额相等。支付宝账号100 你读到余额要取,有人向你转100 但是事物没提交(这时候你读到的余额应该是100,而不是200) 这种就是一致性
-
隔离性(Isolation) 隔离性意味着事务必须在不干扰其他进程或事务的前提下独立执行。换言之,在事务或工作单元执行完毕之前,其所访问的数据不能受系统其他部分的影响。
-
持久性(Durability) 持久性表示在某个事务的执行过程中,对数据所作的所有改动都必须在事务成功结束前保存至某种物理存储设备。这样可以保证,所作的修改在任何系统瘫痪时不至于丢失。
MySQL的隔离级别有哪些?
MySQL定义了四种隔离级别,包括一些具体规则,用于限定事务内外哪些改变是可见的,哪些改变是不可见的。低级别的隔离一般支持更高的并发处理,并且拥有更低的系统开销。
READ UNCOMMITTED 读取未提交内容 在这个隔离级别,所有事务都可以“看到”未提交事务的执行结果。在这种级别上,可能会产生很多问题,除非用户真的知道自己在做什么,并有很好的理由选择这样做。本隔离级别很少用于实际应用,因为它的性能也不必其他性能好多少,而别的级别还有其他更多的优点。读取未提交数据,也被称为“脏读”
READ COMMITTED 读取提交内容 大多数数据库系统的默认隔离级别(但是不是MySQL的默认隔离级别),满足了隔离的早先简单定义:一个事务开始时,只能“看见”已经提交事务所做的改变,一个事务从开始到提交前,所做的任何数据改变都是不可见的,除非已经提交。这种隔离级别也支持所谓的“不可重复读”。这意味着用户运行同一个语句两次,看到的结果是不同的。
REPEATABLE READ 可重复读 MySQL数据库默认的隔离级别。该级别解决了READ UNCOMMITTED隔离级别导致的问题。它保证同一事务的多个实例在并发读取事务时,会“看到同样的”数据行。不过,这会导致另外一个棘手问题“幻读”。InnoDB和Falcon存储引擎通过多版本并发控制机制解决了幻读问题。
SERIALIZABLE 可串行化 该级别是最高级别的隔离级。它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简而言之,SERIALIZABLE是在每个读的数据行上加锁。在这个级别,可能导致大量的超时Timeout和锁竞争Lock Contention现象,实际应用中很少使用到这个级别,但如果用户的应用为了数据的稳定性,需要强制减少并发的话,也可以选择这种隔离级。
一级封锁协议。事务T在修改数据R之前必须先对其加X 锁 直到事务结束才释放。可防止丢失修改 二级封锁协议。一级封锁协议加上事务T在读取数据R之前先对其加S锁,读完后即可释放S锁。可防止丢失修改,还可防止读“脏”数据 三级封锁协议。一级封锁协议加上事务T在读取数据R之前先对其加S锁,直到事务结束才释放。可防止丢失修改、防止读“脏”数据与防止数据(不可)重复读
两段锁协议。可串行化的。可能发生死锁
-
脏读
脏读是指一个事务读取了未提交事务执行过程中的数据。 当一个事务的操作正在多次修改数据,而在事务还未提交的时候,另外一个并发事务来读取了数据,就会导致读取到的数据并非是最终持久化之后的数据,这个数据就是脏读的数据。
-
不可重复读
不可重复读是指对于数据库中的某个数据,一个事务执行过程中多次查询返回不同查询结果,这就是在事务执行过程中,数据被其他事务提交修改了。 不可重复读同脏读的区别在于,脏读是一个事务读取了另一未完成的事务执行过程中的数据,而不可重复读是一个事务执行过程中,另一事务提交并修改了当前事务正在读取的数据。
-
虚读(幻读)
幻读是事务非独立执行时发生的一种现象,例如事务T1批量对一个表中某一列列值为1的数据修改为2的变更,但是在这时,事务T2对这张表插入了一条列值为1的数据,并完成提交。此时,如果事务T1查看刚刚完成操作的数据,发现还有一条列值为1的数据没有进行修改,而这条数据其实是T2刚刚提交插入的,这就是幻读。 幻读和不可重复读都是读取了另一条已经提交的事务(这点同脏读不同),所不同的是不可重复读查询的都是同一个数据项,而幻读针对的是一批数据整体(比如数据的个数)。
怎么处理MySQL的慢查询?
处理 MySQL 的慢查询可以采取以下几种方法:
-
使用索引优化:慢查询通常是由于缺乏或不正确使用索引导致的。通过分析慢查询的执行计划,确定应该创建哪些索引来加速查询。可以使用
EXPLAIN
或EXPLAIN ANALYZE
命令来查看查询的执行计划,并使用CREATE INDEX
命令创建适当的索引。 -
优化查询语句:优化查询语句的编写可以显著提升查询性能。避免使用全表扫描和不必要的连接操作,尽量简化查询条件,并使用合适的查询语句,如使用
INNER JOIN
替代WHERE
子句中的子查询。 -
- 确保编写高效的 SQL 查询语句,避免不必要的查询、使用 `SELECT *`、多余的子查询等。
- 尽量避免在 WHERE 子句中对字段进行函数操作,会影响索引的使用。
- 使用合适的 JOIN 操作,避免笛卡尔积(Cartesian Product)和不必要的连接。
- 限制返回结果集的大小,避免一次性获取过多数据。 -
调整服务器参数:通过适当设置 MySQL 服务器的参数,可以提升数据库的性能。例如,增加缓冲区大小(如
innodb_buffer_pool_size
)、优化查询缓存(如query_cache_size
)和调整并发连接数等。 -
切分大表:如果查询的表非常大,可以考虑将大表切分为更小的表,在查询时只查询所需的分片表,可以减轻单个查询的负载,提高查询速度。
-
定期优化和维护数据库:定期对数据库进行优化和维护可以提升整体性能。包括定期进行表优化、碎片整理、数据归档和备份等操作,以确保数据库的健康状态。
-
监控和记录慢查询:及时发现和记录慢查询是优化的起点。通过开启慢查询日志(slow query log)来记录慢查询语句,然后根据日志分析找出消耗时间较长的查询,优化这些查询可以提高整体性能。
-
使用缓存:对于一些频繁查询的数据,可以考虑使用缓存技术,如使用 Redis、Memcached 等缓存服务器缓存查询结果,减少对数据库的访问次数,提高响应速度。
-
使用数据库连接池:使用数据库连接池可以减少数据库连接的开销,提高并发性能。连接池会管理数据库连接的获取和释放,合理配置连接池参数可以提高数据库的性能和稳定性。
通过以上方法,可以逐步优化和改进数据库的慢查询问题,提升数据库的性能和响应速度。需要根据具体的业务场景和数据库配置来选择和调整优化策略。
ACID是靠什么保证的?
原子性由undolog日志来保证,它记录了需要回滚的日志信息,事务回滚时撤销已经执行成功的sql
一致性是由其他三大特性保证,程序代码要保证业务上的一致性
隔离性是由MVCC来保证
持久性由redolog来保证,mysql修改数据的时候会在redolog中记录一份日志数据,就算数据没有保存成功,只要日志保存成功了,数据仍然不会丢失
什么是MVCC?
1、MVCC
MVCC,全称Multi-Version Concurrency Control,即多版本并发控制。MVCC是一种并发控制的方法,一般在数据库管理系统中,实现对数据库的并发访问,在编程语言中实现事务内存。
MVCC在MySQL InnoDB中的实现主要是为了提高数据库并发性能,用更好的方式去处理读写冲突,做到即使有读写冲突时,也能做到不加锁,非阻塞并发读。
2、当前读
像select lock in share mode(共享锁), select for update ; update, insert ,delete(排他锁)这些操作都是一种当前读,为什么叫当前读?就是它读取的是记录的最新版本,读取时还要保证其他并发事务不能修改当前记录,会对读取的记录进行加锁。
3、快照读(提高数据库的并发查询能力)
像不加锁的select操作就是快照读,即不加锁的非阻塞读;快照读的前提是隔离级别不是串行级别,串行级别下的快照读会退化成当前读;之所以出现快照读的情况,是基于提高并发性能的考虑,快照读的实现是基于多版本并发控制,即MVCC,可以认为MVCC是行锁的一个变种,但它在很多情况下,避免了加锁操作,降低了开销;既然是基于多版本,即快照读可能读到的并不一定是数据的最新版本,而有可能是之前的历史版本
4、当前读、快照读、MVCC关系
MVCC多版本并发控制指的是维持一个数据的多个版本,使得读写操作没有冲突,快照读是MySQL为实现MVCC的一个非阻塞读功能。MVCC模块在MySQL中的具体实现是由三个隐式字段,undo日志、read view三个组件来实现的。
MVCC解决的问题是什么?
数据库并发场景有三种,分别为:
1、读读:不存在任何问题,也不需要并发控制
2、读写:有线程安全问题,可能会造成事务隔离性问题,可能遇到脏读、幻读、不可重复读
3、写写:有线程安全问题,可能存在更新丢失问题
MVCC是一种用来解决读写冲突的无锁并发控制,也就是为事务分配单项增长的时间戳,为每个修改保存一个版本,版本与事务时间戳关联,读操作只读该事务开始前的数据库的快照,所以MVCC可以为数据库解决一下问题:
1、在并发读写数据库时,可以做到在读操作时不用阻塞写操作,写操作也不用阻塞读操作,提高了数据库并发读写的性能
2、解决脏读、幻读、不可重复读等事务隔离问题,但是不能解决更新丢失问题
MVCC实现原理是什么?
MVCC(Multi-Version Concurrency Control)是一种数据库并发控制技术,常用于实现事务的隔离性,保证事务之间的并发执行。MVCC 的实现原理主要通过版本控制来实现多个事务并发执行而不会相互影响,常见于像 MySQL、PostgreSQL 等数据库系统中。
### MVCC 的实现原理如下:
1. **版本控制:** 每次对数据库的数据进行更新时,并不直接修改原始数据,而是创建新的版本(或称为快照)来保存被修改的数据。这样,每个事务在读取数据时,都会读取到一个版本的数据,而不会受到其他事务同时修改数据的影响。
2. **事务版本号:** 每个事务有一个唯一的事务版本号,用来标识该事务可以读取的数据版本。事务在读取数据时,会根据自己的事务版本号来确定所读取数据的版本,从而实现事务之间的隔离性。
3. **数据行的版本信息:** 每行数据都会包含版本信息,通常有两种方式来实现:
- 一种是在数据行中添加版本号字段,用来记录数据的版本信息;
- 另一种是使用时间戳,记录数据被修改的时间。
4. **读取版本判断:** 当事务读取数据时,系统会根据事务的版本号和数据的版本信息来判断是否可以读取该数据,一般有以下几种情况:
- 如果数据的版本号在事务开始之前,则可以读取;
- 如果数据的版本号在事务开始之后,但是数据的更新事务未提交,则根据事务隔离级别确定是否可以读取。
5. **写操作处理:** 当事务对数据进行更新时,系统会创建一个新的版本来保存修改后的数据,同时保留原始数据的版本。其他事务在读取数据时,可以根据版本控制机制来读取到合适的数据版本。
通过 MVCC 技术,数据库系统可以实现高并发性和事务隔离性,同时保证数据的一致性。不同的数据库系统可能会有不同的实现细节,但 MVCC 的核心思想是基于版本控制来实现并发控制。
RC(读提交内容)、RR(可重复读)级别下的InnoDB快照读有什么不同
因为Read View生成时机的不同,从而造成RC、RR级别下快照读的结果的不同
1、在RR级别下的某个事务的对某条记录的第一次快照读会创建一个快照即Read View,将当前系统活跃的其他事务记录起来,此后在调用快照读的时候,还是使用的是同一个Read View,所以只要当前事务在其他事务提交更新之前使用过快照读,那么之后的快照读使用的都是同一个Read View,所以对之后的修改不可见
2、在RR级别下,快照读生成Read View时,Read View会记录此时所有其他活动和事务的快照,这些事务的修改对于当前事务都是不可见的,而早于Read View创建的事务所做的修改均是可见
3、在RC级别下,事务中,每次快照读都会新生成一个快照和Read View,这就是我们在RC级别下的事务中可以看到别的事务提交的更新的原因。
总结:在RC隔离级别下,是每个快照读都会生成并获取最新的Read View,而在RR隔离级别下,则是同一个事务中的第一个快照读才会创建Read View,之后的快照读获取的都是同一个Read View.
什么是mysql的主从复制?
MySQL 主从复制是指数据可以从一个MySQL数据库服务器主节点复制到一个或多个从节点。MySQL 默认采用异步复制方式,这样从节点不用一直访问主服务器来更新自己的数据,数据的更新可以在远程连接上进行,从节点可以复制主数据库中的所有数据库或者特定的数据库,或者特定的表。
mysql为什么需要主从同步?
1、在业务复杂的系统中,有这么一个情景,有一句sql语句需要锁表,导致暂时不能使用读的服务,那么就很影响运行中的业务,使用主从复制,让主库负责写,从库负责读,这样,即使主库出现了锁表的情景,通过读从库也可以保证业务的正常运作。
2、做数据的热备
3、架构的扩展。业务量越来越大,I/O访问频率过高,单机无法满足,此时做多库的存储,降低磁盘I/O访问的频率,提高单个机器的I/O性能。
mysql复制原理是什么?
(1)master服务器将数据的改变记录二进制binlog日志,当master上的数据发生改变时,则将其改变写入二进制日志中;
(2)slave服务器会在一定时间间隔内对master二进制日志进行探测其是否发生改变,如果发生改变,则开始一个I/OThread请求master二进制事件
(3)同时主节点为每个I/O线程启动一个dump线程,用于向其发送二进制事件,并保存至从节点本地的中继日志中,从节点将启动SQL线程从中继日志中读取二进制日志,在本地重放,使得其数据和主节点的保持一致,最后I/OThread和SQLThread将进入睡眠状态,等待下一次被唤醒。
也就是说:
-
从库会生成两个线程,一个I/O线程,一个SQL线程;
-
I/O线程会去请求主库的binlog,并将得到的binlog写到本地的relay-log(中继日志)文件中;
-
主库会生成一个log dump线程,用来给从库I/O线程传binlog;
-
SQL线程,会读取relay log文件中的日志,并解析成sql语句逐一执行;
注意:
1--master将操作语句记录到binlog日志中,然后授予slave远程连接的权限(master一定要开启binlog二进制日志功能;通常为了数据安全考虑,slave也开启binlog功能)
。 2--slave开启两个线程:IO线程和SQL线程。其中:IO线程负责读取master的binlog内容到中继日志relay log里;SQL线程负责从relay log日志里读出binlog内容,并更新到slave的数据库里,这样就能保证slave数据和master数据保持一致了。
3--Mysql复制至少需要两个Mysql的服务,当然Mysql服务可以分布在不同的服务器上,也可以在一台服务器上启动多个服务。
4--Mysql复制最好确保master和slave服务器上的Mysql版本相同(如果不能满足版本一致,那么要保证master主节点的版本低于slave从节点的版本) 5--master和slave两节点间时间需同步
具体步骤:
1、从库通过手工执行change master to 语句连接主库,提供了连接的用户一切条件(user 、password、port、ip),并且让从库知道,二进制日志的起点位置(file名 position 号); start slave
2、从库的IO线程和主库的dump线程建立连接。
3、从库根据change master to 语句提供的file名和position号,IO线程向主库发起binlog的请求。
4、主库dump线程根据从库的请求,将本地binlog以events的方式发给从库IO线程。
5、从库IO线程接收binlog events,并存放到本地relay-log中,传送过来的信息,会记录到master.info中
6、从库SQL线程应用relay-log,并且把应用过的记录到relay-log.info中,默认情况下,已经应用过的relay 会自动被清理purge
简述Myisam和Innodb的区别?
MyISAM和InnoDB是MySQL数据库中常见的两种存储引擎,它们在性能、功能和特性上有一些区别。以下是它们的主要区别:
-
事务支持: MyISAM不支持事务,而InnoDB支持事务。事务是一系列数据库操作的逻辑单元,可以保证数据的一致性和完整性。对于需要严格事务支持的应用,如高并发的OLTP(联机事务处理)系统,InnoDB更加适合。
-
并发控制: MyISAM使用表级锁定,即当一个线程对表执行写操作时,其他线程无法对该表进行写操作,但可以进行读操作。而InnoDB使用行级锁定,允许并发事务读写不同的行,提供更好的并发性能。
-
数据完整性: MyISAM不提供外键约束,也不支持事务的回滚和崩溃恢复功能。而InnoDB支持外键约束,可以保证数据的一致性和完整性,并提供了崩溃恢复和回滚机制。
-
索引类型: MyISAM只支持表级锁定,对于大量的读操作和少量的写操作性能较好。InnoDB使用B+树索引,支持行级锁定,对于大量的读写操作性能较好。
-
崩溃恢复: MyISAM在崩溃后需要执行修复操作来恢复数据一致性,修复过程相对较慢。而InnoDB通过事务的日志和redolog来实现快速崩溃恢复。
总的来说,MyISAM对于读操作较多的应用场景和小型网站更适合,而InnoDB适用于需要事务支持、并发性要求高以及数据完整性的应用,特别是对于大型的OLTP系统。在选择存储引擎时,需要根据应用的特点和需求进行综合考虑。
InnoDB存储引擎: 主要面向OLTP(Online Transaction Processing,在线事务处理)方面的应用,是第一个完整支持ACID事务的存储引擎(BDB第一个支持事务的存储引擎,已经停止开发)。 特点:
1 支持行锁 2 支持外键 3 支持自动增加列AUTO_INCREMENT属性 4 支持事务 5 支持MVCC模式的读写 6 读的效率低于MYISAM 7.写的效率高优于MYISAM 8.适合频繁修改以及设计到安全性较高的应用 9.清空整个表的时候,Innodb是一行一行的删除,
MyISAM存储引擎: 是MySQL官方提供的存储引擎,主要面向OLAP(Online Analytical Processing,在线分析处理)方面的应用。
特点:
1 独立于操作系统,当建立一个MyISAM存储引擎的表时,就会在本地磁盘建立三个文件,例如我建立tb_demo表,那么会生成以下三个文件tb_demo.frm,tb_demo.MYD,tb_demo.MYI 2 不支持事务, 3 支持表锁和全文索引 4 MyISAM存储引擎表由MYD和MYI组成,MYD用来存放数据文件,MYI用来存放索引文件。MySQL数据库只缓存其索引文件,数据文件的缓存交给操作系统本身来完成; 5 MySQL5.0版本开始,MyISAM默认支持256T的单表数据; 6.选择密集型的表:MYISAM存储引擎在筛选大量数据时非常迅速,这是他最突出的优点 7.读的效率优于InnoDB 8.写的效率低于InnoDB 9.适合查询以及插入为主的应用 10.清空整个表的时候,MYISAM则会新建表
简述mysql中索引类型有哪些,以及对数据库的性能的影响?
普通索引:允许被索引的数据列包含重复的值
唯一索引:可以保证数据记录的唯一性
主键索引:是一种特殊的唯一索引,在一张表中只能定义一个主键索引,主键用于唯一标识一条记录,使用关键字primary key来创建
联合索引:索引可以覆盖多个数据列
全文索引:通过建立倒排索引,可以极大的提升检索效率,解决判断字段是否包含的问题,是目前搜索引擎使用的一种关键技术
索引可以极大地提高数据的查询速度
通过使用索引,可以在查询的过程中,使用优化隐藏器,提高系统的性能
但是会降低插入、删除、更新表的速度,因为在执行这些写操作的时候,还要操作索引文件
索引需要占物理空间,除了数据表占数据空间之外,每一个索引还要占一定的物理空间,如果要简历聚簇索引,那么需要的空间就会更大,如果非聚簇索引很多,一旦聚簇索引改变,那么所有非聚簇索引都会跟着变
mysql聚簇和非聚簇索引的区别
都是B+树的数据结构 聚簇索引:将数据存储与索引放到了一块、并且是按照一定的顺序组织的,找到索引也就找到了数 据,数据的物理存放顺序与索引顺序是一致的,即:只要索引是相邻的,那么对应的数据一定也是 相邻地存放在磁盘上的 非聚簇索引:叶子节点不存储数据、存储的是数据行地址,也就是说根据索引查找到数据行的位置 再取磁盘查找数据,这个就有点类似一本树的目录,比如我们要找第三章第一节,那我们先在这个 目录里面找,找到对应的页码后再去对应的页码看文章。