头像

大菜狗

太原理工大学




离线:1天前


最近来访(903)
用户头像
克里斯保罗
用户头像
feng0
用户头像
学不会就开摆
用户头像
Eliuak
用户头像
rarestark
用户头像
RyanMoriarty
用户头像
Acwer
用户头像
小郑同学
用户头像
tonngw
用户头像
patrick___666
用户头像
vlp
用户头像
whitetree
用户头像
琉璃_1
用户头像
JimmyHu
用户头像
杨淇
用户头像
crazycarry
用户头像
dong_
用户头像
wsh_
用户头像
白日做梦_1
用户头像
Pr


面试深入之Spring(SpringBoot)

Bean的生命周期

bean的创建

整体流程

spring会扫描指定路径的的包,看包下面的每一个类,如果类上面有相关注解,spring就会帮你创建这个类的bean对象,经过依赖注入,初始化前,初始化,AOP,一系列流程创建对象,并管理bean的生命周期

流程总结:

  1. 扫描包(指定包路径)
  2. 推断构造方法
  3. 用构造方法实例化对象(单例bean,即所有getbean的哈希值相同)
  4. 依赖注入
  5. 初始化前
  6. 执行初始化方法
  7. 初始化后(AOP)
  8. 生成代理对象
  9. 得到bean

构造方法的选取

bean要帮你创建对象,依据就是类的构造方法,spring默认用无参构造方法,当只有一个有参构造方法,就用这个,如果有多个有参构造方法,就会报错,这时要手动指定某个构造方法为默认构造方法,就是在方法前加上 @AutoWrite.

构造方法中参数的注入

入果参数传入的是对象,应该怎么注入?从spring对象池里找

spring对象池可以理解为1个map对象池里放这所有注册过的bean。找的时候先通过反射根据类型找,如果有多个 ,则根据参数名字去找

上面这个方法叫推断构造方法

AOP和代理对象

手写spring容器的思路

首先要实现一个根据类名,获取对象的getbean方法

如何获取到这些类

想想spring的实现方法,是通过在类上写一些注解。Spring在扫描包的时候,通过这些注解,识别这些类,并

AOP

Sping Bean

Bean的创建

@SpringBootTest
class JavaallApplicationTests {
    @Autowired
    ApplicationContext applicationContext;

    @Test
    void isBeanSingleton() {
        System.out.println(applicationContext.getBean("BeanTest"));
        BeanTest beanTest1 = (BeanTest) applicationContext.getBean("BeanTest");
        BeanTest beanTest2 = (BeanTest) applicationContext.getBean("BeanTest");


        BeanTest beanTest3 = new BeanTest();

        System.out.println("两个通过spring创建的bean:"+(beanTest1 == beanTest2));
        System.out.println("两个通过spring创建的bean的成员对象:"+(beanTest1.getBeanContent() == beanTest2.getBeanContent()));
        System.out.println("通过spring创建的bean和手动创建的对象:"+(beanTest1 == beanTest3));
        System.out.println("通过spring创建的bean和手动创建的对象的成员对象:"+(beanTest1.getBeanContent() == beanTest3.getBeanContent()));


    }
    @Test
    void testClone() throws IOException, ClassNotFoundException {
        BeanTest beanTest1 = new BeanTest();
        BeanTest beanTest2 = beanTest1.clone();
        BeanTest beanTest3 = (BeanTest) beanTest1.deepClone();

        System.out.println("浅拷贝的2个对象:"+(beanTest1 == beanTest2));
        System.out.println("浅拷贝的2个对象的int:"+(beanTest1.getAge()== beanTest2.getAge()));
        System.out.println("浅拷贝的2个对象的string:"+(beanTest1.getName()== beanTest2.getName()));
        System.out.println("浅拷贝的2个对象成员对象:"+(beanTest1.getBeanContent()==beanTest2.getBeanContent()));
        System.out.println("================================================================================");
        System.out.println("浅拷贝的2个对象:"+(beanTest1 == beanTest3));
        System.out.println("浅拷贝的2个对象的int:"+(beanTest1.getAge()== beanTest3.getAge()));
        System.out.println("浅拷贝的2个对象的string:"+(beanTest1.getName()== beanTest3.getName()));
        System.out.println("浅拷贝的2个对象成员对象:"+(beanTest1.getBeanContent()==beanTest3.getBeanContent()));
    }

}

结果:

两个通过spring创建的bean:true
两个通过spring创建的bean的成员对象:true
通过spring创建的bean和手动创建的对象:false
通过spring创建的bean和手动创建的对象的成员对象:false

Bean的注册

beanDefinitionMap

SpringBoot中@Component是如何生效的

SpringBoot帮助Spring解决了哪些问题

1.统一的依赖版本管理

pom.xml里点开

<artifactId>spring-boot-starter-parent</artifactId>

再点开里面的

<artifactId>spring-boot-dependencies</artifactId>

就可以看到

其对开发场景中常用框架的依赖文件做了统一版本号管理,所以被管理的这些依赖在pom.xml中引入是不需要写版本号

形如:

<artifactId>spring-boot-starter-xxx</artifactId>

就是官方提供的依赖启动器

即提供了官方认为的开发者在该场景业务开发中需要的依赖统一配置

2.自动配置

回忆ssm阶段,当我们想使用第三方依赖,如mybatis ,需要在xml中自己进行手动配置,把这个依赖交给spring管理

   <!-- 配置整合mybatis -->
    <!-- 1.关联数据库文件 -->
    <context:property-placeholder location="classpath:database.properties"/>

    <!-- 2.数据库连接池 -->
    <!--数据库连接池
        dbcp 半自动化操作 不能自动连接
        c3p0 自动化操作(自动的加载配置文件 并且设置到对象里面)
    -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <!-- 配置连接池属性 -->
        <property name="driverClass" value="${jdbc.driver}"/>
        <property name="jdbcUrl" value="${jdbc.url}"/>
        <property name="user" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>

        <!-- c3p0连接池的私有属性 -->
        <property name="maxPoolSize" value="30"/>
        <property name="minPoolSize" value="10"/>
        <!-- 关闭连接后不自动commit -->
        <property name="autoCommitOnClose" value="false"/>
        <!-- 获取连接超时时间 -->
        <property name="checkoutTimeout" value="10000"/>
        <!-- 当获取连接失败重试次数 -->
        <property name="acquireRetryAttempts" value="2"/>
    </bean>

    <!-- 3.配置SqlSessionFactory对象 -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!-- 注入数据库连接池 -->
        <property name="dataSource" ref="dataSource"/>
        <!-- 配置MyBatis全局配置文件:mybatis-config.xml -->
        <property name="configLocation" value="classpath:mybatis-config.xml"/>

    </bean>

可以想像,我们开发一个业务,需要的依赖很多,如果每一个都需要这么配置的话,将会使开发变的异常麻烦

于是对于这些依赖,spring官方将这些配置帮我们搞好了,无须我们再自己手写这些配置

我们只需要对我们自己写的代码进行配置即可

3.由xml配置文件变为注解+ymal

注:springboot中仍然可以用xml配置,只是现在的开发习惯不用而已,并不是不能用

xml中仍然有许多重复的代码,springboot将配置方式再次简化,用注解实现类的注册




面试深入之mysql

mysql中的专有名词解释

  1. 脏页:当内存数据页跟磁盘数据页内容不一致的时候,我们称这个内存页为“脏页”。
  2. 干净页:内存数据写入到磁盘后,内存和磁盘上的数据页的内容就一致了,称为“干净页”。
  3. redo log:重做日志,确保事务的持久性。防止在发生故障的时间点,尚有脏页未写入磁盘,在重启mysql服务的时候,根据redo log进行重做;redo log通常是物理日志,记录的是数据页的物理修改,而不是某一行或某几行修改成怎样怎样,它用来恢复提交后的物理数据页(恢复数据页,且只能恢复到最后一次提交的位置)。
  4. undo log:回滚日志,保存了事务发生之前的数据的一个版本,可以用于回滚,同时可以提供多版本并发控制下的读(MVCC),也即非锁定读;undo log一般是逻辑日志,根据每行记录进行记录。
  5. binlog:归档日志,二进制日志,用于复制,在主从复制中,从库利用主库上的binlog进行重播,实现主从同步。 用于数据库的基于时间点的还原。
  6. 聚集(簇)索引:数据行的物理顺序与列值(一般是主键的那一列)的逻辑顺序相同,一个表中只能拥有一个聚集索引。索引的叶子节点就是对应的数据节点
  7. 非聚集索引:该索引中索引的逻辑顺序与磁盘上行的物理存储顺序不同,一个表中可以拥有多个非聚集索引。非聚集索引叶节点仍然是索引节点,只是有一个指针指向对应的数据块,此如果使用非聚集索引查询,而查询列中包含了其他该索引没有覆盖的列,那么他还要进行第二次的查询,查询节点上对应的数据行的数据。
  8. 慢查询:,全名是慢查询日志,是MySQL提供的一种日志记录,用来记录在MySQL中响应时间超过阀值的语句。

Mysql体系结构

组成

  1. 连接池组件
  2. 管理服务和工具组件
  3. SQL接口组件
  4. 查询分析器组件

  5. 优化器组件

  6. 缓冲(Cache)组件,
  7. 插件式存储引擎。
  8. 物理文件

MySQL存储引擎

背景

MySQL5对其结构体系做了较大的改造,并引入了一个新的概念:插件式存储引擎体系结构

存储引擎层和sql 层各自更为独立,耦合更小,甚至可以做到在线加载新的存储引擎,也就是完全可以将一个新的存
储引擎加载到一个正在运行的MySQL 中,而不影响MySQL 的正常运行。

插件式存储引擎的架构,为存储引擎的加载和移出更为灵活方便,也使自行开发存储引擎更为方便简单

MySQL 的插件式存储引擎主要包括MyISAM,Innodb,NDB Cluster,Maria,Falcon,Memory,Archive,Merge,Federated 等,其中最著名而且使用最为广泛的MyISAM 和Innodb两种存储引擎。

MyISAM

MyISAM的文件结构

MyISAM 存储引擎的表在数据库中,每一个表都被存放为三个以表名命名的物理文件

  1. 存放表结构定义信息的.frm 文件
  2. 存放了表的数据的.MYD文件
  3. 存放索引数据的.MYI文件

