八股文--数据库篇

什么是数据库事务?

事务是作为一个逻辑单元执行的一系列操作,要么一起成功,要么一起失败。一个逻辑工作单元必须有四个属性,称为 ACID(原子性、一致性、隔离性和持久性)属性,只有这样才能成为一个事务。

数据库的四大特性(数据库事务有什么好处)

1)原子性:(Atomicity)

事务必须是原子工作单元;对于其数据修改,要么全都执行,要么全都不执行。

2)一致性:(Consistency)

事务在完成时,必须使所有的数据都保持一致状态。在相关数据库中,所有规则都必须应用于事务的修改,保持所有数据的完整性。事务结束时,所有的内部数据结构(如 B 树索引或双向链表)都必须是正确的。

3)隔离性:(Isolation)

由并发事务所作的修改必须与任何其它并发事务所作的修改隔离。事务查看数据时数据所处的状态,要么另一并发事务修改它之前的状态,要么是另一事务修改它之后的状态,事务不会查看中间状态的数据。

4)持久性:(Durability)

事务完成之后,它对于系统的影响是永久性的。该修改即使出现系统故障也将一直保持。

binlog、redo log 和 undo log

链接

binlog:binlog用于保证数据库的持久性,记录数据库执行的写入性操作(不包括查询)信息,以二进制的形式保存在磁盘中。(用于数据库恢复)(mysql 执行的所有命令都会记录)

redo log: 为了保证数据库的持久性。记录事务对数据页做了哪些修改 innoDb引擎才有的日志,只记录事务的数据上的修改
因为Innodb是以页为单位进行磁盘交互的,而一个事务很可能只修改一个数据页里面的几个字节,这个时候将完整的数据页刷到磁盘的话,太浪费资源了!
一个事务可能涉及修改多个数据页,并且这些数据页在物理上并不连续,使用随机IO写入性能太差!
redo log包括两部分:一个是内存中的日志缓冲(redo log buffer),另一个是磁盘上的日志文件(redo log file)。mysql每执行一条DML语句,先将记录写入redo log buffer,后续某个时间点再一次性将多个操作记录写到redo log file。这种先写日志,再写磁盘的技术就是MySQL里经常说到的WAL(Write-Ahead Logging) 技术。
使用场景:
1、批量修改硬盘文件,加快效率。
2、若没有更新到硬盘数据库挂了,能成功恢复操作

redo log与binlog区别
由binlog和redo log的区别可知:binlog日志只用于归档,只依靠binlog是没有crash-safe能力的。但只有redo log也不行,因为redo log是InnoDB特有的,且日志上的记录落盘后会被覆盖掉。因此需要binlog和redo log二者同时记录,才能保证当数据库发生宕机重启时,数据不会丢失。

undo log:undo log 保证数据的原子性。undo log主要记录了数据的逻辑变化,比如一条INSERT语句,对应一条DELETE的undo log,对于每个UPDATE语句,对应一条相反的UPDATE的undo log,这样在发生错误时,就能回滚到事务之前的数据状态。(侧重于回滚到之前的某个状态,即记录的是相反的逻辑操作)

事务并发时会发生什么问题

脏读:脏读是指在一个事务处理过程里读取了另一个未提交的事务中的数据。
不可重复读:某个数据,一个事务范围内多次查询却返回了不同的数据值,这是由于在查询间隔,被另一个事务修改并提交了。不可重复读的重点是update和delete,同样的条件,你读取过的数据,再次读取出来发现值不一样;
幻读:是指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行。

注:
可重复读仅能保证再次执行同样的查询时,先前返回【过】的结果一定跟之前一样,而不保证会不会多出别的记录。如果返回了之前没有出现过的记录,就是幻读
为什么加锁不能解决幻读? 因为InnoDB是加的行锁,行锁无法锁住之前事务中没有存在的行。

