决战圣地玛丽乔亚Day21----Mysql事务相关及结构相关

算法:

组合问题的剪纸优化.

上次的组合问题,我们是对层数的每个元素遍历一遍,在某些情况下是没有必要的,例如,4个数取4个组合。那么我们的起始位置必须要满足个数的条件,那么从2开始取,最多取2,3,4三个数,不满足条件可以舍去。

 

 

 

接下来看一下优化过程如下:

  1. 已经选择的元素个数:path.size();

  2. 所需需要的元素个数为: k - path.size();

  3. 列表中剩余元素(n-i) >= 所需需要的元素个数(k - path.size())

  4. 在集合n中至多要从该起始位置 : i <= n - (k - path.size()) + 1,开始遍历

为什么有个+1呢,因为包括起始位置,我们要是一个左闭的集合。

举个例子,n = 4,k = 3, 目前已经选取的元素为0(path.size为0),n - (k - 0) + 1 即 4 - ( 3 - 0) + 1 = 2。

从2开始搜索都是合理的,可以是组合[2, 3, 4]。

这里大家想不懂的话,建议也举一个例子,就知道是不是要+1了。

所以优化之后的for循环是:

for (int i = startIndex; i <= n - (k - path.size()) + 1; i++) // i为本次搜索的起始位置

优化后的代码:

复制代码
class Solution {
private:
    vector<vector<int>> result;
    vector<int> path;
    void backtracking(int n, int k, int startIndex) {
        if (path.size() == k) {
            result.push_back(path);
            return;
        }
        for (int i = startIndex; i <= n - (k - path.size()) + 1; i++) { // 优化的地方
            path.push_back(i); // 处理节点
            backtracking(n, k, i + 1);
            path.pop_back(); // 回溯,撤销处理的节点
        }
    }
public:

    vector<vector<int>> combine(int n, int k) {
        backtracking(n, k, 1);
        return result;
    }
};
复制代码

LeetCode216.组合的总和:

找出所有相加之和为 n 的 k 个数的组合。组合中只允许含有 1 - 9 的正整数,并且每种组合中不存在重复的数字。

说明:

  • 所有数字都是正整数。
  • 解集不能包含重复的组合。

示例 1: 输入: k = 3, n = 7 输出: [[1,2,4]]

示例 2: 输入: k = 3, n = 9 输出: [[1,2,6], [1,3,5], [2,3,4]]

 

本题和上面的组合如出一辙。

手写的代码如下:

复制代码
package com.dwj;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

public class Main {
    LinkedList<Integer> path =  new LinkedList<>();
    List<List<Integer>> resultList  =  new ArrayList<>();
    int sum = 0;

    public static void main(String[] args) {
        Main main = new Main();
        List<List<Integer>> result =  main.combine(7,3);
        System.out.println(result);
    }


    public List<List<Integer>> combine(int n, int k) {
        backtracking(n,k,1);
        return resultList;
    }

    private void backtracking(int n, int k, int startIndex){
        //2.终止条件
        if(path.size()==k && sum == n){
            resultList.add(new ArrayList<>(path));
            return;
        }
        if(sum>n){
            return ;
        }
        //3.单层遍历逻辑
  // 也可以改为 if (path.size() > k) return; 执行效率上是一样的
        for(int i=startIndex; i<=9-(k-path.size())+1;i++){
            path.add(i);
            sum += i;
            backtracking(n,k,i+1);
            path.removeLast();
            sum -= i;
        }
    }

}
复制代码

需要注意的点是,满足条件返回resultList的时候,需要注意

resultList.add(new ArrayList<>(path))     不能直接   resultList.add(path)   由于这种写法,导致最终执行无效。

剪纸操作:

for(int i=startIndex; i<=9-(k-path.size())+1;i++)
k-path.size代表当前还需要多少个元素满足个数。 如果当前k=3 path= 1 k-path.size代表还需要两个元素才能满足个数的需求。
然后通过总数9-2 = 7 代表从7开始, [7,8,9]是最后的一个满足条件. +1是由于使用<=也就是是一个左闭右开
想不明白大不了不去剪纸,用最笨的方法,i<=9

——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————

为什么聚簇索引建议使用自增主键?

我觉得这段话说的很好:

上文讨论过InnoDB的索引实现,InnoDB使用聚集索引,数据记录本身被存于主索引(一颗B+Tree)的叶子节点上。这就要求同一个叶子节点内(大小为一个内存页或磁盘页)的各条数据记录按主键顺序存放,因此每当有一条新的记录插入时,MySQL会根据其主键将其插入适当的节点和位置,如果页面达到装载因子(InnoDB默认为15/16),则开辟一个新的页(节点)。

如果表使用自增主键,那么每次插入新的记录,记录就会顺序添加到当前索引节点的后续位置,当一页写满,就会自动开辟一个新的页。

这样就会形成一个紧凑的索引结构,近似顺序填满。由于每次插入时也不需要移动已有数据,因此效率很高,也不会增加很多开销在维护索引上。

如果使用非自增主键(如果身份证号或学号等),由于每次插入主键的值近似于随机,因此每次新纪录都要被插到现有索引页得中间某个位置。

此时MySQL不得不为了将新记录插到合适位置而移动数据,甚至目标页面可能已经被回写到磁盘上而从缓存中清掉,此时又要从磁盘上读回来,这增加了很多开销,同时频繁的移动、分页操作造成了大量的碎片,得到了不够紧凑的索引结构,后续不得不通过OPTIMIZE TABLE来重建表并优化填充页面。

因此,只要可以,请尽量在InnoDB上采用自增字段做主键。

Mysql的事务隔离级别有哪些?默认的隔离级别是什么?

mysql的事务隔离级别有:

1)UncommitedRead未提交读,可以读取到未提交的事务。 A事务可以读到B事务未提交的数据,B事务返回不想提交了,但是数据已经被A读到了,A读到的是脏数据。脏读现象出现。

2)Read-commited不可重复读,重复读结果不一样。 一个事务提交后,数据才能被其他事务看到。并且其他事务每次对数据进行修改和提交,都能读到最新数据。 A事务做两次查询,查询的途中B事务进行数据的修改,B事务在两次查询之间提交事务,因为只能读到提交的数据,所以A出现了两次查询不一致的结果。

3)RepeatedRead 可重复读(Mysql默认):在不可重复读的基础上,让两次读到的数据是一致的。但是可能存在幻读的问题,这个在下面进行介绍。
MVCC机制多版本并发控制:保证InnoDB下执行一致性读操作。
InnoDB会给每行增加两个隐藏字段实现MVCC,创建时间和过期时间,但是实际存的是版本号,每次开启事务版本号会增加。
Select操作是快照读读历史版本,其他操作是当前读。
select操作不会更新版本号,是快照读(历史版本);insert、update和delete会更新版本号,是当前读(当前版本)

4)Serializable:串行化,读写数据锁表。效率极低。

持久性和一致性的区别?和CAP一致性的区别?分布式事务是如何实现隔离级别的?

什么是幻读?是如何解决幻读的?

总体来说,幻读的产生是由于我们对某一区间进行读操作的过程中,被另一事物进行了改动导致两次读取内容不同。 注意这里讲的是范围。

在mysql中,对当前读和快照读进行了不同的处理解决了幻读的问题。(当前读:更新操作   快照读:普通select,不包括select for update这种上锁)

对于当前读:我们使用间隙锁,在读取某一行数据时,选取左右临界点,锁住当前区域的数据,如果有其他锁请求会被阻塞。

对于快照读:使用了mvcc多版本并发控制机制,通过记录版本号的方式保存数据。 如果是普通的select操作,我们读取最近一次的快照交给事务,如果是更新操作,需要更新版本号。

默认的事务隔离级别是RR。

更细致的部分参考:https://www.cnblogs.com/dwj-ngu/p/17141962.html

 

SQL优化部分:
1.explain

 explain  [sql]

参数含义:

 

 

 select_type:查询的类型

partitions:匹配的分区,分区表命中的分区情况,非分区表该字段为空。

type:

NULL > system > const > eq_ref > ref > ref_or_null > index_merge > range > index > ALL

NULL:  执行阶段无须访问表和索引,最快。不需要查表的数学计算等...

system:表只有一行数据,const类型的特例,可忽略

eq_ref: 连表查询,按链表的主键或唯一外键联合查询。连接起来是一对一

ref: 单表或多表链接查询,外键一对多

ref_or_null: ref的基础上可以搜索null值

index_merge:多索引联合查询最后去交并集

range:范围查询,=, <>, >, >=, <, <=, IS NULL, <=>, BETWEEN, IN()或者like等运算符的查询中。

index:遍历索引树,由于索引文件比数据文件小,所以比all快一点。
all:遍历所有数据。

 

possible_keys:这个表里面存在且可能会被使用的索引,可能会在这个字段下面出现,但是一般都以key为准

key:使用的索引,如果是覆盖索引,那么只会出现在key里面。
ken_len:索引长度(这个值越短越有利)

ref: 索引的哪一列被使用了

rows:返回结果行数

filtered:是查询的行数与总行数的比值

extra:这一字段包含不适合在其他列显示,但是也非常重要的额外信息

这一字段包含不适合在其他列显示,但是也非常重要的额外信息。

  • Using filesort 表示当SQL中有一个地方需要对一些数据进行排序的时候,优化器找不到能够使用的索引,所以只能使用外部的索引排序,外部排序就不断的在磁盘和内存中交换数据,这样就摆脱不了很多次磁盘IO,以至于SQL执行的效率很低。反之呢?由于索引的底层是B+Tree实现的,他的叶子节点本来就是有序的,这样的查询能不爽吗?

    EXPLAIN
    SELECT * FROM course AS C ORDER BY C.`name` 
     type    possible_keys  key     key_len  ref       rows  filtered  Extra           
     ------  -------------  ------  -------  ------  ------  --------  ----------------
     ALL     (NULL)         (NULL)  (NULL)   (NULL)      20    100.00  Using filesort  

    没有给C.name建立索引,所以在根据C.name排序的时候,他就使用了外部排序

  • Using tempporary 表示在对MySQL查询结果进行排序时,使用了临时表,,这样的查询效率是比外部排序更低的,常见于order bygroup by

    EXPLAIN
    SELECT C.`name` FROM course AS C GROUP BY C.`name`
    possible_keys  key     key_len  ref       rows  filtered  Extra                            
    -------------  ------  -------  ------  ------  --------  ---------------------------------
    (NULL)         (NULL)  (NULL)   (NULL)      20    100.00  Using temporary; Using filesort  

    上面这个查询就是同时触发了Using temporaryUsing filesort,可谓是雪上加霜。

  • Using index 表示使用了索引,很优秀👍。
  • Using where 使用了where但是好像没啥用。
  • Using join buffer 表明使用了连接缓存,比如说在查询的时候,多表join的次数非常多,那么将配置文件中的缓冲区的join buffer调大一些。
  • impossible where 筛选条件没能筛选出任何东西
  • distinct 优化distinct操作,在找到第一匹配的元组后即停止找同样值的动作

 

posted @   NobodyHero  阅读(6)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 25岁的心里话
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!
· 零经验选手,Compose 一天开发一款小游戏!
历史上的今天:
2017-02-27 枚举类.
点击右上角即可分享
微信分享提示