每个表都有且仅有这样三个文件做为MyISAM 存储类型的表的存储,也就是说不管这个表有多少个索引,都是存放在
同一个.MYI文件中。

MyISAM的索引类型

1.B-Tree索引

B-Tree 索引,顾名思义,就是所有的索引节点都按照balance tree 的数据结构来存储,所有的索引数据节点都在叶节点。

那就是参与一个索引的所有字段的长度之和不能超过1000 字节

2.R-Tree 索引

R-Tree 索引的存储方式和b-tree 索引有一些区别,主要设计用于为存储空间和多维数据的字段做索引,所以目前的MySQL 版本来说,也仅支持geometry 类型的字段作索引。

3.Full-text 索引

Full-text 索引就是我们长说的全文索引,他的存储结构也是b-tree。主要是为了解决在我们需要用like 查询的低效问题。

最经常使用的就是B-Tree 索引了,偶尔会使用到Fulltext,但是R-Tree 索引一般系统中都是很少用到的。另外MyISAM 的B-Tree 索引有一个较大的限制,那就是参与一个索引的所有字段的长度之和不能超过1000 字节。

MyISAM的索引存储格式

MyISAM 的数据存放格式是分为静态固定长度(FIXED)、动态可变长度(DYNAMIC)以及压缩(COMPRESSED)这三种格式

虽然每一个MyISAM 的表都是存放在一个相同后缀名的.MYD 文件中,但是每个文件的存放格式实际上可能并不是完全一样的

三种格式中是否压缩是完全可以任由我们自己选择的,可以在创建表的时候通过ROW_FORMAT 来指定{COMPRESSED | DEFAULT},也可以通过myisampack 工具来进行压缩,默认是不压缩的

在非压缩的情况下,是静态还是动态,就和我们表中个字段的定义相关了。只要表中有可变长度类型的字段存在,那么该表就肯定是DYNAMIC 格式的,如果没有任何可变长度的字段,则为FIXED 格式

MyISAM引擎的缺点

MyISAM 存储引擎的某个表文件出错之后,仅影响到该表,而不会影响到其他表,更不会影响到其他的数据库

如果我们的出据苦正在运行过程中发现某个MyISAM 表出现问题了,则可以在线通过check table 命令来尝试校验他,并可以通过repairtable命令来尝试修复

Innodb 存储引擎

特点

1、支持事务安装

2、数据多版本读取

Innodb 在事务支持的同时,为了保证数据的一致性已经并发时候的性能,通过对undo信息,实现了数据的多版本读取。

3、锁定机制的改进

Innodb 改变了MyISAM 的锁机制,实现了行锁(MyISAM为表锁)。Innodb 的行锁机制的实现是通过索引来完成的

4、实现外键

innodb的文件结构

分为两大部分

数据文件(存放表数据和索引数据)

存放数据表中的数据和所有的索引数据,包括主键和其他普通索引

Innodb 的表空间分为两种形式

一种是共享表空间,也就是所有表和索引数据被存放在同一个表空间(一个或多个数据文件)中,通过innodb_data_file_path 来指定,增加数据文件需要停机重启。

另外一种是独享表空间,也就是每个表的数据和索引被存放在一个单独的.ibd 文件中

注意事项

1.我们可以自行设定使用共享表空间还是独享表空间来存放我们的表

2.共享表空间都是必须存在的,因为Innodb 的undo 信息和其他一些元数据信息都是存放在共享表空间里面的

3.共享表空间的数据文件是可以设置为固定大小和可自动扩展大小两种形式的,自动扩展形式的文件可以设置文件的最大大小和每次扩展量

4.Innodb 不仅可以使用文件系统,还可以使用原始块设备,也就是我们常说的裸设备。

5.当我们的文件表空间快要用完的时候,我们必须要为其增加数据文件,当然,只有共享表空间有此操作

6.Innodb 在创建新数据文件的时候是不会创建目录的,如果指定目录不存在,则会报错并无法启动。

7.Innodb 在给共享表空间增加数据文件之后,必须要重启数据库系统才能生效,如果是使用裸设备,还需要有两次重启。

日志文件

特点

1.Innodb 的日志文件和Oracle 的redo 日志比较类似,同样可以设置多个日志组(最少2个),同样采用轮循策略来顺序的写入,甚至在老版本中还有和Oracle 一样的日志归档特性。

2.由于Innodb 是事务安全的存储引擎,所以系统Crash 对他来说并不能造成非常严重的损失,

3.由于有redo 日志的存在,有checkpoint 机制的保护,Innodb 完全可以通过redo 日志将数据库Crash 时刻已经完成但还没有来得及将数据写入磁盘的事务恢复,也能够将所有部分完成并已经写入磁盘的未完成事务回滚并将数据还原。

4.Innodb 的所有参数基本上都带有前缀“innodb_”,不论是innodb 数据和日志相关,还是其他一些性能,事务等等相关的参数都是一样。

Innodb体系架构详解

InnoDB有多个内存块,你可以认为这些内存块组成了一个大的内存池,负责如下工作:

  1. 维护所有进程/线程需要访问的多个内部数据结构。
  2. 缓存磁盘上的数据,方便快速地读取,并且在对磁盘文件的数据进行修改之前在这里缓存。
  3. 重做日志 (redo log)缓冲。

image-20220326104828327.png

后台线程

查看后台线程信息

show engine innodb status

内存

InnoDB存储引擎内存由以下几个部分组成:

  1. 缓冲池(buffer pool)、
  2. 重做日志缓冲池(redo log buffer)
  3. 额外的内存池(additional memory pool),分别由配置文件中的参数innodb_buffer_pool_size和innodb_log_buffer_size的大小定。

缓冲池是占最大块内存的部分,用来存放各种数据的缓存。

InnoDB的存储引擎的工作方

因为InnoDB的存储引擎的工作方式总是将数据库文件按页(每页16K)读取到缓冲池然后按最近最少使用(LRU)的算法来保留在缓冲池中的缓存数据。如果数据库文件需要修改,总是首先修改在缓存池中的页(发生修改后,该页即为脏页),然后再按照一定的频率将缓冲池的脏页刷新(flush)到文件

innodb存储引擎内存结构.png

在InnoDB存储引擎中,对内存的管理是通过一种称为内存堆(heap)的方式进行的。在对一些数据结构本身分配内存时,需要从额外的内存池中申请,当该区域的内存不够时,会从缓冲池中申请。InnoDB实例会申请缓冲池(innodb_buffer_pool)的空间,但是每个缓冲池中的帧缓冲(frame buffer)还有对应的缓冲控制对象(buffercontrol block),而且这些对象记录了诸如LRU、锁、等待等方面的息,而这个对象的内存需要从额外内存池中申请。因此,当你申请了很大的InnoDB缓冲池时,这个值也应该相应增加。

在InnoDB存储引擎中,对内存的管理是通过一种称为内存堆(heap)的方式进行的。在对一些数据结构本身分配内存时,需要从额外的内存池中申请,当该区域的内存不够时,会从缓冲池中申请。InnoDB实例会申请缓冲池(innodb_buffer_pool)的空间,但是每个缓冲池中的帧缓冲(frame buffer)还有对应的缓冲控制对象(buffercontrol block),而且这些对象记录了诸如LRU、锁、等待等方面的信息,而这个对象的内存需要从额外内存池中申请。因此,当你申请了很大的InnoDB缓冲池时,额外内存池也应该相应增加。

主线程

InnoDB存储引擎的主要工作都是在一个单独的后台线程master thread中完成的

master thread的线程优先级别最高。其内部由几个循环(loop)组成:主循环(loop)后台循环 (background loop)、刷新循环(flush loop)、暂停循环(suspend loop)。masterthread会根据数据库运行的状态在loop、background loop、 flush loop和suspend loop中进行切换。

循环中每秒一次的操作包括:

日志缓冲刷新到磁盘,即使这个事务还没有提交(总是)。

即使某个事务还没有提交,InnoDB存储引擎仍然会每秒将redo log缓冲中的内容刷新到redo log文件,这就是为什么再大的事务也会提交的很快

合并插人缓冲(可能)。

合并插人缓冲(insert buffer)并不是每秒都发生。InnoDB存储引擎会判断当前一秒内发生的IO次数是否小于5次,如果小于5次,InnoDB认为当前的IO压力很小,可以执行合并插人缓冲的操作。

至多刷新100个InnoDB的缓冲池中的脏页到磁盘(可能)。

刷新100个脏页也不是每秒都在发生。InnoDB存储引擎通过判断当前缓冲池中脏页的比例(buf_get_modified_ratio_pct)是否超过了配置文件中innodb.maxdirty_pages_pct这个参数(默认为90,代表90%),如果超过了这个阈值,InnoDB存储引擎认为需要做磁盘同步操作,将100个脏页写人磁盘。

如果当前没有用户活动,切换到background loop(可能)。

若当前没有用户活动(数据库空闲时)或者数据库关闭时,就会切换到这个循环。这个循环会执行以下操作:
删除无用的Undo页 (总是)。
合并20个插人缓冲(总是)。
跳回到主循环 (总是)。
不断刷新100个页,直到符合条件(可能,跳转到flush loop中完成)。

两次写

如果说插人缓冲带给InnoDB存储引擎的是性能,那么两次写带给InnoDB存储引擎的是数据的可靠性。当数据库宕机时,可能发生数据库正在写一个页面,而这个页只写了一部分(比如16K的页,只写前4K的页)的情况,我们称之为部分写失效(partial page write)。在InnoDB存储引擎未使用double write技术前,曾出现过因为部分写失效而导致数据失的情况。

innodb的索引

b+树索引

b+树的结构及特点

1、b+树其实有点类似于线段树,非叶子节点中存放的是子树中某一段的头指针

2、b+树的子树间无高度差,所有叶子节点都在同一层,且用一个双向指针连接,且是严格按照主键从左到右排序;且每个叶子节点都是大小相同的页

b+树的插入操作

b+树的插入.png

这里总结一下:

1、首先,叶子节点的页大小是固定的,每页存放的数据的条数也是固定的

2、当要插入的那一页,页满的时候,先看看左边相邻的页有没有空间,有的话进行旋转,没有的话再考虑拆页

一个案例,插入

插入前:

b+树插入案例插入前.png

插入70(旋转):

b+树插入时旋转.png

插入95(拆页):

b+树插入时拆页.png

聚集索引

聚集索引.png

自适应哈希索引