数据库的隔离级别

  1. Read uncommitted(读未提交):最低级别,以上情况均无法保证。
  2. Read committed(读已提交):可避免脏读情况发生。
    实现机制:修改时加排他锁,直到事务提交后才释放,读取时加共享锁,读取完释放。事务1读取数据时加上共享锁后(这 样在事务1读取数据的过程中,其他事务就不会修改该数据),不允许任何事物操作该数据,只能读取,之后1如果有更新操作,那么会转换为排他锁,其他事务更
    无权参与进来读写,这样就防止了脏读问题。
    但是当事务1读取数据过程中,有可能其他事务也读取了该数据,读取完毕后共享锁释放,此时事务1修改数据,修改
    完毕提交事务,其他事务再次读取数据时候发现数据不一致,就会出现不可重复读问题,所以这样不能够避免不可重复读问题。
  3. Repeatable read(可重复读) mysql的默认隔离级别:可避免脏读、不可重复读情况的发生。
    实现机制:读取数据时加共享锁,写数据时加排他锁,都是事务提交才释放锁。读取时候不允许其他事物修改该数据,不管数据在事务过程中读取多少次,数据都是一致的,避免了不可重复读问题。

注:读已提交和可重复读的区别在于,读已提交读数据时加的共享锁是读取完释放,而可重复读是事务提交才释放

  1. Serializable(串行化):可避免脏读、不可重复读、幻读情况的发生。

    实现机制:所有的读操作均为当前读,读加读锁 (S锁),写加写锁 (X锁)。 Serializable隔离级别下,读写冲突,因此并发度急剧下降,在MySQL/InnoDB下不建议使用。

mysql在RR(Repeatable read)隔离级别下如何解决幻读的?

链接
MySQL默认隔离级别(RR)使用MVCC+锁混合的模式来解决脏读、不可重读、幻读等问题。

(MVCC是乐观锁,,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号机制或CAS算法实现)

解决方法:MySQL的大多数事务型存储引擎实现的都不是简单的行级锁。基于提升并发性能的考虑,它们一般都同时实现了多版本并发控制(MVCC)
InnoDB的MVCC,是通过在每行记录后面保存两个隐藏的列来实现的。这两个列,一个保存了行的创建时间,一个保存行的过期时间(或删除时间)。当然存储的并不是实际的时间值,而是系统版本号(systemversionnumber)。每开始一个新的事务,系统版本号都会自动递增。事务开始时刻的系统版本号会作为事务的版本号,用来和查询到的每行记录的版本号进行比较。
SELECT InnoDB会根据以下两个条件检查每行记录:InnoDB只查找版本早于当前事务版本的数据行(也就是,行的系统版本号小于或等于事务的系统版本号),这样可以确保事务读取的行,要么是在事务开始前已经存在的,要么是事务自身插入或者修改过的。行的删除版本要么未定义,要么大于当前事务版本号。这可以确保事务读取到的行,在事务开始之前未被删除。只有符合上述两个条件的记录,才能返回作为查询结果。INSERT InnoDB为新插入的每一行保存当前系统版本号作为行版本号。DELETE InnoDB为删除的每一行保存当前系统版本号作为行删除标识。UPDATE InnoDB为插入一行新记录,保存当前系统版本号作为行版本号,同时保存当前系统版本号到原来的行作为行删除标识。保存这两个额外系统版本号,使大多数读操作都可以不用加锁。这样设计使得读数据操作很简单,性能很好,并且也能保证只会读取到符合标准的行。不足之处是每行记录都需要额外的存储空间,需要做更多的行检查工作,以及一些额外的维护工作。

mysql有哪些engine,有什么区别?

链接
innodb,myisam:
区别:

  1. innodb支持事务,对于InnoDB每一条SQL语言都默认封装成事务,自动提交,这样会影响速度。

  2. InnoDB支持外键,而MyISAM不支持。

  3. InnoDB的B+树主键索引的叶子节点就是数据文件,辅助索引的叶子节点是主键的值;而MyISAM的B+树主键索引和辅助索引的叶子节点都是数据文件的地址指针。

  4. InnoDB支持表、行(默认)级锁,而MyISAM支持表级锁

  5. InnoDB表必须有唯一索引(如主键)(用户没有指定的话会自己找/生产一个隐藏列Row_id来充当默认主键),而Myisam可以没有

  6. Innodb存储文件有frm、ibd,而Myisam是frm、MYD、MYI

    Innodb:frm是表定义文件,ibd是数据文件
    
    Myisam:frm是表定义文件,myd是数据文件,myi是索引文件
    

聚簇索引和非聚簇索引的区别?不同引擎的主索引和二级(辅助)索引的存储方式?