InnoDB存储引擎会监控对表上索引的查找,如果观察到建立哈希索引可以带来速度的提升,则建立哈希索引,所以称之为自适应(adaptive)的。自适应哈希索引通过缓冲池
的B+树构造而来,因此建立的速度很快。而且不需要将整个表都建哈希索引,InnoDB存储引擎会自动根据访问的频率和模式来为某些页建立哈希索引。

MySQL安全管理

数据库系统安全的三大防线

1.网络

2.主机

3.数据库

4.代码

权限系统

相关权限信息主要存储在几个被称为granttables 的系统表中,即: mysql.Usermysql.dbmysql.Hostmysql.table_privmysql.column_priv

由于权限信息数据量比较小,而且访问又非常频繁,所以Mysql 在启动的时候,就会将所有的权限信息都Load 到内存中保存在几个特定的结构中

权限的5大级别

  1. Global Level:全局权限控制,所有权限信息都保存在mysql.user 表中。Global Level 的所有权限都是针对整个mysqld 的,对所有的数据库下的所有表及所有字段都有效。
  2. Database Level:Database Level 是在Global Level 之下,其他三个Level 之上的权限级别,其作用域
    即为所指定整个数据库中的所有对象
  3. Table Level:Table Level 的权限作用范围是授权语句中所指定数据库的指定表
  4. Column Level
  5. Routine Level

访问控制实现原理

用户想要访问数据库,必须提供:主机名或ip地址用户名密码

如果要通过localhost 访问的话,必须要有一条专门针对localhost 的授权信息,即使不对任何主机做限制也不行

如果MySQL 正在运行之中的时候,我们对系统做了权限调整,那调整之后的权限什么时
候会生效呢?

Mysql数据备份

MyISAM 存储引擎的数据备份

1.MyISAM 存储引擎文件的物理文件比较集中,而且不支持事务没有redo和undo 日志,对数据一致性的要求也并不是特别的高,所以MyISAM 存储引擎表的物理备份也比较简单,只要将MyISAM 的物理文件copy 出来即可

2.MyISAM存储引擎的同一个表的数据文件和索引文件之间是有一致性要求的。

3.当MyISAM 存储引擎发现某个表的数据文件和索引文件不一致的时候,会标记该表处于不可用状态,并要求你进行修复动作,

4.我们自己必须至少保证数据库在备份时候的数据是处于某一个时间点的,这样就要求我们必须做到在备份MyISAM 数据库的物理文件的时候让MyISAM 存储引擎停止写操作,仅仅提供读服务,其根本实质就是给数据库表加锁来阻止写操作

5.MySQL 自己提供了一个使用程序mysqlhotcopy,这个程序就是专门用来备份MyISAM 存储引擎的

Innodb 存储引擎的数据备份

1.Innodb 存储引擎由于是事务性存储引擎有redo 日志和相关的undo 信息,而且对数据的一致性和完整性的要求也比MyISAM 要严格很多,所以Innodb 的在线(热)物理备份要比MyISAM 复杂很多,一般很难简单的通过几个手工命令来完成,大都是通过专门的Innodb
在线物理备份软件来完成

2.Innodb 存储引擎的开发者(Innobase 公司)开发了一款名为ibbackup 的商业备份软件,专门实现Innodb 存储引擎数据的在线物理备份功能。该软件可以在MySQL 在线运行的状态下,对数据库中使用Innodb 存储引擎的表进行备份,不过仅限于使用Innodb 存储引擎的表。

影响MySQL性能的相关因素

适合利用Cache提前缓存数据的场景

  1. 系统各种配置及规则数据;
  2. 活跃用户的基本信息数据;
  3. 活跃用户的个性化定制信息数据;
  4. 准实时的统计信息数据;
  5. 其他一些访问频繁但变更较少的数据;

合理的sql语句的应用

一个经典的对比案例

在我们的示例网站系统中,现在要实现每个用户查看各自相册列表(假设每个列表显示10 张相片)
的时候,能够在相片名称后面显示该相片的留言数量。这个需求大家认为应该如何实现呢?

方案一:

1、通过

SELECT id,subject,url FROM photo WHERE user_id = ? limit 10

得到第一页的相片相关信息;

2、通过第1 步结果集中的10 个相片id 循环运行十次

SELECT COUNT(*) FROM photo_commentWHERE photh_id = ?

来得到每张相册的回复数量然后再瓶装展现对象。

方案二:

1、通过

SELECT id,subject,url FROM photo WHERE user_id = ? limit 10

得到第一页的相片相关信息;

2、通过程序拼装上面得到的10 个photo 的id,再通过in 查询

SELECT photo_id,count(*) FROM photo_comment WHERE photo_id in (拼装后的id) GROUP BY photo_id

一次得到10 个photo 的所有回复数量,再组装两个结果集得到展现对象。

两种方案的复杂度对比
  1. 从MySQL 执行的SQL 数量来看,第一种解决方案为11(1+10=11)条SQL 语句,第二种解决方案
    为2 条SQL 语句(1+1);
  2. 从应用程序与数据库交互来看,第一种为11 次,第二种为2 次;
  3. 从数据库的IO 操作来看,简单假设每次SQL 为1 个IO,第一种最少11 次IO,第二种小于等于11
    次IO,而且只有当数据非常之离散的情况下才会需要11 次;
  4. 从数据库处理的查询复杂度来看,第一种为两类很简单的查询,第二种有一条SQL 语句有GROUP
    BY 操作,比第一种解决方案增加了了排序分组操作;
  5. 从应用程序结果集处理来看,第一种11 次结果集的处理,第二中2 次结果集的处理,但是第二种
    解决方案中第二词结果处理数量是第一次的10 倍;
  6. 从应用程序数据处理来看,第二种比第一种多了一个拼装photo_id 的过程。
消耗性能上的对比
  1. 由于MySQL 对客户端每次提交的SQL 不管是相同还是不同,都需要进行完全解析,这个动作主要
    消耗的资源是数据库主机的CPU,那么这里第一种方案和第二种方案消耗CPU 的比例是11:2。SQL 语句的
    解析动作在整个SQL 语句执行过程中的整体消耗的CPU 比例是较多的;
  2. 应用程序与数据库交互所消耗的资源基本上都在网络方面,同样也是11:2;
  3. 数据库IO 操作资源消耗为小于或者等于1:1;
  4. 第二种解决方案需要比第一种多消耗内存资源进行排序分组操作,由于数据量不大,多出的消耗
    在语句整体消耗中占用比例会比较小,大概不会超过20%,大家可以针对性测试;
  5. 结果集处理次数也为11:2,但是第二中解决方案第二次处理数量较大,整体来说两次的性能消
    耗区别不大;
  6. 应用程序数据处理方面所多出的这个photo_id 的拼装所消耗的资源是非常小的,甚至比应用程
    序与MySQL 做一次简单的交互所消耗的资源还要少。
总结

综合上面的这6 点比较,我们可以很容易得出结论,从整体资源消耗来看,第二中方案会远远优于第一种解决方案。

而在实际开发过程中,我们的程序员却很少选用。主要原因其实有两个,

一个是第二种方案在程序代码实现方面可能会比第一种方案略为复杂,尤其是在当前编程环境中面向对象思想的普
及,开发工程师可能会更习惯于以对象为中心的思考方式来解决问题。

还有一个原因就是我们的程序员可能对SQL 语句的使用并不是特别的熟悉,并不一定能够想到第二条SQL 语句所实现的功能。对于

探究sql语句对性能的影响

当MySQL Server 的连接线程接收到Client 端发送过来的SQL 请求之后,会经过一系列的分解Parse,进行相应的分析。然后,MySQL 通过查询优化器模块(Optimizer)根据该SQL 所设涉及到的数据表的相关统计信息进行计算分析,然后再得出一个MySQL 认为最合理最优化的数据访问方式,也就是我们常说的“执行计划”,然后再根据所得到的执行计划通过调用存储引擎借口来获取相应数据。然后再将存储引擎返回的数据进行相关处理,并以Client 端所要求的格式作为结果集返回给Client 端的应用程序。

两种锁的级别

InnoDB存储引擎实现了如下两种标准的行级锁:

  1. 共享锁 (S Lock),允许事务读一行数据。
  2. 排他锁 (X Lock),允许事务删除或者更新一行数据。

当一个事务已经获得了行r的共享锁,那么另外的事务可以立即获得行r的共享锁,因为读取并没有改变行r的数据,我们称这种情况为锁兼容。但如果有事务想获得行r的排他
锁,则它必须等待事务释放行r上的共享锁这种情况我们称为锁不兼容。

意向锁

InnoDB存储引擎支持多粒度锁定,这种锁定允许在行级上的锁和表级上的锁同时存在。为了支持在不同粒度上进行加锁操作,InnoDB存储引擎支持一种额外的锁方式,我们称之
为意向锁。意向锁是表级别的锁,其设计目的主要是为了在一个事务中揭示下一行将被请求的锁的类型。InnoDB存储引擎支持两种意向锁:

  1. 意向共享锁 (IS Lock),事务想要获得一个表中某几行的共享锁。
  2. 意向排他锁 (IX Lock),事务想要获得一个表中某几行的排他锁。

因为InnoDB存储引擎支持的是行级别的锁,所以意向锁其实不会阻塞除全表扫以外的任何请求。

查看锁的信息

版本的InnoDB Plugin中,在INFORMATION_SCHEMA架构下添加了INNODB_TRX、INNODB_LOCKS、INNODB_LOCK_WAITS。通过这三张表,可以更简单地监控当前的
事务并分析可能存在的锁的问题。通过实例我们来分析这三张表,先看表INNODB_TRX,

INNODB_TRX由8个字段组成:

  1. trx_id:InnoDB存储引擎内部唯一的事务ID。
  2. trx_state:当前事务的状态。
  3. trx_started:事务的开始时间。
  4. trx_requested_lock_id:等待事务的锁ID。如trx_state的状态为LOCK WAIT,那么该值代表当前的事务等待之前事务占用锁资源的ID。若trx_state不是LOCK WAIT,则该值为NULL。
  5. trx_wait_started:事务等待开始的时间。
  6. trx_weight:事务的权重,反映了一个事务修改和锁住的行数。在InnoDB存储引擎中,当发生死锁需要回滚时,InnoDB存储引擎会选择该值最小的进行回滚。
  7. trx_mysql_thread_id:MySQL中的线程ID,SHOW PROCESSLIST显示的结果。
  8. trx_query:事务运行的SQL语句。在实际使用中发现,该值有时会显示为NULL(不知道是不是Bug)。

INNODB_LOCK_WAITS由4个字段组成:

  1. requesting_trx_id:申请锁资源的事务ID.
  2. requesting_lock_id:申请的锁的ID。
  3. blocking_trxid:阻塞的事务ID。
  4. blocking_trx_id:阻塞的锁的ID。

一致性非锁定读

一致性的非锁定行读(consistent nonlocking read)是指InnoDB存储引擎通过行多版本控制(multi versioning)的方式来读取当前执行时间数据库中行的数据。如果读取的行正
在执行DELETE、UPDATE操作,这时读取操作不会因此而会等待行上锁的释放,相反,InnoDB存储引擎会去读取行的一个快照数据

快照数据是指该行之前版本的数据该实现是通过Undo段来实现。而Undo用来在事务中回滚数据,因此快照数据本身是没有额外的开销。此外,读取快照数据是不需要上锁的,因为没有必要对历史的数据进行修改。

可以看到,非锁定读的机制大大提高了数据读取的并发性,在InnoDB存储引擎默认设置下,这是默认的读取方式,即读取不会占用和等待表上的锁。但是在不同事务隔离级别
下,读取的方式不同,并不是每个事务隔离级别下读取的都是一致性读。同样,即使都是使用一致性读,但是对于快照数据的定义也不相同。

快照

快照数据其实就是当前行数据之前的历史版本,可能有多个版本。一个行可能有不止一个快照数据。我们称这种技术为行多版本技术。由此带来的并发控制,称之为多版本并发控制(MultiVersionConcurrencyControl,MVCC)。

不同事务隔离级别下的快照

在Read Committed和Repeatable Read(InnoDB存储引擎的默认事务隔离级别)下,InnoDB存储引擎使用非锁定的一致性读。

在ReadCommitted事务隔离级别下,对于快照数据,非一致性读总是读取被锁定行的最新一份快照数据,这其实不太符合事务的隔离性原则。

在Repeatable事务隔离级别下和Repeatable Read事务隔离级别下,对于快照数据,非一致性读总是读取事务开始时的行数据版本

自增长和锁

从MySQL 5.1.22版本开始,InnoDB存储引擎中提供了一种轻量级互斥量的自增长实现机制,这种机制大大提高了自增长值插人的性能。InnoDB
存储引擎提供了一个参数innodb_autoinc_lock_mode,默认值为1。

另外,InnoDB存储引擎下,自增长值的列必须是索引,并且是索引的第一个列,如果是第二个列则会报错;而MyISAM存储引擎则没有这个问题

innodb_autoinc_lock_mode=1:·。对于在插入前就能确定行数的语句(Simple inserts),该值会用互斥量(mutex)去对内存中的计数器进行累加的操作

对于插入前不能得到确定行数的语句(Bulk inserts),还是使用传统表锁的AUTO-INC Locking方式。这样做,如果不考虑回滚操作,对于自增值的增长还是连续的。而且在这种方式下Statement-Based方式的Replication还是能很好地工作。需要注意的是,如果已经使用表锁(AUTO-INC Locing)的方式产生自增长的值,而这时需要再进行“Simple inserts”的操作时,还是要等待AUTO-INC Locking的释放。

innodb_autoinc_lock_mode=2在这个模式下,对于所有“INSERT-like”自增长值的产生都是通过互斥量,而不是AUTO-INC Locking的方式。显然,这是最高性能的方式。然而,这带来一定的问题。因为并发插人的存在,所以每次插人时,自增长的值可能不是连续的。此外,最重要的是,基于Statement-Base Replication会出现问题。因此,使用这个模式,任何时候都应该使用Row-Base Replication。这样才能保证最大的并发性能和Replication数据的同步。

外键和锁

前面已经介绍了外键,外键主要用于引用完整性的约束检查。在InnoDB存储引擎中,对于一个外键列,如果没有显式地对这个列加索引,InnoDB存储引擎自动对其加一个索引
因为这样可以避免表锁这比Oracle做得好,Oracle不会自动添加索引,用户必须自己手工添加,这也是导致很多死锁问题产生的原因。

对于外键值的插入或者更新,首先需要查询父表中的记录,即SELECT父表。但是对于父表的SELECT操作,不是使用一致性非锁定读的方式,因为这样会发生数据不一致的
问题,因此这时使用的是SELECT…LOCK IN SHARE MODE方式,主动对父表加一个S锁。如果这时父表上已经这样加X锁,那么子表上的操作会被阻塞,

锁的算法

InnoDB存储引擎有3中行锁的算法设计,分别是:

  1. Record Lock:单个行记录上的锁。
  2. Gap Lock:间隙锁,锁定一个范围,但不包含记录本身。
  3. Next-Key Lock:Gap Lock + Record Lock,锁定一个范围,并且锁定记录本身。

例子

对于:

select * from sys_user where user_id = 5 lock in share mode

如果主键5,前面的数字是1,间隙锁会锁住(1,5);不包括1,5,行锁会锁住1

Record Lock总是会去锁住索引记录。如果InnoDB存储引擎表建立的时候没有设置任何一个索引,这时InnoDB存储引擎会使用隐式的主键来进行锁定。

Next-Key Lock是结合了Gap Lock和Record Lock的一种锁定算法,在Next-Key Lock算法下,InnoDB对于行的查询都是采用这种锁定算法。对于不同SQL查询语句,可能设置共
享的(Share)Next-Key Lock和排他的(exlusive)Next-Key Lock。

锁的一些问题

丢失更新

丢失更新(lost update)是一个经典的数据库问题。实际上,所有多用户计算机系统
环境下有可能产生这个问题。简单说来,出现下面的情况时,就会发生丢失更新:
(1)事务T1查询一行数据,放人本地内存,并显示给一个终端用户User1。
(2)事务T2也查询该行数据,并将取得的数据显示给终端用户User2。
(3)User1修改这行记录,更新数据库并提交。
(4)User2修改这行记录,更新数据库并提交。
显然,这个过程中用户User1的修改更新操作“丢失”了。即第一次更新的值被第二次更新覆盖了

想要解决,就得把更新的行加一个排他锁

脏读

理解脏读之前,需要理解脏数据的概念。脏数据和脏页有所不同。

脏页指的是在缓冲池中已经被修改的页,但是还没有刷新到磁盘,即数据库实例内存中的页和磁盘的页中的数据是不一致的,当然在刷新到磁盘之前,日志都已经被写人了重做日志文件中。

而所谓脏数据,是指在缓冲池中被修改的数据,并且还没有被提交(commit)。对于脏页的读取,是非常正常的。脏页是因为数据库实例内存和磁盘的异步同步造成的,这并不影响数据的一致性。并且因为是异步的,因此可以带来性能的提高。而脏数据却不同,脏数据是指未提交的数据。如果读到了脏数据,即一个事务可以读到另外一个事务中未提交的据,则显然违反了数据库的隔离性。脏读指的就是在不同的事务下,可以读到另外事务未提交的数据,简单来说,就是可以读到脏数据。比如下面的例子所示:

不可重复读

不可重复读是指在一个事务内多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据。那么,在第一个事务的两次读数据之间,由于第二个事务的修改,第一个事务两次读到的数据可能是不一样的。这样就发生了在一个事务内两次读到的数据是不一样的,因此称为不可重复读。

可重复读和脏读的区别是:脏读是读到未提交的数据;而不可重复读读到的确实是已经提交的数据,但是其违反了数据库事务一致性的要求。




面试深入之java多线程

使用多线程的方式

1.继承Thread类

缺点:java语言不支持多继承

Thread类底层也是实现的Rannable,执行run方法的调用顺序与其执行顺序无关

2.实现Runnable接口

在run方法前添加 synchronized关键字,可以使多个线程在执行 run 方法时,以排队的方式进行处理。

多线程共享变量可能引发的问题

实例与变量的线程安全问题

1.在使用synchronized关键字修饰方法时,在多线程情况下会出现指令重排问题

在某些 JVM 中, i– 的操作要分成如下 3 步:
1)取得原有ⅰ值。
2)计算 i-1。
3)对i进行赋值。
在这3个步骤中,如果有多个线程同时访问,那么一定会出现非线程安全问题。

2.一个书中的案例:

用println打印全局变量的$i–$,在多线程环境下会引发线程安全问题,因为:

虽然$println()$方法在内部是同步的,但$i–$的操作却是在进入$println()$之前发生的,所以有发生非线程安全问题的概率,

常用方法

1.currentTread0方法

可以返回该方法在被哪个线程调用,书中案例为:构造方法被main线程调用,run方法被Thread线程调用

isAlive()方法

可以判断线程当前是否在活动状态

停止线程

1.interrupet()停止线程

书中案例:在for循环中停止的线程,for循环下面的语句还可以正常输出;要解决这一问题,可以将代码块放入try中,并在for循环内抛出异常

2.在sleep状态下停止线程

如果在sleep状态下停止线程,则会进入catch语句,并清除停止的状态值

如果先停止再sleep,则会进入catch

3.用stop()方法停止线程

会造成数据不一致的后果,所以已经作废

暂停线程

suspend与resume方法

缺点:

1.独占:在使用suspend与resume方法时,如果使用不当,极易造成公共的同步对象的独占,使得其他线程无法访问公共同步对象。

2.不同步:会造成因线程暂停导致数据不同步的情况

线程的优先级

可以使用setPriority()方法设置线程的优先级,优先级为$1-10$

优先级具有继承的特性:A线程调用的B线程,B线程会继承A线程的优先级

线程先执行不代表先执行完,线程的优先级具有随机性

守护线程

线程分为2种,一种为用户线程,一种为守护线程

main线程和GC线程是经典的两个守护线程

对象和变量的并发访问

“非线程安全”其实会在多个线程对同一个对象中的实例变量进行并发访问时发生,

产生的后果就是“脏读”,也就是取到的数据其实是被更改过的。而“线程安全”就是以获得的实例变量的值是经过同步处理的,不会出现脏读的现象。

方法内的变量是线程安全的

synchronized

1.锁的是谁?

关键字synchronized 取得的锁都是对象锁,而不是把一段代码或方法(函数)当作锁,哪个线程先执行带synchronized关键字的方法,哪个线程就持有该方法所属对象的锁 Lock;