聚簇索引: 叶子节点中存放的就是整张表的行记录数据聚簇索引就是按照每张表的**主键(也称主索引)**构造一颗B+树,同时叶子节点中存放的就是整张表的行记录数据。
非聚簇索引::非聚簇索引访问数据总是需要二次查找,二级索引(辅助索引)创建B+树的非聚集索引。非聚簇索引叶子节点存储的不再是行的物理位置,而是主键值。通过辅助索引首先找到的是主键值,在通过主键和地址信息拿到数据。

这两种索引内部都是B+树。

InnoDB的主索引采用聚集索引,二级(辅助)索引采用非聚集索引。而myisam索引使用非聚集索引。

什么是回表查询?
在主键索引树上,通过主键就可以一次性查出来我们所需要的数据,速度非常的快。

因为主键和行记录就存储在一起,定位到了主键,也就定位到了所要找的记录,当前行的所有字段都在这(这也是为什么我们说,在创建表的时候,最好是创建一个主键,查询时也尽量用主键来查询)。

对于普通索引,则需要根据where条件后条件字段对应的的索引树(非聚集索引)找到叶子节点对应的主键,然后再通过主键去主键索引树查询一遍,才可以得到要找的记录。这就叫 回表查询。

什么是联合索引?索引最左前缀原理?什么是覆盖索引?

链接
联合索引: 多个字段组成的索引。如下表‘idx_a_b_c’