也就是说,一个线程调用某个对象的方法时,这个对象就被锁住了,如果有另一个线程调用该对象的其他方法,就只能等待持有锁的线程把锁释放

但如果多个线程访问多个对象,则 JVM 会创建多个锁。

2.synchronized的性质

1.是可重入锁

可重人锁的概念是:自己可以再次获取自己的内部锁。
比如有1条线程获得了某个对象的锁,此时这个对象锁还没有释放,当其再次想要获取这个对象的锁的时候还是可以获取的,如果不可重人的话,就会造成死锁。

2.是悲观锁

3.出现异常自动释放

4.同步不可继承

父类中的方法用synchronized修饰,子类在继承这个方法时,不会同时继承锁

3.弊端及其解决方案(同步代码块)

如果一个对象长时间的持有锁,其他对象只能等待,就会等待很久

解决方案:使用同步代码块

方法内部的同步代码块,形如:

synchronized (this) {
//you code
}

而不是在方法上加synchronized,这样就做到了:在方法中:不在代码块中的语句异步执行,代码块中的语句同步执行

当有多个同步代码块时,当线程访问一个同步代码块,其他代码块将被阻塞;即多个同步代码块之间也是同步(按顺序执行的)

synchronized与synchronized (this)都是锁的对象

4.synchronized与synchronized (this)异同

书上的大段原话

多个线程调用同一个对象中的不同名称的 synchronized 同步方法或 synchronized(this)同步代码块时,调用的效果就是按顺序执行,也就是同步的,阻塞的。这说明synchronized 同步方法或 synchronized(this)同步代码块分别有两种作用。

1.synchronized同步方法:

  1. 对其他synchronized 同步方法或 synchronized(this) 同步代码块调用呈阻塞状态。
  2. 同一时间只有一个线程可以执行 synchronized 同步方法中的代码。

2.synchronized(this)同步代码块

  1. 对其他synchronized 同步方法或 synchronized(this) 同步代码块调用呈阻塞状态。
  2. 同一时间只有一个线程可以执行 synchronized(this)同步代码块中的代码。

其实Java还支持对“任意对象”作为“对象监视器”来实现同步的功能。这个“任意对象”大多数是实例变量及方法的参数,使用格式为synchronized(非this对象).

5.同步代码块synchronized(任意对象)做对象监视器

1)当多个线程同时执行 synchronized(x)(} 同步代码块时呈同步效果。
2)当其他线程执行ⅹ对象中synchronized 同步方法时呈同步效果。
3)当其他线程执行ⅹ对象方法里面的synchronized(this)代码块时也呈现同步效果。
但需要注意:如果其他线程调用不加 synchronized 关键字的方法时,还是异步调用。

6.synchronized应用在静态方法前锁Class

和将 synchronized 关键字加到非static 方法上使用的效果是一样的。其实还是有本质上的不同的,synchronized 关键字加到static静态方法上是给 Class 类上锁,而synchronized 关键字加到非static 静态方法上是给对象上锁。

7.synchronized与String连用时的问题

synchronized(String)使用时会由于jvm中有string常量池缓存功能导致多个线程持有相同的锁,所以同步代码块不与String连用

如果非要实现字符串的操作可以用new String()解决,原因如下:

Object a = new String("A");
Object b = new String("B");
System.out.println(a.hashCode()==b.hashCode());
String c = "B";
String d = "B";
System.out.println(c.hashCode()==d.hashCode());
//输出结果为:
//false
//true

原因猜想:用new关键字,定义出两个不同的字符串对象,直接写两个相同的字符串,在常量池中是同一个对象

volatile

Java编程语言允许线程访问共享变量,为了确保共享变量能被准确和一致地更新,线程应该确保通过排他锁单独获得这个变量。Java语言提供了volatile,在某些情况下比锁要更加方便。

与vlolatile实现相关一些名词解释

JMM名词解释.png

1)将当前处理器缓存行的数据写回到系统内存。
2)这个写回内存的操作会使在其他CPU里缓存了该内存地址的数据无效。

Java内存模型的基础

在并发编程中,需要处理两个关键问题:线程之间如何通信及线程之间如何同步

线程之间的通信机制有两种:共享内存消息传递

在执行程序时,为了提高性能,编译器和处理器常常会对指令做重排序。重排序分3种类型。

1)编译器优化的重排序。编译器在不改变单线程程序语义的前提下,可以重新安排语句
的执行顺序。
2)指令级并行的重排序。现代处理器采用了指令级并行技术(Instruction-Level
Parallelism,ILP)来将多条指令重叠执行。如果不存在数据依赖性,处理器可以改变语句对应
机器指令的执行顺序。
3)内存系统的重排序。由于处理器使用缓存和读/写缓冲区,这使得加载和存储操作看上
去可能是在乱序执行。

2和3属于处理器重排序

JMM的编译器重排序规则会禁止特定类型的编译器重排
序(不是所有的编译器重排序都要禁止)。对于处理器重排序,JMM的处理器重排序规则会要
求Java编译器在生成指令序列时,插入特定类型的内存屏障(Memory Barriers,Intel称之为
Memory Fence)指令,通过内存屏障指令来禁止特定类型的处理器重排序。




面试前必看基础sql语句

面试常用4表

学生表(Student)、课程表(Course)、成绩表(Score)以及教师信息表(Teacher)

建表

begin;
DROP TABLE IF EXISTS `Teacher`;

CREATE TABLE `Teacher`
(
    `Tno`       varchar(3)  NOT NULL comment '教师编号',
    `Tname`     varchar(4)  NOT NULL comment '老师姓名',
    `Tsex`      varchar(2)  NOT NULL comment '老师性别',
    `Tbirthday` date       DEFAULT NULL comment '老师生日',
    `Tprof`     varchar(6) DEFAULT NULL comment '老师职称',
    `Depart`    varchar(10) NOT NULL comment '老师专业',
    PRIMARY KEY (`Tno`)
) ENGINE = InnoDB
  CHARSET = utf8;


INSERT INTO `Teacher` ( `Tno`, `Tname`, `Tsex`, `Tbirthday`, `Tprof`
                      , `Depart`)
VALUES ('804', '李诚', '男', '1958-12-02', '副教授', '计算机系'),
       ('825', '王萍', '女', '1972-05-05', '助教', '计算机系'),
       ('831', '刘冰', '女', '1977-08-14', '助教', '电子工程系'),
       ('856', '张旭', '男', '1969-03-12', '讲师', '电子工程系');


DROP TABLE IF EXISTS `Course`;

CREATE TABLE `Course`
(
    `Cno`   varchar(5)  NOT NULL comment '课程号',
    `Cname` varchar(10) NOT NULL comment '查看名',
    `Tno`   varchar(3)  NOT NULL comment '授课教师编号',
    PRIMARY KEY (`Cno`),
    KEY `Tno` (`Tno`),
    CONSTRAINT `Course_ibfk_1` FOREIGN KEY (`Tno`) REFERENCES `Teacher` (`Tno`)
) ENGINE = InnoDB
  CHARSET = utf8;

INSERT INTO `Course` (`Cno`, `Cname`, `Tno`)
VALUES ('3-105', '计算机导论', '825'),
       ('3-245', '操作系统', '804'),
       ('6-166', '数字电路', '856'),
       ('9-888', '高等数学', '831');

/*Table structure for table `Score` */
DROP TABLE IF EXISTS `Score`;

/*Table structure for table `Student` */
DROP TABLE IF EXISTS `Student`;

CREATE TABLE `Student`
(
    `Sno`       varchar(3) NOT NULL comment '学生学号',
    `Sname`     varchar(8) NOT NULL comment '学生姓名',
    `Ssex`      varchar(2) NOT NULL comment '学生性别',
    `Sbirthday` date       DEFAULT NULL comment '学生出生年月',
    `Sclass`    varchar(5) DEFAULT NULL comment '学生所在班级',
    PRIMARY KEY (`Sno`)
) ENGINE = InnoDB
  CHARSET = utf8;

INSERT INTO `Student` (`Sno`, `Sname`, `Ssex`, `Sbirthday`, `Sclass`)
VALUES ('101', '李军', '男', '1976-02-20', '95033'),
       ('103', '陆君', '男', '1974-06-03', '95031'),
       ('105', '匡明', '男', '1975-10-02', '95031'),
       ('107', '王丽', '女', '1976-01-23', '95033'),
       ('108', '曾华', '男', '1977-09-01', '95033'),
       ('109', '王芳', '女', '1975-02-10', '95031');

/*Table structure for table `Teacher` */

CREATE TABLE `Score`
(
    `Sno`    varchar(3) NOT NULL comment '学号',
    `Cno`    varchar(5) NOT NULL comment '课程号',
    `Degree` decimal(4, 1) DEFAULT NULL comment '成绩',
    PRIMARY KEY (`Sno`, `Cno`),
    KEY `Cno` (`Cno`),
    CONSTRAINT `Score_ibfk_1` FOREIGN KEY (`Sno`) REFERENCES `Student` (`Sno`),
    CONSTRAINT `Score_ibfk_2` FOREIGN KEY (`Cno`) REFERENCES `Course` (`Cno`)
) ENGINE = InnoDB
  CHARSET = utf8;

INSERT INTO `Score` (`Sno`, `Cno`, `Degree`)
VALUES ('101', '3-105', '64.0'),
       ('101', '6-166', '85.0'),
       ('103', '3-105', '92.0'),
       ('103', '3-245', '86.0'),
       ('105', '3-105', '88.0'),
       ('105', '3-245', '75.0'),
       ('107', '3-105', '91.0'),
       ('107', '6-166', '79.0'),
       ('108', '3-105', '78.0'),
       ('108', '6-166', '81.0'),
       ('109', '3-105', '76.0'),
       ('109', '3-245', '68.0');

commit;

表内容

学生表图片.png

sql语句练习

1.查询与李军选至少一门相同课的所有学生姓名

select distinct Sname
from Student join Score on Student.Sno = Score.Sno
where Cno in (select distinct Cno from Student, Score where Student.Sno = Score.Sno and Sname = '李军');

2.查询与李军选至少2门相同课的所有学生姓名

select Sname
from score join Student on score.Sno = Student.Sno
where Cno IN (select distinct Cno from Student, Score where Student.Sno = Score.Sno and Sname = '李军') and Sname!='李军'
group by Score.Sno having count(*)>1;

3.查询与学号为101的同学选至少2门相同课的所有学生姓名

SELECT Sname
FROM student JOIN score s1 ON s1.Sno = student.Sno
WHERE EXISTS (
              SELECT Sno
              FROM score s2
              WHERE s1.Sno != 101 and
                      s2.Sno = 101 and
                      s1.Cno = s2.Cno
          )
GROUP BY s1.Sno
HAVING COUNT(Cno) > 1;







3张表

学生表(Student)、课程表(Course)、成绩表(Score)

建表

begin;
-- (1)创建Student表
CREATE TABLE Student
(
    Sno   CHAR(8) PRIMARY KEY,
    Sname CHAR(8),
    Ssex  CHAR(2) NOT NULL,
    Sage  INT,
    Sdept CHAR(20)
);
-- (2)创建Course表
CREATE TABLE Course
(
    Cno     CHAR(4) PRIMARY KEY,
    Cname   CHAR(40) NOT NULL,
    Cpno    CHAR(4),
    Ccredit SMALLINT
);
-- (3)创建SC表
CREATE TABLE SC
(
    Sno   CHAR(8),
    Cno   CHAR(4),
    Grade SMALLINT,
    CONSTRAINT `SC_ibfk_1` FOREIGN KEY (Sno) REFERENCES Student (Sno),
    CONSTRAINT `SC_ibfk_2` FOREIGN KEY (Cno) REFERENCES Course (Cno)

);



-- (6)修改表结构及约束
--  增加班级列
ALTER TABLE Student
    ADD Sclass char(4);
--  修改年龄列
ALTER TABLE Student
    MODIFY Sage smallint;
--  增加约束
ALTER TABLE Course
    ADD UNIQUE (Cname);
-- (7)删除表


-- (1)为Course表按课程名称创建索引
CREATE INDEX cname_index On Course (Cname);
-- (2)为Student表按学生姓名创建唯一索引
CREATE UNIQUE INDEX sname_index ON Student (Sname);
-- (3)为SC表按学号和课程号创建聚集索引
CREATE INDEX sno_cno_index On SC (Sno, Cno desc);

-- (4)为Course表按课程号创建唯一索引
CREATE UNIQUE INDEX iSCno ON Course (Cno);


-- 3.创建视图
-- 建立信息系学生的视图:

CREATE VIEW IS_Student
AS
SELECT Sno, Sname, Sage
FROM Student
WHERE Sdept = 'IS';


-- (1)插入到Student表

INSERT INTO Student
VALUES ('20100001', '李勇', '男', 20, 'CS', '1001'),
       ('20100002', '刘晨', '女', 19, 'CS', '1001');

INSERT INTO Student(Sno, Sname, Ssex, Sage, Sdept, Sclass)
VALUES ('20100021', '王敏', '女', 18, 'MA', '1002'),
       ('20100031', '张立', '男', 19, 'IS', '1003');

INSERT INTO Student(Sno, Sname, Ssex, sclass)
VALUES ('20100003', '刘洋', '女', '1001');


INSERT INTO Student(Sno, Sname, Ssex, Sage, Sdept, sclass)
VALUES ('20100010', '赵斌', '男', '19', 'IS', '1005');

INSERT INTO Student
VALUES ('20100022', '张明明', '男', 19, 'CS', '1002');


-- (2)插入到Course表

INSERT INTO Course(Cno, Cname, Cpno, Ccredit)
VALUES ('1', '数据库系统原理', '5', 4);
INSERT INTO Course(Cno, Cname, Cpno, Ccredit)
VALUES ('2', '高等数学', null, 2);
INSERT INTO Course(Cno, Cname, Cpno, Ccredit)
VALUES ('3', '管理信息系统', '1', 4);

-- 请写出插入其余行的插入语句,并插入数据。
INSERT INTO Course(Cno, Cname, Cpno, Ccredit)
VALUES ('6', '数据处理', null, '2');
INSERT INTO Course(cno, cname, cpno, ccredit)
values ('7', 'c语言', null, '4');

-- (3)插入到SC表
INSERT INTO SC
VALUES ('20100001', '1', 92);
INSERT INTO SC
VALUES ('20100002', '2', 80);
INSERT INTO SC(Sno, Cno)
VALUES ('20100003', '1');
INSERT INTO SC(Sno, Cno, Grade)
VALUES ('20100010', '3', null);

-- 请写出插入其余行的插入语句,并插入数据。
INSERT INTO SC
VALUES ('20100002', '2', 85);
INSERT INTO SC
VALUES ('20100002', '3', 88);
INSERT INTO SC
VALUES ('20100002', '1', 90);


commit ;

表内容

学校数据库实验student表.png

sql语句练习

1.为SC表按学号和课程号创建聚集索引

CREATE CLUSTERED INDEX iSnoCno On SC(Sno,Cno desc)

3.为Course表按课程号创建唯一索引

CREATE UNIQUE INDEX iSCno ON Course(Cno)

4.填写赵斌同学的管理信息系统课程的成绩

UPDATE SC SET Grade = 85
 WHERE Sno='20100010' AND Cno='3'

5.将计算机科学系全体学生的成绩加5分

UPDATE sc SET Grade=Grade + 5
 WHERE 'CS'=(select Sdept from student where student.Sno=sc.Sno)

删除计算机科学系所有学生的选课记录

DELETE FROM SC WHERE 'CS'=(select Sdept from student where student.Sno=SC.Sno );

查询全体学生的姓名、出生年份和所有系,要求用小写字母表示所有系名。

SELECT Sname, 'Year of Birth:' as BIRTH, 2000-Sage BIRTHDAY, DEPARTMENT = LOWER(Sdept)
FROM Student;

查询选修了课程的学生学号:比较ALL和DISTINCT的区别

SELECT Sno FROM SC;
SELECT DISTINCT Sno FROM SC;

按范围查询
查询年龄在20~23岁之间的学生的姓名、系别和年龄

SELECT Sname,Sdept,Sage FROM Student WHERE Sage BETWEEN 20 AND 23

查询属性值属于指定集合的行
查询信息系(IS)、数学系(MA)和计算机科学系(CS)学生的姓名和性别

SELECT Sname,Ssex FROM Student WHERE Sdept IN ('IS','MA','CS');

模糊查询
查询所有姓刘学生的姓名、学号和性别

SELECT Sname,Sno,Ssex FROM Student WHERE Sname LIKE '刘%'

查询空值
查询缺少成绩的学生的学号和相应的课程号

SELECT Sno,Cno FROM sc WHERE Grade is null;





关于服务器自动化配置的一些操作

自动化配置服务器,其本质就是开机自动运行脚本

开机自动运行脚本的思路

1.以某一用户身份去登陆服务器,在linux系统下会默认运行用户家目录下的.bashrc 配置文件

2.linux或unix系统下,/etc/rc.d/rc.local 用于添加开机启动命令

问题:基于 Debian 的 Linux 系统默认支持 rc-local.service 服务,这个服务主要用来在系统启动时运行用户自定义的脚本命令等。只有在centos或Ubuntu16以下的才有。

3.对于docker容器内脚本自运行的方法,是在dockerFile 中用 ENTRYPOINT[/bin/bash,脚本]

docker-compose启动容器后执行脚本或命令不退出 | 运行内部程序

知道了以上的思路,我们想开机自动执行某脚本的思路是:

1.在.bashrc中执行对应脚本

2./etc/rc.local是/etc/rc.d/rc.local的软连接,软连接相当于windows的快捷键

研究.bashrc

让我们来看一下阿里云ubuntu20.04的默认.bashrc,(我把其中的英文注释都翻译的一遍)

# ~.bashrc:由 bash(1) 为非登录 shell 执行。有关别名定义的示例,请参见 usrsharedocbashexamplesstartup-files(在 bash-doc 包中)。
# 您可能希望将所有添加到一个单独的文件中,例如 ~.bash_aliases,而不是直接在此处添加它们。
# 请参阅 bash-doc 包中的 usrsharedocbash-docexamples。

# 如果不以交互方式运行,则不要执行任何操作
[ -z "$PS1" ] && return

# 不要在历史记录中放置重复的行。有关更多选项,请参见 bash(1)
# ...或强制ignoreups和ignorespace
HISTCONTROL=ignoredups:ignorespace

# 追加到历史文件,不要覆盖它
shopt -s histappend

# 要设置历史长度,请参见 bash(1) 中的 HISTSIZE 和 HISTFILESIZE
HISTSIZE=1000
HISTFILESIZE=2000

# 检查每个命令后的窗口大小,如有必要,
# 更新 LINES 和 COLUMNS 的值。
shopt -s checkwinsize

# 对非文本输入文件更友好,请参阅lesspipe(1)
[ -x /usr/bin/lesspipe ] && eval "$(SHELL=/bin/sh lesspipe)"

# 设置变量标识您工作的 chroot(在下面的提示中使用)
if [ -z "$debian_chroot" ] && [ -r /etc/debian_chroot ]; then
    debian_chroot=$(cat /etc/debian_chroot)
fi

# 设置一个花哨的提示(非颜色,除非我们知道我们“想要”颜色)
case "$TERM" in
    xterm-color) color_prompt=yes;;
esac

# 如果终端有能力,取消注释彩色提示;转身
# 默认关闭以不分散用户的注意力:终端窗口中的焦点
# 应该在命令的输出上,而不是在提示符上
# 强制颜色提示=是

if [ -n "$force_color_prompt" ]; then
    if [ -x /usr/bin/tput ] && tput setaf 1 >&/dev/null; then
    # 我们有颜色支持;假设它符合 Ecma-48
    # (ISOIEC-6429)。 (缺乏这样的支持是极其罕见的,而且这样的
    # 案例倾向于支持 setf 而不是 setaf。)
    color_prompt=yes
    else
    color_prompt=
    fi
fi

if [ "$color_prompt" = yes ]; then
    PS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '
else
    PS1='${debian_chroot:+($debian_chroot)}\u@\h:\w\$ '
fi
unset color_prompt force_color_prompt

# 如果这是一个 xterm,则将标题设置为 user@host:dir
case "$TERM" in
xterm*|rxvt*)
    PS1="\[\e]0;${debian_chroot:+($debian_chroot)}\u@\h: \w\a\]$PS1"
    ;;
*)
    ;;
esac