CREATE TABLE `test` (
  `id` int(4) NOT NULL AUTO_INCREMENT,
  `a` varchar(10) NOT NULL,
  `b` varchar(10) NOT NULL,
  `c` varchar(10) NOT NULL,
  `d` varchar(10) NOT NULL,
  `e` varchar(10) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `idx_a_b_c` (`a`,`b`,`c`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;

联合索引存储的b+树结构大概如下,当索引的维护其实是以第一个字段来优先排序的,如果你的查询条件里没有第一个字段就没法通过索引比较来定位数据
在这里插入图片描述

索引最左前缀原理

  • 联合索引的最左前缀匹配指的是where条件一定要有联合索引的第一个字段。
  • 是否走联合索引与where条件的顺序无关,只与字段有关。

覆盖索引:
覆盖索引其实是一种特殊的联合索引,怎么理解呢,即是你查询的字段的所有数据都在索引上,不需要再进行一次回表查询,这样的索引即为覆盖索引。例如下sql即会走覆盖索引。

SELECT a,b,c from test where a = '333';

唯一索引

唯一索引就是 使一个或者多个列的值具有唯一性。另外,不会像主键索引一样,我们的每张表中可以有很多个唯一索引

CREATE UNIQUE INDEX

在这里插入图片描述
唯一索引和主索引的区别:
1、唯一索引本质上是辅助索引,然后加了唯一约束。存储的结构不一样
2、唯一索引可以为null(可以存在多个null),主索引不可以。

InnoDB为什么推荐使用自增ID作为主键?

答:自增ID可以保证每次插入时B+索引是从右边扩展的,可以避免B+树和频繁合并和分裂(对比使用UUID)。如果使用字符串主键和随机主键,会使得数据随机插入,效率比较差。

分页查询 limit offset太大的时候会有什么问题?

csdn
因此可以证实,mysql查询时,offset过大影响性能的原因是多次通过主键索引访问数据块的I/O操作。(注意,只有InnoDB有这个问题,而MYISAM索引结构与InnoDB不同,二级索引都是直接指向数据块的,因此没有此问题 。

B+树的优点和缺点?为什么不用hash存储?

优点:
1、请求设计的磁盘IO访问速度快(高度低、非叶子节点不带数据)。
2、查询效率稳定,都是O(logn)
3、叶子结点带指针,且排序,查询批量数据只需要知道收尾地址即可得到所有数据。

缺点:
1、平衡树增删效率不高,
2、存储比较大,要构造节点,存储一些结点与结点之间额外的信息。
(1、2都是树的通病)

为什么不用hash?
b+树的查找效率O(logn),而hash是O(1)
1、B+树更适合文件存储结构,不像哈希表,需要一次把数据都加入内存,b+树可以分层加载数据,减少内存使用。
2、B+树存储的数据都在叶子结点,并且叶子结点有指针相连,往往sql查询不只只有一个条件,多条件查询结果可以在叶子结点通过指针相连,效率比哈希表更快。
3、hash表不能范围查找。

count(*) 和count(1)的区别? inner join 和where的区别? 使用select * 效率怎样?

1、count(*) 和count(1)和count(列名)

count(*)包括了所有的列,相当于行数,在统计结果的时候,不会忽略列值为NULL
count(1)包括了忽略所有列,用1代表代码行,在统计结果的时候,不会忽略列值为NULL
count(列名)只包括列名那一列,在统计结果的时候,会忽略列值为空(这里的空不是只空字符串或者0,而是表示null)的计数,即某个字段值为NULL时,不统计。

执行效率上:
列名为主键,count(列名)会比count(1)快
列名不为主键,count(1)会比count(列名)快
如果表多个列并且没有主键,则 count(1) 的执行效率优于 count(*)
如果有主键,则 select count(主键)的执行效率是最优的
如果表只有一个字段,则 count(*)最优。

2、 inner join 和where的区别?

多表查询时,where语句实际上是创造两张表的笛卡尔积的数据,例如有1000顾客和1000条销售记录,这个查询会先产生1000000个结果,然后通过正确的 ID过滤出1000条记录。inner join (等同于 join ,一样的)这样数据库就只产生等于ID 的1000条目标结果。增加了查询效率。

有些数据库系统会识别出 WHERE连接并自动转换为 INNER JOIN。

3、为什么不推荐使用select * ?

  1. 用“SELECT * ”数据库需要解析更多的对象、字段、权限、属性等相关内容,在 SQL语句复杂,硬解析较多的情况下,会对数据库造成沉重的负担

  2. 增大网络开销;有时会误带上如log、IconMD5之类的无用且大文本字段,数据传输size会几何增涨。如果DB和应用程序不在同一台机器,这种开销非常明显

  3. 即使 mysql 服务器和客户端是在同一台机器上,使用的协议还是 tcp,通信也是需要额外的时间。

  4. 使用select * ,不走索引,查询效率很慢。推荐使用带索引的查询。

Redis的特点? Redis基本的数据结构?

Redis是一个关系型支持持久化的内存数据库,通过持久化机制把内存中的数据同步到硬盘文件来保证数据持久化。

Redis为什么那么快:
(一)纯内存操作
(二)大部分数据处理使用单线程操作,避免了多线程的资源消耗和频繁的上下文切换
(三)采用了非阻塞I/O多路复用机制
在这里插入图片描述

上图就是基于多路复用的 Redis IO 模型。redis会单独使用一个线程来处理多用户的网络IO。图中的多个 FD 就是刚才所说的多个套接字。Redis 网络框架调用 epoll 机制,让内核监听这些套接字。此时,Redis 线程不会阻塞在某一个特定的监听或已连接套接字上,也就是说,不会阻塞在某一个特定的客户端请求处理上。redis会从事件处理队列中拿请求,有则拿走,没有不会等待(非阻塞)。正因为此,Redis 可以同时和多个客户端连接并处理请求,从而提升并发性。

Redis基本数据结构:
(一)String .value可以是String也可以是数字。一般做一些复杂的计数功能的缓存。
(二)hash
这里value存放的是结构化的对象,比较方便的就是操作其中的某个字段。博主在做单点登录的时候,就是用这种数据结构存储用户信息,以cookieId作为key,设置30分钟为缓存过期时间,能很好的模拟出类似session的效果。
(三)list
使用List的数据结构,可以做简单的消息队列的功能。另外还有一个就是,可以利用lrange命令,做基于redis的分页功能,性能极佳,用户体验好。本人还用一个场景,很合适—取行情信息。就也是个生产者和消费者的场景。LIST可以很好的完成排队,先进先出的原则。
(四)set
因为set堆放的是一堆不重复值的集合。所以可以做全局去重的功能。为什么不用JVM自带的Set进行去重?因为我们的系统一般都是集群部署,使用JVM自带的Set,比较麻烦,难道为了一个做一个全局去重,再起一个公共服务,太麻烦了。
另外,就是利用交集、并集、差集等操作,可以计算共同喜好,全部的喜好,自己独有的喜好等功能。
(五)sorted set
sorted set多了一个权重参数score,集合中的元素能够按score进行排列。可以做排行榜应用,取TOP N操作。

Redis的持久化方案RDB和AOF的区别
链接

Redis的集群部署
链接

mybatis的优缺点

在这里插入图片描述

#{}和${}的区别

#{}是占位符,mybatis会将sql中的#{}替换成’?'号,调用prepareStatement来赋值,防止sql注入,提供系统安全性。
${}是拼接附,sql调用statement,不安全。
${} 填充不会加‘’符号,表名用"$"

mybatis索引的设计原则

在这里插入图片描述

mybatis的执行流程?

链接
在这里插入图片描述
mybatis运行时要先通过resources把核心配置文件也就是mybatis.xml文件加载进来,然后通过xmlConfigBulider来解析,解析完成后把结果放入configuration中,并把它作为参数传入到build()方法中,并返回一个defaultSQLSessionFactory。我们再调用openSession()方法,来获取SqlSession,在构建SqlSession的同时还需要transaction和executor用于后续执行操作。
mybatis的执行流程的核心步骤
csdn

  1. 对xml文件的解析。解析xml对象,通过使用configration创建 SqlSessionFactoryBuilder 类。再使用SqlSessionFactoryBuilder创建SqlSessionFactory。(一个xml文件其实对应一个sqlSessionFactory,mybatis使用建造者模式创建sqlSessionFactory)。
  2. sqlSessionFactory创建sqlSession。SqlSessionFactory使用的是工厂模式来生成对应的sqlSession。sqlSession使用代理模式以适用于所有sql的解析。首先解析Mapper里的sql语句,接着使用preparedStatement(即jdbc)进行对数据库的访问。当我们使用Configuration的getMapper方法时,会调用mapperRegistry.getMapper方法,而该方法又会调用mapperProxyFactory.newInstance(sqlSession)来生成一个具体的代理,具体过程如下:
    这里有两个步骤,第一个是提前创建一个Proxy,第二个是使用的时候会自动请求Proxy,然后由Proxy来执行具体事务;
/**
 * @author Lasse Voss
 */
public class MapperProxyFactory<T> {
 
    private final Class<T> mapperInterface;
    private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<Method, MapperMethod>();
 
    public MapperProxyFactory(Class<T> mapperInterface) {
        this.mapperInterface = mapperInterface;
    }
 
    public Class<T> getMapperInterface() {
        return mapperInterface;
    }
 
    public Map<Method, MapperMethod> getMethodCache() {
        return methodCache;
    }
 
    @SuppressWarnings("unchecked")
    protected T newInstance(MapperProxy<T> mapperProxy) {
        return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface },
                mapperProxy);
    }
 
    public T newInstance(SqlSession sqlSession) {
        final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
        return newInstance(mapperProxy);
    }
 
}

在这里,先通过T newInstance(SqlSession sqlSession)方法会得到一个MapperProxy对象,然后调用T newInstance(MapperProxy mapperProxy)生成代理对象然后返回。

而查看MapperProxy的代码,可以看到如下内容:

public class MapperProxy<T> implements InvocationHandler, Serializable {
 
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try {
            if (Object.class.equals(method.getDeclaringClass())) {
                return method.invoke(this, args);
            } else if (isDefaultMethod(method)) {
                return invokeDefaultMethod(proxy, method, args);
            }
        } catch (Throwable t) {
            throw ExceptionUtil.unwrapThrowable(t);
        }
        final MapperMethod mapperMethod = cachedMapperMethod(method);
        return mapperMethod.execute(sqlSession, args);
    }

该MapperProxy类实现了InvocationHandler接口,并且实现了该接口的invoke方法。
通过这种方式,我们只需要编写Mapper.java接口类,当真正执行一个Mapper接口的时候,就会转发给MapperProxy.invoke方法,而该方法则会调用后续的sqlSession.cud>executor.execute>prepareStatement等一系列方法,完成SQL的执行和返回。

  1. 访问前其实会先访问缓存。mysql有一级缓存和二级缓存。一级缓存默认开启,范围为同一个xml(sqlSessionFactory)中。以及缓存其实就是一个HashMap的结构进行存储的。(可以看第2点的源码部分)二级缓存会设计所有的xml(sqlSessionFactory)。
 private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<Method, MapperMethod>();

mybatis的一级缓存和二级缓存?

链接

一级缓存基于sqlSession默认开启,在操作数据库时需要构造SqlSession对象,在对象中有一个HashMap用于存储缓存数据。当一个SqlSession结束后该SqlSession中的一级缓存也就不存在了。

二级缓存是mapper级别的缓存。使用二级缓存时,多个SqlSession使用同一个Mapper的sql语句去操作数据库,得到的数据会存在二级缓存区域,它同样是使用HashMap进行数据存储。相比一级缓存SqlSession,二级缓存的范围更大,多个Sqlsession可以共用二级缓存,二级缓存是跨SqlSession的。

二级缓存的作用域是mapper的同一个namespace。不同的sqlSession两次执行相同的namespace下的sql语句,且向sql中传递的参数也相同,即最终执行相同的sql语句,则第一次执行完毕会将数据库中查询的数据写到缓存,第二次查询会从缓存中获取数据,不再去底层数据库查询,从而提高效率。

在MyBatis配置文件(mybatis-config.xml)中开启二级缓存(详细过程自己百度搜索开启)//value属性默认为false在**Mapper.xml中开启当前mapper的namespace下的二级缓存代表创建了一个LRU缓存,并每隔60秒刷新,最大存储512个对象,而且返回的对象被认为是只读的。

mybatis中使用到的设计模式

mybatis中使用的设计模式
mybatis执行流程
建造者模式

mysql执行计划怎么看?

EXPLAIN select * from A where X=? and Y=?

在这里插入图片描述
possible_keys:表示查询时,可能使用的索引
key:表示实际使用的索引
key_len:索引字段的长度

ACID靠什么保证?

1

MySQL表删除数据,索引文件会不会变小?

链接
索引文件并不会变小,索引只会将数据清空,而不会释放空间。

简答的方法是重新建一个新的表,将旧数据复制到新表中,但是会存在一个问题,迁移表的过程中旧表如果有新的数据写入时,这些数据不会被迁移,会造成数据丢失。
解决方法:
,MySQL 5.6 版本开始引入 Online DDL, 目的的解决新建。
在生成临时文件过程中,如果有对表A做写操作,操作会记录到一个日志文件中,
当临时文件生成后,再重放日志文件,将操作应用到临时文件。

与新建表的最大区别,增加了日志文件记录和重放功能。迁移过程中,允许对表A做增删改操作。

数据库线程池,以及参数

将数据库连接作为对象存储在内存中,当用户需要访问数据库时,并非建立一个新的连接,而是从连接池中取出一个已建立的空闲连接对象。使用完毕后,用户也并非将连接关闭,而是将连接放回连接池中,以供下一个请求访问使用。而连接的建立、断开都由连接池自身来管理。

spring默认线程池:hikari
链接

  • minimum-idle: :最小空闲链接时间,是线程池启动的时候就会自动创建。
  • maximum-pool-size: 线程池最多可以创建的线程数量。
  • idle-timeout: 空闲链接存活最大时间,当minimum-idle < maximum-pool-size时候,该值才有效。大于minimum的连接对象空闲时间大于该值,则会被线程池注销掉。默认是10分钟
  • max-lifetime: 此属性控制池中连接的最长生命周期,0表示永久,默认30分钟
    hikari:
      pool-name: DateHikariCP
      # 最小空闲连接数
      minimum-idle: 5
      # 空闲链接存活最大时间,默认是10分钟(60000)
      idle-timeout: 1800000
      # 最大连接数,默认是10
      maximum-pool-size: 10
      # 从连接池返回的连接自动提交
      auto-commit: true
      # 连接最大存活时间,0表示永久,默认30分钟
      max-lifetime: 1800000
      # 连接超时时间,默认是30秒
      connection-timeout: 30000
      # 测试连接是否可用的查询语句
      connection-test-query: SELECT 1

数据库的三大范式

知乎
第一范式:属性不可分割。其实就是数据库一个字段对应一个数据
第二范式:第二范式就是要有主键,要求其他字段都依赖于主键。
第三范式就是要消除传递依赖,方便理解,可以看做是“消除冗余”。其实就是非主键之间不能存在依赖。

引用:
https://www.cnblogs.com/jieerma666/p/10805578.html

https://blog.csdn.net/yangguosb/article/details/81903583

https://zhuanlan.zhihu.com/p/149981715

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值