# 启用 ls 的颜色支持并添加方便的别名
if [ -x /usr/bin/dircolors ]; then
    test -r ~/.dircolors && eval "$(dircolors -b ~/.dircolors)" || eval "$(dircolors -b)"
    alias ls='ls --color=auto'
    #别名 dir='dir --color=auto'
    #别名 vdir='vdir --color=auto'

    alias grep='grep --color=auto'
    alias fgrep='fgrep --color=auto'
    alias egrep='egrep --color=auto'
fi

# 更多 ls 别名
alias ll='ls -alF'
alias la='ls -A'
alias l='ls -CF'

# ~.bashrc:由 bash(1) 为非登录 shell 执行。有关别名定义的示例,
# 请参见 usrsharedocbashexamplesstartup-files(在 bash-doc 包中)。
# 您可能希望将所有添加到一个单独的文件中,例如 ~.bash_aliases,
# 而不是直接在此处添加它们。请参阅 bash-doc 包中的 usrsharedocbash-docexamples。

if [ -f ~/.bash_aliases ]; then
    . ~/.bash_aliases
fi

# 启用可编程完成功能(您不需要启用,如果 /etc/bash.bashrc and /etc/profile/sources /etc/bash.bashrc 已经启用 
#if [ -f /etc/bash_completion ] && ! shopt -oq posix; then
#    . /etc/bash_completion
#fi

下面是一段.bashrc详细的说明:

如果你运行一个基于 Unix 或者类 Unix 的操作系统,bash 很有可能是作为默认终端被安装的。虽然存在很多不同的 shell,bash 却是最常见或许也是最主流的。如果你不明白那意味着什么,bash 是一个能解释你输入进终端程序的东西,并且基于你的输入来运行命令。它在一定程度上支持使用脚本来定制功能,这时候就要用到 .bashrc 了。

为了加载你的配置,bash 在每次启动时都会加载 .bashrc 文件的内容。每个用户的 home 目录都有这个 shell 脚本。它用来存储并加载你的终端配置和环境变量。

终端配置可以包含很多不同的东西。最常见的,.bashrc 文件包含用户想要用的别名。别名允许用户通过更短的名字或替代的名字来指向命令,对于经常在终端下工作的人来说这可是一个省时利器。

总结.bashrc的作用

1.配置命令的一些别名

2.配置颜色支持

3.在bash终端被启动时,执行文件中的一些shell语句

一个实际的案例

对于一个把所有服务都放在一个容器中的项目,启动项目需要运行多个环境

由于有多个需要一直启动的服务,所以需要通过shell来操作tmux,来同时运行多个服务

启动每个项目的脚本runacapp:

#! /bin/bash


USER_PASSWORD=
USERNAME=doctao
WORKDIR=/home/$USERNAME/acapp
#
cd $WORKDIR || exit
#su- $USERNAME

echo $USER_PASSWORD | sudo -S /etc/init.d/nginx start
echo $USER_PASSWORD | sudo -S redis-server /etc/redis/redis.conf

# 后台新建一个session
tmux new-session -d -s acapp_workspace

#向选择的窗口发送指令

tmux send-keys "uwsgi --ini scripts/uwsgi.ini" C-m

#多次切割后每个小窗口的编号会变化
tmux split-window -v

#启动`django_channels`服务

tmux send-keys "daphne -b 0.0.0.0 -p 5015 acapp.asgi:application" C-m

#多次切割后每个小窗口的编号会变化

#该命令会把当前工作区域分成左右两个小窗格,光标会移动到右面的窗口
tmux split-window -h

tmux send-keys "cd match_system/src/" C-m
tmux send-keys "chmod +x main.py" C-m
tmux send-keys "./main.py" C-m


#tmux select-pane -t 1
#tmux send-keys "command" C-m
#tmux send-keys "cd /home/zcmlc/go/src/zcm_activity" C-m
#tmux select-pane -t 2
#tmux send-keys "mysql -uroot -p123456 --host 192.168.1.221 --sigint-ignore --auto-vertical-output" C-m
#tmux send-keys "use data" C-m
#tmux -2 attach-session -t ssh  //挂载到之前运行的session上

想要在容器启动时自运行这个脚本,只需要在 .bashrc 中添加:

if [ -f /home/doctao/acapp/runacapp.sh ]; then
      bash /home/doctao/acapp/runacapp.sh
fi

关于配置容器ssh连接的方法

想要容器可以被ssh,需要sshd

要想在镜像构建时下载sshd,或其他各种软件时,会碰到如下问题:

1.容器是一个极简的操作系统,容器中自带的软件源几乎没有东西可以下载

2.在运行语句时,一旦某一句下载命令失败,构建会停止,如果选择 -y,也需要输出错误日志

3.sshd要想可以连接,需要更改配置文件参数

4.由于需要copy一些配置文件,需要解决跨系统传输时文件格式的问题(行末的\n)

apt-get命令详解(超详细)

1.下载问题的解决

要想让容器可以下载软件,必须更换软件源

方法

1.阿里源/etc/apt/sources.list的文件内容

deb http://mirrors.aliyun.com/ubuntu/ focal main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ focal main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ focal-security main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ focal-security main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ focal-updates main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ focal-updates main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ focal-backports main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ focal-backports main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ focal-proposed main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ focal-proposed main restricted universe multiverse

2.将阿里源写在文件中通过dockerfile中的COPY命令将文件传在容器中

# 更换阿里源
COPY ./db/sources.list /etc/apt/sources.list

3.要想成功使用阿里源,必须安装其共钥,具体公钥的key,需要先运行一遍看一下日志中缺少的key

apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29
apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 3B4FE6ACC0B21F32
apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 871920D1991BC93C
#每一行最后的一串字符,就是在日志中需要你自己找的key

4.更新软件源的命令

 #更新软件源
 apt-get clean
 apt-get update

5.下载必要软件

apt-get -y install vim
apt-get -y install openssh-server
apt-get -y install tofrodos

6.想要用户可以远程ssh登陆容器,必须修改配置文件/etc/ssh/sshd_config

将配置信息写入文件

echo 'RSAAuthentication yes \n\
        PubkeyAuthentication yes \n\
        AuthorizedKeysFile .ssh/authorized_keys \n\
        PermitRootLogin yes \n\
        ClientAliveInterval 60'\
>> /etc/ssh/sshd_config

7.将容器运行自启后的脚本文件放入容器并修正其格式,并放入.bashrc配置文件

#将开机运行脚本放入容器
COPY ./db/startup_run.sh /root/startup_run.sh

#修正文件格式
RUN fromdos /root/startup_run.sh

#添加脚本的运行权限
RUN chmod +x /root/startup_run.sh

#将bashrc脚本放入容器
COPY ./db/.bashrc /root/.bashrc

#修正文件格式
RUN fromdos /root/.bashrc

8.容器的root账户是默认没有密码的,需要设置一个密码

#添加root用户的密码
RUN  echo root:密码|chpasswd



本地Git仓库关联多个远程仓库的两种方法

背景

通常情况下,一个本地Git仓库对应一个远程仓库,每次pullpush仅涉及本地仓库和该远程仓库的同步;然而,在一些情况下,一个本地仓库需要同时关联多个远程仓库,比如:同时将一个项目发布在GithubCoding上,以兼顾国内外的访客。

那么,如何让一个本地仓库同时关联多个远程仓库呢?

方法1:每次pushpull时需分开操作

首先,查看本地仓库所关联的远程仓库:(假定最初仅关联了一个远程仓库)

$ git remote -v
origin  git@github.com:keithnull/keithnull.github.io.git (fetch)
origin  git@github.com:keithnull/keithnull.github.io.git (push)

然后,用git remote add <name> <url>添加一个远程仓库,其中name可以任意指定(对应上面的origin部分),比如:

$ git remote add coding.net git@git.coding.net:KeithNull/keithnull.github.io.git

再次查看本地仓库所关联的远程仓库,可以发现成功关联了两个远程仓库:

$ git remote -v
coding.net      git@git.coding.net:KeithNull/keithnull.github.io.git (fetch)
coding.net      git@git.coding.net:KeithNull/keithnull.github.io.git (push)
origin  git@github.com:keithnull/keithnull.github.io.git (fetch)
origin  git@github.com:keithnull/keithnull.github.io.git (push)

此后,若需进行push操作,则需要指定目标仓库,git push <repo> <branch>,对这两个远程仓库分别操作:

$ git push origin master
$ git push coding.net master

同理,pull操作也需要指定从哪个远程仓库拉取,git pull <repo> <branch>,从这两个仓库中选择其一:

$ git pull origin master
$ git pull coding.net master

方法2:pushpull无需额外操作

在方法1中,由于我们添加了多个远程仓库,在pushpull时便面临了仓库的选择问题。诚然如此较为严谨,但是在许多情况下,我们只需要保持远程仓库完全一致,而不需要进行区分,因而这样的区分便显得有些“多余”。

同样地,先查看已有的远程仓库:(假定最初仅关联了一个远程仓库)

git remote -v
origin  git@github.com:keithnull/keithnull.github.io.git (fetch)
origin  git@github.com:keithnull/keithnull.github.io.git (push)

然后,不额外添加远程仓库,而是给现有的远程仓库添加额外的URL。使用git remote set-url -add <name> <url>,给已有的名为name的远程仓库添加一个远程地址,比如:

git remote set-url --add origin git@gitee.com:liuyutaocode/web-multiplayer-online-games.git

再次查看所关联的远程仓库:

git remote -v
origin  git@github.com:keithnull/keithnull.github.io.git (fetch)
origin  git@github.com:keithnull/keithnull.github.io.git (push)
origin  git@git.coding.net:KeithNull/keithnull.github.io.git (push)

可以看到,我们并没有如方法1一般增加远程仓库的数目,而是给一个远程仓库赋予了多个地址(或者准确地说,多个用于push的地址)。

因此,这样设置后的pushpull操作与最初的操作完全一致,不需要进行调整。

总结

以上是给一个本地仓库关联多个远程仓库的两种方法,二者各有优劣,不过出于简便考虑,我最终采用了方法2。

此外,上述内容中涉及到的Git指令略去了许多不常用的参数,如需更加详细的说明,可以查阅Git文档,或者直接在命令行运行git remote --help




WordPress建站踩坑历程

搭建

docker官方wordpress

为了后期的维护,在docker-compose.yml中配置端口映射的同时,添加对22端口的映射

主题

WordPress 主题 Sakura 🌸 | 樱花庄的白猫 (2heng.xin)

WordPress Sakura主题配置博客

Hexo 博客美化

hexo+sakura主题美化合集

容器管理

1.更新软件园

apt-get update

2.下载ssh

apt-get install ssh

3.下载git

apt-get install git

4.创建.git,并在其中生成密钥


5.想要容器可以被ssh,需要sshd

sudo apt-get install openssh-server
sudo /etc/init.d/ssh start

6.

#启用公钥私钥配对认证方式 
PubkeyAuthentication yes 
#公钥文件路径(和上面生成的文件同)
AuthorizedKeysFile .ssh/authorized_keys  
PermitRootLogin yes #root能使用ssh登录
#参数数值是秒 , 是指超时时间
ClientAliveInterval 60  
#设置允许超时的次数
ClientAliveCountMax 3 

阿里源镜像

FROM ubuntu:20.04

RUN mv /etc/apt/sources.list /etc/apt/sources.list.bak && \
    echo "deb http://mirrors.aliyun.com/ubuntu/ focal-updates main restricted" >/etc/apt/sources.list && \
    echo "deb http://mirrors.aliyun.com/ubuntu/ focal universe" >/etc/apt/sources.list && \
    echo "deb http://mirrors.aliyun.com/ubuntu/ focal-updates universe" >/etc/apt/sources.list && \
    echo "deb http://mirrors.aliyun.com/ubuntu/ focal multiverse" >/etc/apt/sources.list && \
    echo "deb http://mirrors.aliyun.com/ubuntu/ focal-updates multiverse" >/etc/apt/sources.list && \
    echo "deb http://mirrors.aliyun.com/ubuntu/ focal-backports main restricted universe multiverse" >/etc/apt/sources.list && \
    echo "deb http://mirrors.aliyun.com/ubuntu/ focal-security main restricted" >/etc/apt/sources.list && \
    echo "deb http://mirrors.aliyun.com/ubuntu/ focal-security universe" >/etc/apt/sources.list && \
    echo "deb http://mirrors.aliyun.com/ubuntu/ focal-security multiverse" >/etc/apt/sources.list

插件

链接管理跳转 Pretty Links

docker容器下mysql更改WordPress的site address和home(URL)------局域网 - dora_shao - 博客园 (cnblogs.com)




maven仓库报错处理办法

maven仓库目录结构配置

至今为止及其靠谱的settings.xml

<?xml version="1.0" encoding="UTF-8"?>

<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">


    <!-- localRepository  Default: ${user.home}/.m2/repository -->
    <localRepository>D:\maven\localrepository</localRepository>

    <!--
    interactiveMode Default: true
    <interactiveMode>true</interactiveMode>
    -->

    <!--
      offline Default: false
      <offline>false</offline>
    -->

    <pluginGroups>

        <!--    <pluginGroup>com.your.plugins</pluginGroup>-->

    </pluginGroups>

    <proxies>
        <!--
        <proxy>
          <id>optional</id>
          <active>true</active>
          <protocol>http</protocol>
          <username>proxyuser</username>
          <password>proxypass</password>
          <host>proxy.host.net</host>
          <port>80</port>
          <nonProxyHosts>local.net|some.host.com</nonProxyHosts>
        </proxy>
        -->
    </proxies>

    <servers>
        <!-- server

        <server>
          <id>deploymentRepo</id>
          <username>repouser</username>
          <password>repopwd</password>
        </server>
        -->

        <!--
        <server>
          <id>siteServer</id>
          <privateKey>/path/to/private/key</privateKey>
          <passphrase>optional; leave empty if not used.</passphrase>
        </server>
        -->
    </servers>


    <mirrors>
        <mirror>
            <id>alimaven</id>
            <name>aliyun maven</name>
            <url>https://maven.aliyun.com/repository/public</url>
            <mirrorOf>central</mirrorOf>
        </mirror>
    </mirrors>


    <profiles>
        <profile>
            <id>aliyun1</id>
            <repositories>
                <repository>
                    <id>阿里云中央仓库</id>
                    <url>https://maven.aliyun.com/repository/public</url>
                    <releases>
                        <enabled>true</enabled>
                    </releases>
                    <snapshots>
                        <enabled>true</enabled>
                        <updatePolicy>always</updatePolicy>
                    </snapshots>
                </repository>
            </repositories>
        </profile>

        <profile>
            <id>aliyun2</id>
            <repositories>
                <repository>
                    <id>阿里云google仓库</id>
                    <url>https://maven.aliyun.com/repository/google</url>
                    <releases>
                        <enabled>true</enabled>
                    </releases>
                    <snapshots>
                        <enabled>true</enabled>
                        <updatePolicy>always</updatePolicy>
                    </snapshots>
                </repository>
            </repositories>
        </profile>

        <profile>
            <id>aliyun3</id>
            <repositories>
                <repository>
                    <id>阿里云gradle-plugin仓库</id>
                    <url>https://maven.aliyun.com/repository/gradle-plugin</url>
                    <releases>
                        <enabled>true</enabled>
                    </releases>
                    <snapshots>
                        <enabled>true</enabled>
                        <updatePolicy>always</updatePolicy>
                    </snapshots>
                </repository>
            </repositories>
        </profile>

        <profile>
            <id>aliyun4</id>
            <repositories>
                <repository>
                    <id>阿里云spring仓库</id>
                    <url>https://maven.aliyun.com/repository/spring</url>
                    <releases>
                        <enabled>true</enabled>
                    </releases>
                    <snapshots>
                        <enabled>true</enabled>
                        <updatePolicy>always</updatePolicy>
                    </snapshots>
                </repository>
            </repositories>
        </profile>

        <profile>
            <id>aliyun5</id>
            <repositories>
                <repository>
                    <id>阿里云spring-plugin仓库</id>
                    <url>https://maven.aliyun.com/repository/spring-plugin</url>
                    <releases>
                        <enabled>true</enabled>
                    </releases>
                    <snapshots>
                        <enabled>true</enabled>
                        <updatePolicy>always</updatePolicy>
                    </snapshots>
                </repository>
            </repositories>
        </profile>

        <profile>
            <id>aliyun6</id>
            <repositories>
                <repository>
                    <id>阿里云grails-core仓库</id>
                    <url>https://maven.aliyun.com/repository/grails-core</url>
                    <releases>
                        <enabled>true</enabled>
                    </releases>
                    <snapshots>
                        <enabled>true</enabled>
                        <updatePolicy>always</updatePolicy>
                    </snapshots>
                </repository>
            </repositories>
        </profile>



        <profile>
            <id>aliyun7</id>
            <repositories>
                <repository>
                    <id>阿里云apache snapshots仓库</id>
                    <url>https://maven.aliyun.com/repository/apache-snapshots</url>
                    <releases>
                        <enabled>true</enabled>
                    </releases>
                    <snapshots>
                        <enabled>true</enabled>
                        <updatePolicy>always</updatePolicy>
                    </snapshots>
                </repository>
            </repositories>
        </profile>


    </profiles>


    <activeProfiles>
        <activeProfile>aliyun1</activeProfile>
        <activeProfile>aliyun2</activeProfile>
        <activeProfile>aliyun3</activeProfile>
        <activeProfile>aliyun4</activeProfile>
        <activeProfile>aliyun5</activeProfile>
        <activeProfile>aliyun6</activeProfile>
        <activeProfile>aliyun7</activeProfile>
    </activeProfiles>
</settings>

如何让maven依次扫描多个仓库

Maven配置多仓库无效?来看看这篇文章

无法解析包怎么办

阿里云有许多仓库,一般只扫描public仓库,很有可能你需要的包在其他仓库,不在public仓库。

maven坑爹的是,无论你在[HTML_REMOVED]中写多少个[HTML_REMOVED],它只会扫描第一个。

所以在包搜索不到时,可以尝试更换settings.xml中,第一个[HTML_REMOVED]的地址,然后重新加载

仓库名称 阿里云仓库地址 阿里云仓库地址(老版) 源地址
central https://maven.aliyun.com/repository/central https://maven.aliyun.com/nexus/content/repositories/central https://repo1.maven.org/maven2/
jcenter https://maven.aliyun.com/repository/public https://maven.aliyun.com/nexus/content/repositories/jcenter http://jcenter.bintray.com/
public https://maven.aliyun.com/repository/public https://maven.aliyun.com/nexus/content/groups/public central仓和jcenter仓的聚合仓
google https://maven.aliyun.com/repository/google https://maven.aliyun.com/nexus/content/repositories/google https://maven.google.com/
gradle-plugin https://maven.aliyun.com/repository/gradle-plugin https://maven.aliyun.com/nexus/content/repositories/gradle-plugin https://plugins.gradle.org/m2/
spring https://maven.aliyun.com/repository/spring https://maven.aliyun.com/nexus/content/repositories/spring http://repo.spring.io/libs-milestone/
spring-plugin https://maven.aliyun.com/repository/spring-plugin https://maven.aliyun.com/nexus/content/repositories/spring-plugin http://repo.spring.io/plugins-release/
grails-core https://maven.aliyun.com/repository/grails-core https://maven.aliyun.com/nexus/content/repositories/grails-core https://repo.grails.org/grails/core
apache snapshots https://maven.aliyun.com/repository/apache-snapshots https://maven.aliyun.com/nexus/content/repositories/apache-snapshots https://repository.apache.org/snapshots/

maven插件报错怎么办

手动在下方添加版本号,不知道填哪个版本可以去仓库中搜索

版本任意,不会与项目版本冲突

maven依赖继承报错怎么办

经过实测发现,依赖继承报错不影响项目正常运行

警告

1.不要随便改动源项目的版本,不同版本间的方法差异很大,可能导致项目无法运行






活动打卡代码 工程课 Web-4.1. 拳皇项目

大菜狗
1个月前

拳皇97web版功能日志

acgit地址

拳皇线上地址

1.改进了项目结构,分离了游戏参数与具体业务

拳皇项目结构.png

2.对player类做了更加细粒度的划分

3.增加了一个管理状态机的对象,处理人物状态与对应贴图更加方便

4.实现了下蹲动作,解决了持续按下s,人物就一直下蹲的难点

拳皇下蹲展示.gif

5.用y总上课讲的操作栈思路实现了一个连招动作,但是仍然不太完善

下前拳技能展示.gif