0%

数据库

数据库同步问题:

Mysql面试指南

60 道 MySQL 精选面试题👍 | 二哥的Java进阶之路 (javabetter.cn)

InnoDB为什么是默认的引擎

  1. 是mysql中唯一支持事务的引擎
  2. 锁机制:使用行级锁,而不是表级锁
  3. 支持外键约束
  4. 具有崩溃恢复

索引为什么使用B+树

  1. 支出快速查找:B+树高度相对较低,查找效率高
  2. 有序性:节点的键值是有序排列的
  3. 支持高效插入和删除操作:叶子节点之间使用双向链表链接,可以快读定位到要插入和删除的位置
  4. 适应硬盘存储:B+树节点被控制在硬盘大小的范围内
  5. 支持数据点有序存储和范围查找

如何进行回滚的

  1. 使用Undo Log进行回滚,
  2. 混滚时,按照事务的执行的逆序进行回滚。
  3. Undo Log记录了事务对数据库的修改操作

慢查询如何考虑优化

数据库中的CAP

C:Consistency强一致性,系统在执行某项操作之后,仍然能够保持一致。更新操作执行成功,所有的用户都应该读到最新的值
A:Availabliy可用性,每个操作总是能够在一定时间内返回结果。结果可以是成功或者失败
P:分区容错性,出现网络分区时,系统能够正常运行。

怎么对plan进行优化

  1. 谓词下推:查询时,竟遭过滤掉不需要的数据SELECT * FROM orders WHERE order_date > '2023-01-01';
  2. 索引下推:使用索引来加速查询,尤其是在索引覆盖查询中
  3. 表达式优化:简化和重写表达式来提高效率
  4. 最左匹配优化选择索引:使用索引的最左前缀原则来优化查询
  5. 避免SELECT *
  6. 使用适当的JOIN类型
  7. 使用索引覆盖:创建包含所有查询列的索引避免回表

介绍一下MySQL的索引

MySQL的索引是一种数据结构,可以帮助MySQL快速搞笑的查询,更新数据库中的数据。

B+树的特征,为什么 MySQL 要使用 B+树

特点:

  1. 只有叶子节点会存放实际的数据,包括索引+记录,非叶子节只存放索引
  2. 所有索引都在叶子节点中出现,叶子节点之间构成一个有序链表
  3. 非叶子节点的索引也会同时存在叶子节点中,并且是叶子节点中索引中的最大值或者最小值
  4. 非叶子节点有多少个子节点,就有多少个索引

看看几个隔离级别和解决办法
GBKh和UTF-8不是一种编码方式
mysql中使用的utf-8是utf8mb3 使用13个字节来表示一个字符
而utf-8使用1
4
MVCC (Multi-Version Concurrency Control ,多版本并发控制)MVCC (Multi-Version Concurrency Control ,多版本并发控制)指的就是在使用 READ COMMITTD 、 REPEATABLE READ 这两种隔离级别的事务在执行普通的 SEELCT 操作时访问记录的版本链的过程,这样子可以使不同事务的 读-写 、 写-读 操作并发执行,从而提升系统性能。

  • sql语句的执行过程:mysql8.0 client发送sql语句->sql解析->执行优化和执行计划生成->执行->返回结果
  • 隔离等级:|575
    • MySQL默认是可重复读,可以解决幻读。然后串行化是基于锁实现的,可重复读和读已提交都是基于MVCC实现的,可重复读在当前读的情况下需要加锁才能保证不会出现幻读,所以仅仅使用MVCC不能解决幻读
  • 日志:
    • 种类:错误日志,查询日志,慢查询日志,Binary Log, 中继日志,事务日志
    1. undo log 有两种
      • insert undo log 执行insert 操作产生的undo log isnert只对事务本身可见,其他事务不可见,所以事务提交之后可以直接删除
      • update undo log 提供MVCC机制,提交时放入undo log 链表,等待purge线程进行最后的删除
        • purge:会定期检查 undo log 和数据行,找出那些已经不再需要的,然后将它们从磁盘上删除,从而回收磁盘空间。
    2. 慢查询日志:记录执行时间查过指定阈值的所有SQL查询,
    3. Binlog: 主要记录了数据库的修改操作,事务信息和一些特殊事件:服务器的启动和停止,主从复制状态变更事件等
    4. redo log 如何保证事务的持久性?
      • 事务执行时,首先记录在redo log而不是直接修改数据文件,redo log 保存在内存中的log buffer,然后被异步刷新到磁盘中的redo log file中
      • 事务提交时,相关的数据还没有没刷新到磁盘中,只要保证redo log 被刷新到磁盘中,即使系统在事务提交后崩溃了,也可以在系统重启之后通过redo log 里恢复数据,
      • 为什么要先写入redo log ,因为redo log 时顺序写入的,而写数据需要涉及随机I/O,能够提高数据库的性能
    5. 页表修改之后为什么不直接刷盘?因为刷新到磁盘中非常耗时,所以使用预写式日志的技术,先写入redolog,之后异步刷新内存中的redolog到磁盘中,之后写入数据到内存中的数据页中,之后写入binlog,内存中的数据页会在适当实际刷新到磁盘中。
    6. binlog和redolog的区别:
      • 用途上:binlog用于主从复制和数据恢复,复制时会把主服务器上的binlog发给从服务器,然后从服务器根据binlog中的记录来更新;而redo log 用于保证事务的持久性
      • 格式:binlog:可以是语句格式或者行格式,语句记录SQL执行的语句,行格式记录每一行的数据变化。redo log以物理格式记录数据页的修改,记录的是某个数据页的某个位置,将某个数据修改为了什么
      • 写入时机:binlog 事务提交时,redo log 事务执行过程中就开始写入,事务提交时确保已经将redo log 写入磁盘中
      • 清理方式:binlog 需手动清理或者设置过期时间清理; redo log 当数据页的修改刷新到磁盘中之后,redo log 即可被覆盖重新使用
    7. undo log 如何保证事务的原子性:事务开始执行时,udno log 中会记录修改前的数据,如果事务体骄傲成功,undo log 即可以被清理,如果失败就会使用undo log 回滚事务。
    8. MVCC(乐观锁)如何实现:每个数据行中都有两个隐藏列:一个用于记录改行创建时的事务ID,一个记录删除该行的事务ID,当事务开始时,他会根据当前所有活跃的事务产生Read View ,其中记录了:
      • 创建这个Read View时下个即将被分配的事务ID m_low_limit_id
      • 活跃列表中最小的事务ID m_up_limit_id
      • Read View创建时,其他未提交的活跃事务ID列表 m_ids
      • 创建该Read View 的事务ID m_creator_trx_id
        之后对数据行进行读取时,检查该行的创建事务ID和删除事务ID ReadView可见性|525
      • 如果创建ID大于m_low_limit_id,说明是在当前事务之后创建的,不可见
      • 如果删除ID小于m_up_limit_id,说明在当前事务开始前就已经被删除了,不可见
      • 如果创建事务ID小于m_low_limit_id但删除ID大于m_up_limit_id,需要与m_ids列表中比对,如果存在于m_ids列表中,则不可见
  • 外键:
    • 不建议使用外键,外键应该在应用层中保证,而不是在数据库上使用外键,因为外键对分库分表无法生效,同时需要维护外键,当我们做一些涉及外键的CUD操作时,需要触发相关的操作去检查,保证数据的一致性和正确性,从而消耗额外资源,
  • 集群:
    • redo log 和 undo log都是由InnoDB引擎实现的
    • bin log 是由mysql级别的日志
      • redo和bin 的区别:Redo log是记录了数据的修改,大小到一定程度,新的日志会覆盖,先写redo再写bin,目的是为了保证事务的持久性
      • Binlog是记录了执行了什么sql语句,不会覆盖旧日志,用于实现mysql的主从复制
    • redolog的优势:顺序写,预写入,磁盘同步过程中可以一次同步多个事务的Redo log:先把修改写入redolog,再修改内存中的数据,事务提交时再入bin log,之后再将数据写入磁盘中
    • 主从复制:
      • master节点会不断将sql命令写入Binary Log,子节点使用I/O线程读入master节点中的Bin Log
      • 字节点使用SQL线程执行Bin Log的语句
  • 存储引擎的区别:MyISAM 和 InnoDB 的区别
    • MySQL的存储引擎采用的是插件式架构,支持多种存储引擎,我们甚至可以为不同的数据库表设计不同的存储引擎,存储引擎是基于表而不是数据库的。
    • InnodDB支持事务
    • InnoDB支持行级锁定,MyISAM操作时直接锁定整个表
    • InnoDB支持外键和级联删除/更新
    • InnoBD存储数据更大
    • 崩溃恢复更好
    • InnoDB以聚簇的方式存储数据,MyISAM是将数据和索引分开存储
    • InnoDB支持MVCC
  • 事务:
    • 并发事务带来了哪些问题:
      1. 脏读:一个事务读到了另一个事务修改后回滚的数据
      2. 丢失修改:两个事务同时修改一个数据,先提交的会丢失修改
      3. 不可重复读:执行相同的两个语句,结果不一样
      4. 幻读:多了记录或者是少了记录
        • 3和4的区别:3是记录被修改,4是记录条数变化
    • mysql中的隔离等级: 默认是可重复读,能够防止幻读 实现是通过 锁和MVCC(多版本控制并发控制) 实现的
      1. 读未提交:允许事务读取未提交的事务的修改,会出现脏读,幻读,不可重复读
      2. 读已提交:只能读到提交的事务的修改,避免脏读,但是还有幻读 (读数据时加上共享锁,读取后立刻释放)
      3. 可重复读:同一事务中,多次读取统一数据返回结果要一致 (事务结束才释放锁,同时使用MVCC来保证每个事务都看到一个一致的快照)
      4. 可串行化:强制事务串行执行,影响效率
    • MVCC的原理:
    • 这两个隐藏的列就是undo log日志|700
      • 这俩就是隐藏列,trx_id当前修改之后的事务id,roll_pointer指向修改之前的信息
      • |725
      • MVCC实现原理:ReadView
        • 主要内容:
          • m_ids:生成ReadView时当前系统活跃的读写事务id列表
          • min_trx_id:在生成ReadView时当前系统活跃的读写事务中最小的事务id,也就是min (m_ids)
          • max_trx_id:表示生成ReadVeiw时系统应该分配给下下一个事务的id值
          • creator_trx_id:表示生成该ReadView的事务id
        • 原理:在访问某条记录时根据以下方式来判断:
          1. trx_id == creator_trx_id,表示是自己修改过的记录,可见
          2. trx_id < min_trx_id 可以被访问
          3. trx_id > max_trx_id,不可被访问
          4. trx_id > min && trx_id < max 需要判断:如果trx_id在m_ids中,表示仍然活跃,不可访问,不在表示已经被提交,可以访问了
          5. 如果某个版本对当前事务不可见,则顺着版本连找下一个版本的数据,直到最后,如果最后一个版本也不可见的话,意味着这条记录对这个事务完全不可见,查询结果不返回这个记录
        • 读已提交每次读都会生成一个ReadView,而可重复读智慧在第一次执行查询语句时生成一个ReadView
  • 索引常问:
    • 覆盖索引:查询使用了索引,并且查询过程中已经找到了所有需要的列,不需要继续回表了
    • 索引失效问题:
      1. 使用了!= < > LIKE ,函数操作或者表达式操作
      2. 联合索引中没有遵循最左前缀,跳过了中间的索引字段
  • SQL语句:
    • 常见函数:
      1. Concat()把多列拼接在一起
      2. RTrim() 去除多有空格
      3. AS给列起别名
    • 存储过程:存储过程是一种在数据库中存储的预编译的SQL语句集合,可以通过调用它的名字来执行。存储过程可以接收参数,并且可以返回值,长哟个与进行批处理
      USE test;
      CREATE PROCEDURE productpricing()
      BEGIN
      	SELECT * FROM hello;
      END;
      
      调用
      CALL productpricing()

InooDB详解

  1. InooDB大小一般为16kb
  2. InooDB页表:
  3. File Header:
  4. Page Header:
  5. infimumu+supremum 最小记录和最大记录。不存放真是数据
    • heap_no分别为0和1, record_type分为4种,0普通记录,1表示B+树非叶节点记录,2最小记录,3最大记录。
    • next_record 由当前真实数据指向吓一跳记录的真实数据。吓一跳记录不是按照插入顺序的下一条,而是根据主键排序之后的下一条。infimum的下一条记录是用户记录中主键最小的,用户记录中主键最大的下一条指向supremum
    • 为什么指向的是真是数据的开头?
      • 因为向左读取为记录头信息,向右读取真实数据。 记录头信息刚好是逆序的!
  6. User Record :
    • 由额外记录信息和实际信息组成
    • 额外记录:
      • 变长字段的真实数据的字节长度都放在记录的开头,逆序存放。不存放NULL的字节长度
      • NULLL值列表,NULL值存储在这里,并且是逆序存储,如果存在NULL值就不会存在这个列表
      • 记录头信息:
        • delete_mask标记的记录不会理科删除,而是会放在垃圾列表中,等待覆盖。
        • heap_no是根据主键的大小来确定的
    • 真实信息:
      • 如果未指定主键,会生成row_id隐藏列
  7. Page Directory :
    • 将正常的所有记录划分为几个组,包括最大和最小记录,不包括已删除的记录,最小记录所在的分组只包括自己。
    • 每组最后一个记录的头信息中的n_ownd来表示该组中记录数。
    • 将每组的最后一条记录的地址偏移量拿出来放入PageDirectory中,偏移量被称为槽(slot)
  8. File Trailer: 用于检验一个页是否完整:

索引:

回表问题

当查询的数据在索引树中找不到时,就需要回到主键索引树种去获取。
覆盖索引:
查询时从索引列就能够获取了,就不需要回到主键树中查找了

  • 前提
    1. 数据页链表中的前后保证前一个数据页中的主键的最大值大于后一个页表中主键的最小值,也就主键递增,每一个页表内,主键也递增
    2. 建立一个目录项:包括 key:页中主键最小值 page_no:页号。
  • mysql中的实现:
    1. 使用用户记录的数据页来存储目录项,并且使用record_type = 1来标明,只包含key和page_no即可
    2. 使用双向列表来维护多个目录项
    3. 使用树的结构来重复建立索引,加快访问
    4. 真正的数据页都放在最底层的叶子节点
    • 聚簇索引:1. 使用主键值大小进行记录和排序 2. 叶子节点是完整的用户记录。索引即数据,数据即索引
    • 二级索引:1.使用非主键值来进行记录和排序 2. 叶子节点存储的是索引键和主键的值 3. 查询到叶子节点之后需要再查找一变主键的索引来找到真正的用户记录。 优点是节省空间。
    • 联合索引:1.使用多个列的大小来进行排序。先按照一个列排序,列大小相同时,使用下一个列大小来排序。2. 对于这多个列只需要建立一个B+树即可,节省了空间3.实质上也是二级索引
    • 对比:MyISAM
      • 存储在一个文件中,按照插入顺序,有行号
      • 索引放在另一个文件中,使用主键值+行号作为索引,所以全是二级索引,而且不能二分查找。
    • SQL建立索引:每一个表会自动为主键或者unique建立一个聚簇索引,可使用语句指定建立其他列的索引
    • 删除索引:ALTER TABLE index_demo DROP INDEX idx_c2_c3;
  • 独立表空间:
    1. 区:表空间分为多个区,区包含多个页

指令执行过程

  • select
    1. 使用连接器连接mysql server,期间通过三次握手 show processlist来查看多少个客户端连接了
    2. 查询缓存(8.0) 已抛弃
    3. 解析SQL :
      1. 词法分析,根据字符串识别关键词,分类字符串
      2. 语法分析,语法解析器会根据语法规则来判断是否符合MySQL语法秒如果没问题就会构出SQL语法树
    4. 执行SQL语句,SELECT 查询语句流程主要可以分为下面这三个阶段:
      • prepare 阶段,也就是预处理阶段;检查表和字段是否存在,将* 扩展为表上的列
      • optimize 阶段,也就是优化阶段;优化器主要负责将 SQL 查询语句的执行方案确定下来,比如在表里面有多个索引的时候,优化器会基于查询成本的考虑,来决定选择使用哪个索引。
      • execute 阶段,也就是执行阶段;根据执行计划执行 SQL 查询语句,从存储引擎读取记录,返回给客户端;
  • MySQL一行记录如何
    • 每建立一个数据库都会咋/var.lib/mysql/下建立一个同名的文件夹
    • 文件夹中有三个文件
    • db.opt 用来存储数据库的默认字符集和字符检验规则
    • tableNaem.frm 对应的表结构会保存在这个文件,主要包含表结构的定义
    • tableName.ibd 表中的数据会保存在这个文件中
  • 表空间文件的结构 :
      • 索引段:存放 B + 树的非叶子节点的区的集合;
      • 数据段:存放 B + 树的叶子节点的区的集合;
      • 回滚段:存放的是回滚数据的区的集合,之前讲事务隔离 (opens new window)的时候就介绍到了 MVCC 利用了回滚段实现了多版本查询数据。
    • 区 InnoDB 使用的是B+树 以页为单位来来分配空间时,链表中相邻的两个页的物理位置是不连续的,为了防止随机I/O,可以按照区来分配,一个区可以包含多个页,这样页在位置上就是相邻的了可以使用顺序I/O
    • 页 读取数据以页为单位,当读取一行时,会将一页一同读取出来,放入内存中 默认页 16kb ,页是 InnoDB 存储引擎磁盘管理的最小单元,
    • 行 记录按照行
      • 行格式:COMPACT
        • 变长字段长度列表,用于保存数据占用的大小, 在列表中采用逆序存放数据占用的实际字节,当一行中没有变长字段时,例如全int时,就不会存在变长字段列表了。
        • NULL列表,数据中为NULL 的数据会放在NULL值列表中的。NULL值也是按照逆序存储的,使用二进制 0 1 来表示是否为NULL值,1 为是 当数据表的字段都定义成 NOT NULL 的时候,这时候表里的行格式就不会有 NULL 值列表了
        • 记录头信息
        • delete_mask :标识此条数据是否被删除。从这里可以知道,我们执行 detele 删除记录的时候,并不会真正的删除记录,只是将这个记录的 delete_mask 标记为 1。
        • next_record:下一条记录的位置。从这里可以知道,记录与记录之间是通过链表组织的。在前面我也提到了,指向的是下一条记录的「记录头信息」和「真实数据」之间的位置,这样的好处是向左读就是记录头信息,向右读就是真实数据,比较方便。
        • record_type:表示当前记录的类型,0表示普通记录,1表示B+树非叶子节点记录,2表示最小记录,3表示最大记录
      • 真实数据部分
        • row_id 未指定主键或者唯一约束列时,会有,占用6个字节
        • trx_id事务id表示数据由哪个事务生成的,必需
        • roll_pointer 记录上一个版本的指针,必需
    • 一行数据的最大字节数 65535(不包含 TEXT、BLOBs 这种大对象类型),其实是包含「变长字段长度列表」和 「NULL 值列表」所占用的字节数的
    • 如果一个数据页存不了一条记录,InnoDB 存储引擎会自动将溢出的数据存放到「溢出页」中。

Compact 行格式针对行溢出的处理是这样的:当发生行溢出时,在记录的真实数据处只会保存该列的一部分数据,而把剩余的数据放在「溢出页」中,然后真实数据处用 20 字节存储指向溢出页的地址,从而可以找到剩余数据所在的页。

Compressed 和 Dynamic 这两种格式采用完全的行溢出方式,记录的真实数据处不会存储该列的一部分数据,只存储 20 个字节的指针来指向溢出页。而实际的数据都存储在溢出页中。
索引分类:

  • 按「数据结构」分类:B+tree索引、Hash索引、Full-text索引
  • 按「物理存储」分类:聚簇索引(主键索引)、二级索引(辅助索引)
  • 按「字段特性」分类:主键索引、唯一索引、普通索引、前缀索引
  • 按「字段个数」分类:单列索引、联合索引

MySql查询优化

  • 单表
    1. 根据搜索条件,找出所有可能使用的索引
    2. 计算全表扫描的代价
    3. 计算使用不同索引执行查询的代价
    4. 对比各种执行方案的代价,找出成本最低的那一个
  • 多表连接:
    1. 单次查询驱动表的成本
    2. 多次查询被驱动表的成本(具体查询多少次取决于对驱动表查询的结果集中有多少条记录)
  • 连接查询成本占大头的其实是 驱动表扇出数 x 单次访问被驱动表的成本 ,所以我们的优化
    1. 重点其实是下边这两个部分:
    2. 尽量减少驱动表的扇出
    3. 对被驱动表的访问成本尽量低

Buffer Pool 缓冲池

脏页:缓冲池中的数据已经修改但是没有同步到磁盘中

基本概念

模式

  • 模式: 逻辑模式,是开发者可见的模式,是数据库中全体数据的逻辑结构和特征的描述,是所有用户的公共数据视图。
  • 外模式:子模式,用户模式,用户可见
  • 内模式: 存储模式,是物理存储模式

schema 和 database 区别

database 是一栋楼,schema(模式) 是一个个房间,但是在mysql中两种没有区别。

  • 视图和表的区别:视图是表的子集,常用于将查询结果保存在视图中,方便再次调用
  • 语法:’CREATE VIEW viewName AS 查询出来的表’

范式

  • 1NF 保证原子性即可,不可再分割了
  • 2NF 要有主键,且不存在部份依赖,也就是其他非主键的部分要治理来于主键即可
  • 3NF 非主键之间不存在传递依赖,例如 学号, 姓名, 年龄, 学院名称, 学院电话,满足第二范式,因为后面几个属性都要和主键相关联,不能独立存在,但是通过 学号 -> 学生 -> 所在院 -> 院电话 ,所以存在传递依赖应修改为:学生:(学号, 姓名, 年龄, 所在学院);学院:(学院,学院名称, 电话)。
  • BCNF 消除几个主键之间的传递依赖

ACID

  • Atomicity(原子性):一个事务(transaction)中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被恢复(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。

  • Consistency(一致性):在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表示写入的资料必须完全符合所有的预设规则,这包含资料的精确度、串联性以及后续数据库可以自发性地完成预定的工作。

  • Isolation(隔离性):数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。事务隔离分为不同级别,包括读未提交(Read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串行化(Serializable)。

  • Durability(持久性):事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。

E-R模型

E-R模型是Entity-Relationship模型的缩写,是一种用于数据库设计的概念性数据模型。它用来描述现实世界中的数据之间的关系,以及这些数据的属性。E-R模型提供了一种图形化的方式来表示数据模型,使数据库设计师能够更容易地理解和规划数据库结构。

E-R模型包括以下几个核心概念:

  1. 实体(Entity):实体是现实世界中可以被识别的独立对象,例如人、物品、地点或概念。每个实体都有一些属性,用来描述这个实体的特征。

  2. 属性(Attribute):属性是描述实体的特征或属性,它们用来存储有关实体的信息。例如,对于一个”人”实体,属性可以包括姓名、年龄、性别等。

  3. 关系(Relationship):关系表示不同实体之间的联系或连接。关系可以是一对一、一对多或多对多的。例如,一个”雇员”实体可以与一个”部门”实体之间有一个”属于”关系,表示雇员属于某个部门。

  4. 主键(Primary Key):主键是一个属性或属性组合,用来唯一标识实体集中的每个实体。它确保了每个实体都具有唯一的标识。

  5. 外键(Foreign Key):外键是一个实体中的属性,它引用了另一个实体的主键,用于建立实体之间的关联。

通过使用E-R模型,数据库设计师可以更好地理解数据之间的关系,从而规划数据库的结构,包括表的设计、关系的建立和数据的存储。一旦E-R模型完成,它可以用作设计数据库架构的基础,进而创建物理数据库模式(如关系模型),以实际存储和管理数据。这有助于确保数据库能够有效地存储和检索信息,并满足应用程序的需求。

自联结,变量不会,补一补去

打开

mysql -u root -p

数据类型

关系型数据库:建立在关系模型基础上,由多张相互链接的二维表组成的数据库

SQL语句

  1. 数值类型

    TINYINT 1byte -128 - 127  0 - 255 /*后面加上unsigned 即可指定为无符号*/
    SMALLINT 2
    MEDIUMINT 3
    INT 4
    BIGINT 8
    FLOAT 4
    DOUBLE 8
    DECIMAL 看精度和标度  125.25 精度为5 标度为2
  2. 字符串类型

    CHAR() 定长字符串
    VARCHAR 根据内容计算字符串长度
    TINYTEXT 短文本字符串
  3. 日期类

    DATE YYYY-MM-DD
    TIME HH:MM:SS
    YEAR YYYY
    DATETIME YYYY-MM-DD HH:MM:SS
    TIMESTAMP YYYY-MM-DD HH:MM:SS 时间戳

    常用函数

    DATE_FORMAT()

    时间戳记或称为时间标记(英语:timestamp)是指字符串或编码信息用于辨识记录下来的时间日期。国际标准为ISO 8601

    时间戳记的范例如下:

    • 2016-12-25T00:15:22Z
    • 2005-10-30 10:45 UTC
    • Sat Jul 23 02:16:57 2005
    • 2016年12月25日 (日) 00:14 (UTC)

注释 # 或者 /**/

  1. DDL (Data Definition Language 数据定义语言)用于操作对象及对象本身,这种对象包括数据库,表对象,及视图对象,表头 ^c501f6
  2. DML (Data Manipulation Language 数据操控语言) 用于操作数据库对象对象中包含的数据
  3. DQL (Data Query Language 数据查询语言 )用于查询数据
  4. DCL (Data Control Language 数据控制语句) 用于操作数据库对象的权限

DDL (Data Definition Language 数据定义语言)用于操作对象及对象本身,这种对象包括数据库,表对象,及视图对象,表头

SHOW DATABASES 查询所有的数据库
SELECT DATABASE(); 查询当前所处数据库
CREATE DATABASE  创建
DROP DATABASE XXX 删除
USE xxx  使用数据库
SHOW TABLES 查询当前数据库的所有表
DESC 表名   查询表结构
SHOW CREATE TABLE 表明 查询指定表的建表语句
	字段1 字段1类型 COMMENT 'XXX',/*注释*/
     ........
    
) COMMENTT 'XXX'/*注释可以省略*/
/*字符串类型是varchar(指定的字符长度)*/
/*修改表*/
ALTER TABLE tablename 
1.ADD字段名 类型 comment xxx 添加表头
2.CHANGE 旧 新 类型 comment xxx 修改
3.DROP name
4.RENAME TO name 修改表名

DROP TABLE name 删除
TRUNCATE TABLE name 删除并重新创建同名表,数据依旧会删除

DML (Data Manipulation Language 数据操控语言) 用于操作数据库对象对象中包含的数据

/*添加数据*/
INSERT INTO 表名(字段名) VALUES (数值) 给指定字段添加数据
INSERT INTO 表名  VALUES (数值) 给所有字段加上数据
INSERT INTO 表明(字段名) VALUES (数据) ,(数据)
INSERT INTO 表明 VALUE (数据),(数据)...
/*修改*/
UPDATE 表名 SET 字段名=值, .... WHERE 条件
DELETE 表名 (WHERE 条件) /*删除*/

插入

语句:INSERT

前提:获得权限

用法:

 插入完整的行;

 插入行的一部分;

 插入某些查询的结果。

语法:

INSERT INTO Customers 
VALUES('1000000006', 
 'Toy Land', 
 '123 Any Street', 
 'New York', 
 'NY', 
 '11111', 
 'USA', 
 NULL, 
 NULL);

更加安全的操作,将要插入的列也列出

INSERT INTO Customers(cust_id, 
 cust_name, 
 cust_address, 
 cust_city, 
 cust_state, 
 cust_zip, 
 cust_country, 
 cust_contact, 
 cust_email) 
VALUES('1000000006', 
 'Toy Land', 
 '123 Any Street', 
 'New York', 
 'NY', 
 '11111', 
 'USA', 
 NULL, 
 NULL);
  1. 插入列的一部分,语法和上面一样,只是不需要插入的列可以省略不写,列名不写,对应的数据也不写

  2. 插入检索出的数据

    语法:

    SELECT cust_id, 
     cust_contact, 
     cust_email, 
     cust_name, 
     cust_address, 
     cust_city, 
     cust_state, 
     cust_zip, 
     cust_country 
    FROM CustNew;

    它使用的是列的位置,因此 SELECT 中的第一列(不管

    其列名)将用来填充表列中指定的第一列,第二列将用来填充表列中

    指定的第二列,如此等等

    INSERT SELECT 中 SELECT 语句可以包含 WHERE 子句,以过滤插入的数据。

    INSERT 通常只插入一行。要插入多行,必须执行多个 INSERT 语句。

    INSERT SELECT是个例外,它可以用一条INSERT插入多行,不管SELECT

    语句返回多少行,都

  3. 从一个表复制到另一个表

    CREATE TABLE CustCopy AS 
    SELECT * FROM Customers;

    SELECT INTO 是试验新 SQL 语句前进行表复制的很好工具。先进行复

    制,可在复制的数据上测试 SQL 代码,而不会影响实际的数据。

    更新与删除

    更新:UPDATE 注意要用WHERE 否则会出事

    1. 更新特定行

      语法:

       要更新的表;

       列名和它们的新值;

       确定要更新哪些行的过滤条件。

      UPDATE Customers 
      SET cust_email = 'kim@thetoystore.com' 
      WHERE cust_id = '1000000005';

      UPDATE 语句以 WHERE 子句结束,它告诉 DBMS 更新哪一行。没有 WHERE

      子句,DBMS 将会用这个电子邮件地址更新 Customers 表中的所有行,

      这不是我们希望的。

    2. 更新特定列

      UPDATE Customers 
      SET cust_contact = 'Sam Roberts', 
       cust_email = 'sam@toyland.com' 
      WHERE cust_id = '1000000006';

      在更新多个列时,只需要使用一条 SET 命令,每个“列=值”对之间用

      UPDATE 语句中可以使用子查询,使得能用 SELECT 语句检索出的数据

      更新列数据

      要删除某个列的值,可设置它为 NULL(假如表定义允许 NULL 值)

删除数据

DELETE 注意WHERE 缺少是会出事的

 从表中删除特定的行

 从表中删除所有行。

  1. 删除特定行

    DELETE FROM Customers 
    WHERE cust_id = '1000000006';

    DELETE 语句从表中删除行,甚至是删除表中所有行。但是,DELETE

    不删除表本身

    果想从表中删除所有行,不要使用 DELETE。可使用 TRUNCATE +TABLE +表名

    语句,它完成相同的工作,而速度更快(因为不记录数据的变动)。

  2. 删除全部行

    如果执行 DELETE 语句而不带 WHERE

    子句,表的所有数据都将被删除

DQL (Data Query Language 数据查询语言 )用于查询数据

SELECT + 字段列表
FROM 表名列表
WHERE 条件列表
[[数据库#^aa4a30|GROUP BY 分组字段列表]]
HAVING 分组后条件列表
ORDER BY 排序
LIMIT 分页参数
[[数据库#^1cd456|AS 设置别名]]
WHRER 和 HAVING 的区别,WHERE 在分组之前起作用,HAVING在分组之后起作用

SELECT

SELECT 字段1,... FROM 表 或者 SELECT * FROM
SELECT DISTINCT 去重
SELECT 字段 AS 别名  ... FROM 设置字段的别名

条件

SELECT XXX FROM XXX WHERE +
> >= < <= = <>(不等于,相当于!=) BETWEEN ... AND ... 
IN (...) 满足列表中的其一
LIKE 包含这个字符 模糊匹配
'%a'     //以a结尾的数据
'a%'     //以a开头的数据
'%a%'    //含有a的数据
'_a_'    //三位且中间字母是a的
'_a'     //两位且结尾字母是a的
'a_'     //两位且开头字母是a的
'___'    //含有三个字符的
'[]'     //类似正则表达式
'[^]'    //不包含括号之内的单个字符

聚合函数:将一列数据作为一个整体,进行纵向计算

SELECT +
count 
max
min
avg 平均值
sum
+ 字段列表
+  FROM 表名

分组查询 ^aa4a30

SELECT 字段名 FROM 表 WHERE GROUP BY 分组字段名 HAVING 分组之后的过滤条件

排序查询

SELECT xx FROM 表名 ORDER BY 字段1 排序方式(ASC升序,DESC 降序)
,字段2 XXX 

分页查询

SELECT xx FROM XXX LIMIT (起始索引,查询记录数)

等于OFFSET n ROWS
FETCH NEXY M ROWS ONLY 跳过n行,并返回接下来的m行

DCL (Data Control Language 数据控制语句) 用于操作数据库对象的权限

SELECT * FORM user
CREATE USER '用户名'@'主机名' IDENTIFIED BY '/'/*使用%来指定任意主机都可以访问*/
ALTER USER '用户名' @ '主机名' IDENTIFIED WITH  mysql_native_password BY '新密码'  修改密码
DROP USER '用户名' @ '主机名' 删除用户
/*权限控制*/
SHOW GRANT FOR '用户名'@'主机名'
GRANT 权限列表 ON 数据库名 表名 TO '用户名'@'主机名'
取消权限
REVOKE 权限列表 ON 数据库名 表名 FROM '用户名'@'主机名'

函数 配合SELECT 使用

字符串函数

CONCAT (s1,s2...) 拼接
LOWER(str)
UPPER(str)
LPAD(str,n,pad) 左填充,用pad来填充原字符串,填充n次
RPAD 右填充
TRIM(str) 去掉首位的空格
SUBSTRING(str,start,len) 

数值函数

CEIL() 向上取整
FLOOR() 向下取整
MOD(x,y) x % y
RAND() 0-1内的随机数
ROUND(x,y) 四舍五入保留y位小数

日期函数

CURDATE() 返回当前日期
CURTIME()
NOW() 当前日期和时间
YEAR(date) 获取指定日期的年份
MONTH(date)
DAY(date)
DATE_ADD(date,interval expr type) 返回一个日期/时间加上时间间隔expr后的时间
DATEDIFF(date1,date2) 返回两个日期相差的天数
timestampdiff(间隔类型,前一个时间,后一个时间) 计算日期查
例如: timestampdiff(YEAR,hiredate,now()))

流程函数

if (value,t,f) 如果value true 返回t 否则返回f
IFNULL(v1,v2) 如果v1不为空返回v1,否则返回v2
CASE WHEN (v1) THEN (res1)
WHEN (v2) THEN (res2)
....
ELSE default END

约束 作用域表中字段上的规则,用于限制存储在表中的数据

目的是未来保证数据库中数据的正确性和完整性有效性
分类

非空约束,字段不能为null 关键字: NOT NULL
唯一约束 字段不能重复 UNIQUE
主键约束 主键是一行数据的唯一表示,要求非空且唯一 PRIMARY KEY
默认约束 使用默认值 DEFAULT
检查约束 保证字段值满足某一个条件 CHECK
外键约束 来让两整表之间建立练习,保证数据的一致性 FOREIGN KEY

示例:

create table user  
(  
    name   varchar(10) not null unique comment '姓名不为空且不能重复',  
    age    int check ( age > 0 && age <= 120) comment '年龄检查为0-120',  
    status char(1) default '1' comment '状态默认为1',  
    gender char(1) comment '性别'  
) comment '用户表';

外键

1.建表时
CREATE TABLE (
 ....
 ....
 CONSTRAINT 外键名称 FOREGIN KEY (外键字段名) REFERENCES 主表列名
)
2. ALTER TABLE 表 ADD CONSTRAINT 外键名(自定义) FOREIGN KEY (本表)字段名 REFERENCES 外表+(字段名)

删除/更新

ALTER TABLE 表 ADD CONSTRAINT 外键名(自定义) FOREIGN KEY (本表)字段名 REFERENCES 外表+(字段名) ON UPDATE 更新时的操作  ON DELETE 删除时要执行的操作
操作有
NO ACTION 在父表中删除/更新时,首先检查,该记录是否有外键,如果有则不允许更新或者删除
RESTRICT  同 NO ACTION 
CASCADE 可以删除/更新外键在子表中的记录
SET NULL 设置子表中的外键记录为NULL
SET DEFAULT 设置为一个默认值

多表查询

多表关系:
一对多 多的一方建立外键指向一
多对多 建立第三张中间表,中间表至少包含两个外键,分别关联两方
一对一 任意一方加上外键并设置为UNIQUE

子查询:

查询嵌套,括号内的查询结果作为括号外的条件 例子:

SELECT cust_id 
FROM Orders 
WHERE order_num IN (SELECT order_num 
 FROM OrderItems 
 WHERE prod_id = 'RGAN01');			

或者

SELECT cust_name, 
 cust_state, 
 (SELECT COUNT(*) 
 FROM Orders 
 WHERE Orders.cust_id = Customers.cust_id) AS orders 
FROM Customers 
ORDER BY cust_name;

标量子查询
子查询返回单个值
列子查询,返回结果是一列或者多行
常用操作符号

IN
NOT IN
ANY
SOME  和ANY相同
ALL 

行子查询,返回一行或者是多列

= < > IN NOT IN 

表子查询
返回多行多列
常用IN 来进行查询

联结表

  1. 内联结

比如

进行数据存储的时候,会指定一种联系方式

可以类比为散列表或者map

SELECT vend_name, prod_name, prod_price 
FROM Vendors, Products 
WHERE Vendors.vend_id = Products.vend_id;

正式用法

SELECT vend_name, prod_name, prod_price 
FROM Vendors INNER JOIN Products 
 ON Vendors.vend_id = Products.vend_id;

on之后的式匹配规则,同时为了防止冲突,尽量用点来获取每个不同的库的数据

联结多个表

SELECT prod_name, vend_name, prod_price, quantity 
FROM OrderItems, Products, Vendors 
WHERE Products.vend_id = Vendors.vend_id 
 AND OrderItems.prod_id = Products.prod_id 
 AND order_num = 20007;

2.自联结

联结中仍然可以使用内聚函数

例如

SELECT Customers.cust_id, 
 COUNT(Orders.order_num) AS num_ord 
FROM Customers INNER JOIN Orders 
 ON Customers.cust_id = Orders.cust_id 
GROUP BY Customers.cust_id;

统一表内联结自己

SELECT c1.cust_id, c1.cust_name, c1.cust_contact 
FROM Customers AS c1, Customers AS c2 
WHERE c1.cust_name = c2.cust_name 
 AND c2.cust_contact = 'Jim Jones';

3.自然联结

自然联结排除多次出现,使每一列只返回一次。

自然联结要求你只能选择那些唯一的列,一般通过对一个表使用通配符

(SELECT *),而对其他表的列使用明确的子集来完成。

要自己完成,系统不提供

事实上,我们迄今为止建立的每个内联结都是自然联结,很可能永远都

不会用到不是自然联结的内联结。

4.外联结

联结包含了那些在相关表中没有关联行的行。这种联结

称为外联结。

语法:

內联结:

SELECT Customers.cust_id, Orders.order_num 
FROM Customers INNER JOIN Orders 
 ON Customers.cust_id = Orders.cust_id;

外联结:
使用场景

  • 对每个顾客下的订单进行计数,包括那些至今尚未下订单的顾客;

  • 列出所有产品以及订购数量,包括没有人订购的产品;

  • 计算平均销售规模,包括那些至今尚未下订单的顾客。

SELECT Customers.cust_id, Orders.order_num 
FROM Customers LEFT OUTER JOIN Orders 
 ON Customers.cust_id = Orders.cust_id;

在使用 OUTER

JOIN 语法时,必须使用 RIGHT 或 LEFT 关键字指定包括其所有行的表

(RIGHT 指出的是 OUTER JOIN 右边的表,而 LEFT 指出的是 OUTER JOIN

左边的表)。

也就是left 将要联结左侧的那个表全部选出

right 将右侧的那个表全选出

全外联结

Access、MariaDB、MySQL、Open Office Base 和 SQLite 不支持 FULL

还存在另一种外联结,就是全外联结(full outer join),它检索两个表中

的所有行并关联那些可以关联的行。与左外联结或右外联结包含一个表

的不关联的行不同,全外联结包含两个表的不关联的行。

联结使用条件

 注意所使用的联结类型。一般我们使用内联结,但使用外联结也有效。

 关于确切的联结语法,应该查看具体的文档,看相应的 DBMS 支持何种语法(大多数 DBMS 使用这两课中描述的某种语法)。

 保证使用正确的联结条件(不管采用哪种语法),否则会返回不正确

的数据。

 应该总是提供联结条件,否则会得出笛卡儿积。

 在一个联结中可以包含多个表,甚至可以对每个联结采用不同的联结

类型。虽然这样做是合法的,一般也很有用,但应该在一起测试它们

前分别测试每个联结。这会使故障排除更为简单。

join区别

普通的join是笛卡尔积,即为两张表的排列组合

起别名

在使用时直接用AS +别名就可以用了 ^1cd456

只能每次用的时候起一次别名并且当时使用

下一个语句别名就不能用了,要重新起名字

UNION

语法:

适用于从多个不同的表中挑选不同的列

UNION 中的每个查询必须包含相同的列、表达式或聚集函数(不过,

各个列不需要以相同的次序列出)

UNION 从查询结果集中自动去除了重复的行;换句话说,它的行为与一

条 SELECT 语句中使用多个 WHERE 子句条件一样。

这是 UNION 的默认行为,如果愿意也可以改变它。事实上,如果想返回

所有的匹配行,可使用 UNION ALL 而不是 UNION

SELECT cust_name, cust_contact, cust_email 
FROM Customers 
WHERE cust_state IN ('IL','IN','MI') 
UNION 
SELECT cust_name, cust_contact, cust_email 
FROM Customers 
WHERE cust_name = 'Fun4All';

排序

SELECT 语句的输出用 ORDER BY 子句排序。在用 UNION 组合查询时,只

能使用一条 ORDER BY 子句,它必须位于最后一条 SELECT 语句之后。对

于结果集,不存在用一种方式排序一部分,而又用另一种方式排序另一部分的情况,因此不允许使用多条 ORDER BY 子句

用它来排序所有 SELECT 语句返回的所有结果。

建表

CREATE TABLE A
( 
 prod_id CHAR(10) NOT NULL, 
 vend_id CHAR(10) NOT NULL, 
 prod_name CHAR(254) NOT NULL, 
 prod_price DECIMAL(8,2) NOT NULL, 
 prod_desc VARCHAR(1000) NULL 
);

要指定行列,并且后跟列的数据

类型

CREATE TABLE Orders 
( 
 order_num INTEGER NOT NULL, 
 order_date DATETIME NOT NULL, 
 cust_id CHAR(10) NOT NULL 
);

不要把 NULL 值与空字符串相混淆。NULL 值是没有值,不是空字符串。

如果指定’’(两个单引号,其间没有字符),这在 NOT NULL 列中是允

许的。空字符串是一个有效的值,它不是无值。NULL 值用关键字 NULL

而不是空字符串

默认值在 CREATE TABLE 语句的列定义中用关键字 DEFAULT 指定

更新表定义,可以使用 ALTER TABLE 语句。

使用 ALTER TABLE 更改表结构,必须给出下面的信息:

 在 ALTER TABLE 之后给出要更改的表名(该表必须存在,否则将

出错);

 列出要做哪些更改

ALTER TABLE Vendors 
ADD vend_phone CHAR(20);

更改或删除列、增加约束或增加键,这些操作也使用类似的语法(注意,

下面的例子并非对所有 DBMS 都有效):

输入▼

ALTER TABLE Vendors

DROP COLUMN vend_phone;

使用 ALTER TABLE 要极为小心,应该在进行改动前做完整的备份(表

结构和数据的备份)。数据库表的更改不能撤销,如果增加了不需要的

列,也许无法删除它们。类似地,如果删除了不应该删除的列,可能

会丢失该列中的所有数据。

删除表(删除整个表而不是其内容)非常简单,使用 DROP TABLE 语句即可:

DELETE 只删除表的内容不删除表本身

重新命名

ALTER TABLE oldname RENAME TO newname;

视图

视图为虚拟的表。它们包含的不是数据而是根据需要检索数据的查询。

视图提供了一种封装 SELECT 语句的层次,可用来简化数据处理,重新

格式化或保护基础数据。

用视图将联结集合起来,也就是一个子查询,相当于一个API

创建 视图用 CREATE VIEW 语句来创建。与 CREATE TABLE 一样,CREATE VIEW

删除 删除视图,可以使用 DROP 语句,其语法为 DROP VIEW viewname;。

覆盖(或更新)视图,必须先删除它,然后再重新创建。

用法:

建立视图

CREATE VIEW ProductCustomers AS 
SELECT cust_name, cust_contact, prod_id 
FROM Customers, Orders, OrderItems 
WHERE Customers.cust_id = Orders.cust_id 
 AND OrderItems.order_num = Orders.order_num;

这条语句创建一个名为 ProductCustomers 的视图,它联结三个表,返

回已订购了任意产品的所有顾客的列表。

使用视图

使用时,

SELECT cust_name, cust_contact 
FROM ProductCustomers 
WHERE prod_id = 'RGAN01';

也可以用于统一格式

CREATE VIEW VendorLocations AS 
SELECT RTRIM(vend_name) || ' (' || RTRIM(vend_country) || ')' 
 AS vend_title 
FROM Vendors;

之后可用

SELECT * 
FROM VendorLocations;

用于过滤也一样

CREATE VIEW CustomerEMailList AS 
SELECT cust_id, cust_name, cust_email 
FROM Customers 
WHERE cust_email IS NOT NULL;

之后

SELECT * 
FROM CustomerEMailList;

计算字段

CREATE VIEW OrderItemsExpanded AS 
SELECT order_num, 
 prod_id, 
 quantity,
 item_price, 
 quantity*item_price AS expanded_price 
FROM OrderItems;

之后

SELECT * 
FROM OrderItemsExpanded 
WHERE order_num = 20008;

存储(不会

可以创建存储过程。简单来说,存储过程就是为以后使用而保存的一条

或多条 SQL 语句。可将其视为批文件,虽然它们的作用不仅限于批处理

 通过把处理封装在一个易用的单元中,可以简化复杂的操作(如前面

例子所述)。

 由于不要求反复建立一系列处理步骤,因而保证了数据的一致性。如

果所有开发人员和应用程序都使用同一存储过程,则所使用的代码都

是相同的。

EXECUTE AddNewProduct( ‘JTS01’,

‘Stuffed Eiffel Tower’,

6.49,

‘Plush stuffed toy with the text La

➥Tour Eiffel in red white and blue’ );

管理事务处理(不会深入学习的时候再看)

使用事务处理(transaction processing),通过确保成批的 SQL 操作要么

完全执行,要么完全不执行,来维护数据库的完整性

 事务(transaction)指一组 SQL 语句;

 回退(rollback)指撤销指定 SQL 语句的过程;

 提交(commit)指将未存储的 SQL 语句结果写入数据库表;

 保留点(savepoint)指事务处理中设置的临时占位符(placeholder),

可以对它发布回退(与回退整个事务处理不同)。

事务处理用来管理 INSERT、UPDATE 和 DELETE 语句。不能回退 SELECT

语句(回退 SELECT 语句也没有必要),也不能回退 CREATE 或 DROP 操

作。事务处理中可以使用这些语句,但进行回退时,这些操作也不撤销。

游标

有时,需要在检索出来的行中前进或后退一行或多行,这就是游标的用

途所在。游标(cursor)是一个存储在 DBMS 服务器上的数据库查询,

它不是一条 SELECT 语句,而是被该语句检索出来的结果集。在存储了

游标之后,应用程序可以根据需要滚动或浏览其中的数据。

游标的选项和特性

 能够标记游标为只读,使数据能读取,但不能更新和删除。

 能控制可以执行的定向操作(向前、向后、第一、最后、绝对位置、

相对位置等)。

 能标记某些列为可编辑的,某些列为不可编辑的。

 规定范围,使游标对创建它的特定请求(如存储过程)或对所有请求

可访问。

 指示 DBMS 对检索出的数据(而不是指出表中活动数据)进行复制,

使数据在游标打开和访问期间不变化。

游标使用

 在使用游标前,必须声明(定义)它。这个过程实际上没有检索数据,

它只是定义要使用的 SELECT 语句和游标选项。

 一旦声明,就必须打开游标以供使用。这个过程用前面定义的 SELECT

语句把数据实际检索出来。

 对于填有数据的游标,根据需要取出(检索)各行。

 在结束游标使用时,必须关闭游标,可能的话,释放游标(有赖于具

体的 DBMS)。

声明游标后,可根据需要频繁地打开和关闭游标。在游标打开时,可根

据需要频繁地执行取操作。

创建游标

使用 DECLARE 语句创建游标,这条语句在不同的 DBMS 中有所不同。

DECLARE 命名游标,并定义相应的 SELECT 语句,根据需要带 WHERE 和

其他子句。为了说明,我们创建一个游标来检索没有电子邮件地址的所

有顾客,作为应用程序的组成部分,帮助操作人员找出空缺的电子邮件

地址

语法

DECLARE CustCursor CURSOR 
FOR 
SELECT * FROM Customers 
WHERE cust_email IS NULL

使用游标

OPEN CURSOR CustCursor

关闭游标

CLOSE CustCursor 
DEALLOCATE CURSOR CustCursor

约束(不会捏)

DBMS 通过在数据库表上施加约束来实施引用完整性。大多数约束是在

表定义中定义的,如第 17 课所述,用 CREATE TABLE 或 ALTER TABLE

语句。

主键

我们在第 1 课简单提过主键。主键是一种特殊的约束,用来保证一列(或

一组列)中的值是唯一的,而且永不改动。换句话说,表中的一列(或

多个列)的值唯一标识表中的每一行。这方便了直接或交互地处理表中

的行。没有主键,要安全地 UPDATE 或 DELETE 特定行而不影响其他行会

非常困难。

表中任意列只要满足以下条件,都可以用于主键。

 任意两行的主键值都不相同。

 每行都具有一个主键值(即列中不允许 NULL 值)。

 包含主键值的列从不修改或更新。(大多数 DBMS 不允许这么做,但

如果你使用的 DBMS 允许这样做,好吧,千万别!)

 主键值不能重用。如果从表中删除某一行,其主键值不分配给新行。

一种定义主键的方法是创建它,如下所示

CREATE TABLE Vendors 
( 
 vend_id CHAR(10) NOT NULL PRIMARY KEY, 
 vend_name CHAR(50) NOT NULL, 
 vend_address CHAR(50) NULL, 
 vend_city CHAR(50) NULL, 
 vend_state CHAR(5) NULL, 
 vend_zip CHAR(10) NULL, 
 vend_country CHAR(50) NULL 
);

在此例子中,给表的 vend_id 列定义添加关键字 PRIMARY KEY,使其成

为主键。

ALTER TABLE Vendors 
ADD CONSTRAINT PRIMARY KEY (vend_id);

这里定义相同的列为主键,但使用的是 CONSTRAINT 语法。此语法也可

以用于 CREATE TABLE 和 ALTER TABLE 语句

外键

外键是表中的一列,其值必须列在另一表的主键中。外键是保证引用完

整性的极其重要部分。我们举个例子来理解外键

如第 6 课所述,除帮助保证引用完整性外,外键还有另一个重要作用。

在定义外键后,DBMS 不允许删除在另一个表中具有关联行的行。例

如,不能删除关联订单的顾客。删除该顾客的唯一方法是首先删除相

关的订单(这表示还要删除相关的订单项)。由于需要一系列的删除,

因而利用外键可以防止意外删除数据。

有的 DBMS 支持称为级联删除(cascading delete)的特性。如果启用,

该特性在从一个表中删除行时删除所有相关的数据。例如,如果启用

级联删除并且从 Customers 表中删除某个顾客,则任何关联的订单行

也会被自动删除。

CREATE TABLE Orders 
( 
 order_num INTEGER NOT NULL PRIMARY KEY, 
 order_date DATETIME NOT NULL, 
cust_id CHAR(10) NOT NULL REFERENCES 
➥Customers(cust_id) 
);

索引

CREATE [ UNIQUE | FULLTEXT ] INDEX index_name ON table_name (inex_col_name,... ) ; UNIQUE 代表的是一个唯一的索引,不可重复
SHOW INDEX FROM table_name ;
iDROP INDEX index_name ON table_name ;

触发器

> 触发器是特殊的存储过程,它在特定的数据库活动发生时自动执行。触发
>
> 器可以与特定表上的 INSERT、UPDATE 和 DELETE 操作(或组合)相关联。
>
> 与存储过程不一样(存储过程只是简单的存储 SQL 语句),触发器与单
>
> 个的表相关联。与 Orders 表上的 INSERT 操作相关联的触发器只在
>
> Orders 表中插入行时执行。类似地,Customers 表上的 INSERT 和
>
> UPDATE 操作的触发器只在表上出现这些操作时执行。

> 触发器内的代码具有以下数据的访问权:
>
>  INSERT 操作中的所有新数据;
>
>  UPDATE 操作中的所有新数据和旧数据;
>
>  DELETE 操作中删除的数据。
>
> 根据所使用的 DBMS的不同,触发器可在特定操作执行之前或之后执行。
>
> 下面是触发器的一些常见用途。
>
>  保证数据一致。例如,在 INSERT 或 UPDATE 操作中将所有州名转换
>
> 为大写。
>
>  基于某个表的变动在其他表上执行活动。例如,每当更新或删除一行
>
> 时将审计跟踪记录写入某个日志表。

```mysql
CREATE TRIGGER customer_state 
ON Customers 
FOR INSERT, UPDATE 
AS 
UPDATE Customers 
SET cust_state = Upper(cust_state) 
WHERE Customers.cust_id = inserted.cust_id;

case 来进行多条件判断 不要忘记END

CASE WHEN XXX条件 THEN 满足的结果 ELSE 不满足结果 END

多行耦合

CASE WHEN XXX条件1 THEN Y1
	 WHEN XXX 条件2 THEN  Y2
	 WHEN XXX 条件3 THEN Y3 ELSE Y4 END 
	 

事务

一组操作的集合,不可分割,这些操作要么同时成功,要么同时失败,mysql默认提交方式是自动提交的,所以要改为手动提交才行

方式1

select @@autocommit; 查看提交方式,返回为1则为自动提交,否则为手动提交
set @@autocommit = 0 ; 设置为手动提交

commit 提交

rollback 回滚

方式二
事务操作

start transaction 或者 begin 开启事务
commit 提交
rollback 回滚 

事务特点

  • 原子性 一个事务(transaction)中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。

  • 一致性 在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表示写入的资料必须完全符合所有的预设规则,这包含资料的精确度、串联性以及后续数据库可以自发性地完成预定的工作。

  • 隔离性 数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。事务隔离分为不同级别,包括读未提交(Read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串行化(Serializable)。

  • 持久性 事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。

并发事务问题

  • 脏读 一个事务读到另一个事务还没有提交的数据
  • 不可重复读,一个事务先后读取一条重复记录,但两次读取的数据不同,成为不可重复读
  • 幻读 一个事务按照条件查询时,没有对应的数据行但是在插入数据时,这行数据已经存在了,出现了幻影
    事务隔离级别

    可以手动设置系统的隔离级别

引擎

CREATE TABLE 表名(

字段1 字段1类型 [ COMMENT 字段1注释 ] ,

......

字段n 字段n类型 [COMMENT 字段n注释 ]

) ENGINE = INNODB [ COMMENT 表注释 ] ;
show engines;

InnoDB特点
支持事务,行级锁,外键约束

全局锁:锁定数据库中的所有表。
表级锁:每次操作锁住整张表。
行级锁:每次操作锁住对应的行数据。

常用函数

 1)、length():mysql里面的length()函数是一个用来获取字符串长度的内置函数。
  2)、char_length():在mysql内置函数里面查看字符串长度的还有一个函数是char_length()。
  3)、这两个函数的区别是:
    a)、length(): 单位是字节,utf8编码下,一个汉字三个字节,一个数字或字母一个字节。gbk编码下,一个汉字两个字节,一个数字或字母一个字节。
    b)、char_length():单位为字符,不管汉字还是数字或者是字母都算是一个字符。

正则表达式

^:表示一个字符串或行的开头

[a-z]:表示一个字符范围,匹配从 a 到 z 的任何字符。

[0-9]:表示一个字符范围,匹配从 0 到 9 的任何字符。

[a-zA-Z]:这个变量匹配从 a 到 z 或 A 到 Z 的任何字符。请注意,你可以在方括号内指定的字符范围的数量没有限制,您可以添加想要匹配的其他字符或范围。

[^a-z]:这个变量匹配不在 a 到 z 范围内的任何字符。请注意,字符 ^ 用来否定字符范围,它在方括号内的含义与它的方括号外表示开始的含义不同。

[a-z]*:表示一个字符范围,匹配从 a 到 z 的任何字符 0 次或多次。

[a-z]+:表示一个字符范围,匹配从 a 到 z 的任何字符 1 次或多次。

.:匹配任意一个字符。

\.:表示句点字符。请注意,反斜杠用于转义句点字符,因为句点字符在正则表达式中具有特殊含义。还要注意,在许多语言中,你需要转义反斜杠本身,因此需要使用\\.。

$:表示一个字符串或行的结尾。

JDBC

//导包
import com.mysql.jdbc.Driver;  
  
import javax.swing.plaf.nimbus.State;  
import java.sql.*;  
public class Name {  
public static void main(String[] args) throws Exception {  
//注册驱动程序  
Class.forName("com.mysql.jdbc.Driver");  
//获取连接  
String url = "jdbc:mysql://localhost:3306/t";  
String username = "root";  
String password = "hutao1224";  
Connection conn = DriverManager.getConnection(url,username,password);  
//获取执行sql语句的对象  
String sql = "insert into employees (name)value ('Amy');";  
Statement stmt = conn.createStatement();  
//执行sql语句  
int count = stmt.executeUpdate(sql);  
//处理结果  
System.out.println(count);  
//释放资源  
stmt.close();  
conn.close();
}  
  
}

防止sql注入使用


 @Test
public void testPreparedStatement() throws  Exception {
    //2. 获取连接:如果连接的是本机mysql并且端口是默认的 3306 可以简化书写
    String url = "jdbc:mysql:///db1?useSSL=false";
    String username = "root";
    String password = "1234";
    Connection conn = DriverManager.getConnection(url, username, password);

    // 接收用户输入 用户名和密码
    String name = "zhangsan";
    String pwd = "' or '1' = '1";

    // 定义sql
    String sql = "select * from tb_user where username = ? and password = ?";
    // 获取pstmt对象
    PreparedStatement pstmt = conn.prepareStatement(sql);
    // 设置?的值
    pstmt.setString(1,name);//?的位置和参数
    pstmt.setString(2,pwd);
    // 执行sql
    ResultSet rs = pstmt.executeQuery();// 不需要传递sql语句了
    // 判断登录是否成功
    if(rs.next()){
        System.out.println("登录成功~");
    }else{
        System.out.println("登录失败~");
    }
    //7. 释放资源
    rs.close();
    pstmt.close();
    conn.close();
}

变量的使用

占位符?只能用来取代sql语句中的常变量,而不能取代列名或者表名
select ? from ? where  ?   前两个?不成立,只能用 "+variable+" 的方式

Redis

面试常问

  • 一致性哈希:整个哈希值的空间被视为一个环形,每个节点或数据都被映射到整个环上,当需要查找某个键时,会沿着环查找第一个匹配的节点。
  • 数据结构:
    • SkipList跳表,ZSet中如果保存到键值对数量<128 && 每个元素的长度小于64B就使用ziplist,否则则使用skiplist
      • 数据按照升序排序存储
      • 节点可能包含多个指针,指针跨度不同
    • SortedSet是有序集合,底层存储的每个数据都包含element和score两个值,score是得分,element是字符串值,会根据element和score值排序,形成有序集合,基于SkipList实现的
    • Redis如何判断KEY是否过期呢?
      • :在Redis中会有两个Dict,也就是HashTable,其中一个记录KEY-VALUE键值对,另一个记录KEY和过期时间。要判断一个KEY是否过期,只需要到记录过期时间的Dict中根据KEY查询即可。
  • 集群问题:
    1. Sentinel是哨兵:监控节点状态并自动实现故障转移 |400
      • 哨兵的作用:状态监控,故障转移,状态通知
      • 主观下线是某一个sentinel节点发现某个节点没在规定时间内响应,客观下线是指的是有一半以上的sentinel都认为下线了才行
      • 选举新的master:
      • 选举规则:判断优先级,判断offset值,越高越优先,判断id,越小越优先
        1. 故障检测:sentinel检测主节点和从节点的在线情况,如果有半数以上的sentinel认为主节点故障,就会开始选举
        2. sentinel选举出一个leader节点,每个sentinel都有资格,sentinel确定master下线之后就会请求其他sentinel同意自己成为leader,票数大于一半的sentinel即可成为leader,如果没有选出,就重复直到选出
        3. 选举根据 数据复制的偏移量更大(数据最新),运行id更小(运行时间更长 ),节点的优先级进行选举
        4. 通知所有的从节点设置新的主节点,并且进行复制
        5. 通知客户端重新连接主节点
      • 主从复制:
        • 全量同步,当slave第一次连接到master或者slave断开太久了,repl_baklog(复制积压缓冲区)的offset已经被覆盖了,同步过程中收到的读写命令都会先执行然后记录在repl_baklog中,逐个发送给slave |550
          1. 从节点保存主节点信息,与主节点建立连接,主节点判断是否是第一次请求,是就与从节点同步版本信息
          2. 主节点fork一个子进程保存当前所有的数据,2.发送数据快照3.之后主节点每次执行其他的操作都会同步给从节点
            • **Replication Id**:简称replid,是数据集的标记,replid一致则是同一数据集。每个master都有唯一的replidslave则会继承master节点的replid

            • **offset**:偏移量,随着记录在repl_baklog中的数据增多而逐渐增大。slave完成同步时也会记录当前同步的offset。如果slaveoffset小于masteroffset,说明slave数据落后于master,需要更新。

        • 增量同步:|750
          • 根据offset来进行同步
          • 具体的比较是通过repl_baklog,记录Redis处理过的命令及offset,包括master当前的offset,和slave已经拷贝到的offset发送的是命令:
      • 防止脑裂问题:设置主节点最少需要的从节点数,小于这个数量主节点就会禁止写数据,设置主从复制和同步的最大延迟,如果时间超过这个延迟就会禁止写数据。同时可以用主观下线和客观下线,当出现网络分区时,有部分的节点仍然能够连接上主节点,于是就不会出现重新选举主节点。
      • offset复制偏移量:主从各自维护自己的offset,子节点把自己的offset上报,master保存offset ,部分复制使用二者的偏移量来同步缺失的数据
    2. 分片集群:
      • 每个master保存不同的数据,然后每个master都可以有多个slave节点,之后master之间通过ping来检测彼此之间的健康状态,客户端请求可以访问集群任意节点,最终都会被转发到数据所在的节点
      • 故障转移:每个master转移到自己的slave即可
      • 为什么插槽的数量是16384,16384是2的14次方,是一个平衡性、行呢个和兼容性之间取得折衷的结果
      • 如何将同一类数据固定的保存在同一个Redis实例?
        • Redis计算key的插槽值时会判断key中是否包含{},如果有则基于{}内的字符计算插槽
        • 所以只要用{key} 作为前缀即可把相同的类型的数据计算的插槽一定相同
    3. 常见的缓存有几种:
      • 旁路缓存:使用应用程序来保证缓存和数据库的一致性
        1. 先写DB然后直接删除cache
          • 为何删除? 1.如果db频繁更改,导致cache中的数据很少被访问,删除可以节省服务端的资源 2. 更新cache更容易造成缓存不一致的现象
          • 为何不能先删除cache? 因为 如果有两个请求同时访问,请求1先把cache中的数据删除了,请求二就会从db中读取数据,然后请求一再更改db,会导致请求2读到的是旧值
          • 写cache的速度要比写db快很多,所以很少会造成不一致
        2. 从cache中读取数据,读不到再从db中读入并返回,然后放到cache中
        3. 缺点:
          • 首次请求不在cache中:可以将热点数据提前放入
      • 读写穿透:以cache为主,让cache服务操作,不需要
        • 写:先写cache,cache没有时直接写db,有的话先更新cache,之后更新db 同步更新
        • 读:直接读cache,cache没有直接读db,然后放入cache.
      • 异步缓存写入:也是由cache服务来处理,但是更新时,只更新cache,由异步处理来更新db
        • 缺点:容易造成数据不一致
        • 适合对数据一致性要求没那么 高的
    4. Redis单线程模型:Redis对于每一个客户端的连接都关联一个指令队列和响应队列
      • 为何单线程性能还这么高?
        1. 使用纯内存访问,
        2. 单线程避免不必要的上下文切换和竞争
        3. IO多路复用,对于多个IO,Redis每次处理其中一个IO然后暂停对其他的IO事件。使用一个线程来监听多个socket某个socket可读时及逆行读写
          • 实现:select(文件描述符有上限),poll这俩只会通知用户有有Socket就绪,不确定具体的是哪个,需要轮询来找
          • epoll会通知用户的时候把哪个socket也直接写入用户空间
          • 详细介绍一下为什么I/O复用
          1. Redis是纯内存的,所以性能取决于网络延迟,I/O多路复用实现了高效的网络请求
            1. 常见的IO有 阻塞,非阻塞,多路复用
            2. 非阻塞式IO,读到多少就都多少,写多少,不会等待满足字节要求
      • 优点:避免了线程切换的消耗。
    • 主从复制: 主服务器执行写操作时,会将写操作同步给从服务器,从服务器只读,并执行主服务器同步过来的指令 缺点:主服务器宕机时必须手动恢复
    • 哨兵模式: 监控主从服务器,提供主从节点故障转移的功能
    • 切片集群:将数据分布在不同的服务器上,以此来降低系统对单主节点的依赖,从而提高 Redis 服务的读写性能。
    • 脑裂问题: 由于网络问题,集群节点之间失去联系。主从数据不同步;重新平衡选举,产生两个主服务。等网络恢复,旧主节点会降级为从节点,再与新主节点进行同步复制的时候,由于会从节点会清空自己的缓冲区,所以导致之前客户端写入的数据丢失了。 解决: 当主节点发现从节点下线或者通信超时的总数量大于阈值时,那么禁止主节点进行写数据,直接把错误返回给客户端。
  • 数据相关:
    • 数据淘汰策略:
      • 如何保证Redis中的数据是热点数据? 答案是可以使用LRU删除策略,每次删除数据时删除最近最少使用的键,同时也可以主动去更新热点数据
      • 其余的各种内存淘汰机制:1.内存到达限制时返回错误,不删除,2.删除最近最少使用键,3.lru但是只删除设置了过期时间的键,4.随机删除一些 5. 随机删除一些设置了过期时间的键6. 对访问频率来进行删除
    • 持久化:
      • RDB:创建数据的快照,存的是实际的数据
        • redis建立新的子进程,父进程继续处理请求,子进程负责将内存内容写到临时文件,os的实时复写copy-on-write会使得父进程和子进程共享一个物理页面,所以会为父进程要修改的页面创建副本,所以子进程内的数据就是fork时的快照。 子进程写完文件之后,使用临时文件代替原来的快照文件,之后子进程quit, 进行的是全量快照,会把整个数据全部保存
      • AOF:
        • 重写:AOF重写会调用一个子进程,由子进程去进行重写,会读取数据库中的键值对为每一个键值对生成一个或多个写命令,这些写命令足以恢复这个键值对的状态。然后把他们写入一个新的AOF文件中,同时Redis会维护一个AOF缓冲区,把重写期间进行的数据库写操作记录到缓冲区中,重写完毕之后会把新的AOF文件发给主进程,然后退出,主进程把缓冲区中的写命令写入其中并使用新的AOF文件替换旧的AOF文件
    • 注意:实现原理是操作系统会为进程
      • AOF:记录操作命令而不是副本。
        1. fork,子进程向临时文件中写入重建数据库状态的命令
        2. 父进程接收到请求后,把写命令写入到原来的aof文件中,然后缓存起来这些命令
        3. 子进程搞完之后,通知父进程,父进程把缓存起来的命令写入临时文件
        4. 使用临时文件替代老文件,注意不会读取老文件
      • 混合持久化:两者结合,惰性删除:只有在访问这个键的时候才检查是否过期
      1. 读写分离问题:读占比较大时可以把一部分的流量摊到从节点,只对主节点进行写服务。
    • 过期策略:惰性删除和定时删除,Redis使用了两种结合的方式:当某个key被访问时,会定期检查是否过期,如果过期就删除,同时会定期对一部分key进行检查,如果过期就删除
      • 周期删除的模式:
        1. SLOW模式:通过定时任务定期抽样部分带有TTL的key,判断是否过期。如果过期key比例较高会多次抽象
        2. FAST模式:Redis每次处理NIO事件之前,都会抽样部分带有TTL的key,判断是否过程,因此频率较高。如果时间充足并且过期key比例过高,也会多次抽样。

缓存问题

  • 缓存问题:
    • 缓存雪崩:大量缓存 在同一时间失效或者Redis宕机导致后面的请求都直接落到了db
      • 修改key的TTL,设置随机TTL
        1. 将缓存失效时间打散,在失效时间基础上加一个随机值
        2. 设置缓存不过期
      • 搭建Redis的集群,哨兵模式,集群模式
        • 给业务限流,nginx和spring cloud gateway
      • 使用多级缓存,Guava和Caffeine + Redis, 前面这俩是缓存在服务中的JVM中的,分布式项目中不能跨服务,但是请求速度是最快的,因为是本地的缓存
      • 给缓存业务添加降级限流策略
    • 缓存击穿:热点key过期,大量的请求直接给数据库压力
      • 互斥锁方案,保证同一时间只能有一个业务线程请求业务缓存
      • 不给热点数据设置过期时间,由异步更新缓存,或者在热点数据要过期的时候,提前通知前台线程更新缓存或者重新设置过期时间,保证高可用,数据不会是绝对一致的
    • 缓存穿透:访问的数据不在缓存中,每次都直接查询数据库,给数据库很大压力 ,解决方案是使用布隆过滤器
      • 非法请求的限制,当有大量恶意请求访问不存在的数据时,在API入口要判断请求参数是否合理
      • 设置空值或者默认值
      • 使用布隆过滤器快速判断数据是否存在,避免通过查询数据库来判断数据是否存在
      • 布隆过滤器对一个数据用多个哈希函数来映射,当查询时,如果多个哈希函数的映射后任何一个索引都为0,则一定不存在,如果所有的映射后的位置都是1时,则可能存在集合中。使用的是位图
    • 数据一致性
      • 双写一致问题:
        • 保证一致性
          1. 写使用的延迟双删,删两次缓存,等数据库修改完之后再删除一次缓存,用于解决数据库主从一致性问题,与缓存同步为u管。
          2. Cache Aside 旁路缓存策略
          - 写策略:先更新数据库再删除缓存中的数据
          - 读策略:如果命中了缓存,直接返回,否则从数据库中读入数据并写入缓存,返回给用户
          3. Read/Write Through 读穿/写穿策略: 应用程序只与缓存交互,由缓存组件和数据库交互,
          - 读为名周直接查数据库然后写入缓存
          - 写未命中,缓存存在直接写缓存,然后由缓存组件去更新数据库。缓存不存在则直接更新数据库
          4. Write Back(写回)策略
          • 使用读锁(共享锁)和写锁(排他锁)
        • 允许短暂不一致:
          • 使用消息队列,先修改数据库,数据库修改完之后向MQ发送消息,由cahe服务接受消息来对缓存进行更新
          • 取决于mq的可靠性
  • 延时队列: 把当前要做的事情推迟一段时间再做,如下单未付款取消
    • 可以使用 Zset来纯理,Score属性来存储延迟执行的时间
    • zadd score1 value1;
    • 大key value的值很大
  • 分布式问题:
    1. 分布式锁:setnx 实际使用应该使用 set key value nx ex time 因为这个可以直接在原子性指定时间
    2. Reddsion来续锁:使用lua脚本,来保证原子性
      1. 一个线程获得锁,然后在事务中调用了另一个事务,另一个事务也同样对相同的锁加锁,会发生什么?
        • 如果是直接使用Redis,会阻塞,然后等锁过期后,被调用的函数可以执行,被调用的函数执行之后会释放锁,返回到第一个函数执行,第一个函数执行之后就会重复释放这个锁,无法保证原子性了。
        • 如果使用Redssion,Redssion的RLock数据结构实现了可重入锁:
          • key,status持有还是释放 ,UUID标识线程 , 持有时间(最长的存活时间)
          • Redis中存的value字段为锁的次数
{
  "myLock": {
    "status": "LOCKED",
    "owner": "some-unique-uuid",
    "hold_count": 2
  }
}			
        - 这个问题中就会直接判断出是同一个线程的调用,所以只会给锁的持有技术+1,不会阻塞
  • multi开始事务,discard取消事务redis的事务在出错时,只会回滚出错的命令

  • 使用watch key 来对某一个key加上乐观锁

  • Redis 与 Memcached 区别

    1. Redis支持的数据类型更丰富:Sting Hash List Set Zset, M只支持key-value数据类型
    2. Redis支持数据的持久化,可以将内存中的数据保存在磁盘中。
    3. Redis原生支持集群模式
    4. Redis支持发布订阅模型,LUa吉奥本,事务等
  • 数据类型

    • String 类型的应用场景:SDS,可以存任何类型,直接存在二进制buf数组里
    • List 实现是:
    • Hash 类型:缓存对象、购物车等。
    • Set 类型:聚合计算(并集、交集、差集)场景,比如点赞、共同关注、抽奖活动等。
    • Zset 类型:排序场景,比如排行榜、电话和姓名排序等。
    • BitMap(2.2 版新增):二值状态统计的场景,比如签到、判断用户登陆状态、连续签到用户总数等;
    • HyperLogLog(2.8 版新增):海量数据基数统计的场景,比如百万级网页 UV 计数等;
    • GEO(3.2 版新增):存储地理位置信息的场景,比如滴滴叫车;
    • Stream(5.0 版新增):消息队列,相比于基于 List 类型实现的消息队列,有这两个特有的特性:自动生成全局唯一消息ID,支持以消费组形式消费数据。
  • Redis在执行命令的时候是单线程,但是也使用了多线程的来处理网络IO操作

    • 不是,Redis会启动后台: 三个线程各自有自己的任务队列,
      1. 处理关闭文件
      2. 处理AOF刷盘
      3. 异步释放Redis内存,也就是lazyfree线程
      4. 主线程,负责执行命令
      5. 三个I/O线程来分担网络I/O压力
    • 这些任务的操作都是很耗时的,如果把这些任务都放在主线程来处理,那么 Redis 主线程就很容易发生阻塞,这样就无法处理后续的请求了。
  • 过期删除和内存淘汰: Redis使用懒惰性删除和定期删除。 惰性删除时指不主动删除过期键,每次反问key时检查是否过期,如果过期,则删除。 优点是:减少对系统资源的使用,缺点是:不能及时释放内存

    • 定期删除:每过一段时间,从数据库中取出一定数量的key来进行检查,删除其中的过期key,如果过期key超过一定比例,重复执行定期删除
    • 当 Redis 运行在主从模式下时,从库不会进行过期扫描,从库对过期的处理是被动的。也就是即使从库中的 key 过期了,如果有客户端访问从库时,依然可以得到 key 对应的值,像未过期的键值对一样返回。
    • 从库的过期键处理依靠主服务器控制,主库在 key 到期时,会在 AOF 文件里增加一条 del 指令,同步到所有的从库,从库通过执行这条 del 指令来删除过期的 key。REmote DIctionary Server(Redis) 是一个由 Salvatore Sanfilippo 写的 key-value 存储系统,是跨平台的非关系型数据库,非关系型数据库,
  • 内存淘汰: 随机淘汰,LRU最近最少使用,根据最后一次访问的时间 , LFU最近最不常使用,根据访问次数来淘汰

  • Lua :Redis 在执行 Lua 脚本时,可以以原子性的方式执行

  • Lua脚本使用

    1. EVAL执行脚本 EVAL script numkeys key [key …] arg [arg …]
    2. SCRIPT LOAD script.lua 把脚本加载到redis-serve中,返会一个 SHA1校验和,之后无论是哪个客户端都可以使用这个校验和来运行脚本。
    3. EVALSHA sha1校验和 numkeys key [key …] arg [arg …]
    4. SCRIPT EXISTS sha1来检验这个脚本是否还在
    5. SCRIPT FLUSH刷新所有已经保存的脚本
    6. redis-cli -a 密码 –eval lua脚本内容
  • 对于基本数据类型的操作

    1. string
      • redis中的string可以包含任何数据,包括jpg和序列化的对象,因为string是byte [] 数组
      • setrange key number string 从number开始的下标处替换string,从0开始
      • mset 设置多个
      • xxnx not exit 不存在的话在设置
      • getrange key begin end
      • incryby key 增量 修改数据
      • append key 加
      • strlen 获取长度
    2. hash: string类型的 field 和 value的映射表,占用内存更少,方便取整个对象
      • hset/hsetnx key field1 value1
      • hmget 获取多个fields
      • hexists key field 测试field是否存在
      • hlen key 有多少field
      • hkeys key 获取所有的field
      • hvals 获取所有的value
      • hgetall == hvals + hkeys
    3. list 底层是链表,可当作queue使用,有序,可重复,key为链表的名字
      • lpush 头部添加 rpush尾部添加,linsert key
      • lset 重新设定指定位置的数据
      • lrem key count value 从头开始删除count个和value一样值的数据, count > 0 从头开始,count < 0从尾部开始,count == 0 删除所有的
      • ltrim 只保留给定范围的数据
      • lpop/rpop
      • rpoplpush key1 key2 将key1的尾部移除并加入key2的头部,原子操作
      • lindex下标访问
      • llen获取长度
    4. set
      • 便于求集合的交并差,无序集合,不可重复
      • sadd key value
      • srem key value
      • spop key 随机删除并返回一个元素
      • sdiff 求交集 一个key1 多个key2 , key3,也就是在其他key中与其他key不同元素
      • sdiffstore 将diff的结果保存到另一个key中
      • sinter(store) 交集(存储)
      • sunion(store) 并集
      • smove 将key1 中的删除添加给第二个
      • scard 统计元素个数
      • sismember测试是否为key的元素
      • srandmember随机返回不删除
    5. sorted sets
      • 有序,不可重复,会关联一个double类型score来进行排序,是skip list 和 hash table的混合,score越小的越在前面
      • zadd key score value
      • zrem
      • zincrby key score value 给这个元素的score添加
      • zrank 按照从小到大排序返回某个member的排名
      • zrerank反向排序
      • zcount 返回给定区间score内的数量
      • zcarf 返回元素数量
      • zscore 返回score
    • 常用指令:keys , del , expire,move转移把当前数据库中的数据转移到其他数据中,persist移除给定key的过期时间,randomkey随机返回一个key,rename,type
    • ping,echo,select(0~15),dbsize返回当前数据库中所有的key数量,info获取服务器统计信息
    • monitor实时转存收到的请求
    • flushdb删除所选择的数据库的所有key
    • fushall 删除所有数据库的所有key

Redis 脚本命令

dockercompose快速搭建一主二从一哨兵的redis集群

version: '3'
services:
  redis-master: # 主节点
    image: redis:latest
    command: redis-server --appendonly yes --requirepass bronya # 设置密码
    volumes:
      - ./data/master:/data
    ports:
      - "6379:6379"
    networks:
      - redis-network

  redis-slave1:
    image: redis:latest
    command: redis-server --slaveof redis-master 6379 --appendonly yes --masterauth bronya
    depends_on:
      - redis-master
    volumes:
      - ./data/slave1:/data
    networks:
      - redis-network

  redis-slave2:
    image: redis:latest
    command: redis-server --slaveof redis-master 6379 --appendonly yes --masterauth bronya
    depends_on:
      - redis-master
    volumes:
      - ./data/slave2:/data
    networks:
      - redis-network

  redis-sentinel:
    image: redis:latest
    command: redis-sentinel /etc/redis/sentinel.conf
    depends_on:
      - redis-master
    volumes:
      - ./sentinel.conf:/etc/redis/sentinel.conf
    networks:
      - redis-network

networks:
  redis-network:
    driver: bridge

下表列出了 redis 脚本常用命令:

序号 命令及描述
1 EVAL script numkeys key [key …] arg [arg …]
执行 Lua 脚本。
2 EVALSHA sha1 numkeys key [key …] arg [arg …]
执行 Lua 脚本。
3 SCRIPT EXISTS script [script …]
查看指定的脚本是否已经被保存在缓存当中。
4 SCRIPT FLUSH
从脚本缓存中移除所有脚本。
5 SCRIPT KILL
杀死当前正在运行的 Lua 脚本。
6 SCRIPT LOAD script
将脚本 script 添加到脚本缓存中,但并不立即执行这个脚本。
  • 选择数据库和登录
    如果你想在Redis服务器启动时指定要使用的数据库和密码,可以通过命令行参数进行设置,例如:

redis-server --port 6379 --requirepass your_password --db 3
redis默认有0-15个数据库,直接选择即可

info 来获得服务器和数据库信息

MongoDB

是一种源代码可用的文档数据库,以序列化的 JSON 格式存储数据。MongoDB 将数据存储在外部存储器中,但在企业版中包含内存存储引擎。
常用于应对高并发、海量数据存储、数据库的高可扩展性
例如:社交场景保存用户信息,通过地理位置索引来实现附近的人
游戏场景存储用户信息,方便高效
日志

基本概念:

  1. 文档:数据存储的基本单位
  2. 索引:
    • 单字段索引:索引建立的顺序无所谓,MongoDB会可以从头/尾开始遍历
    • 复合索引:建立在多个字段之上的索引,顺序很重要
    • 多键索引:如果一个字段是数组,对数组建立的索引就是多键索引,相当于会对数组的每个值都建立单字段索引,举例:给Tags建立索引,搜索时只需要tags数组即可筛选出带有tags的所有文档
    • 哈希索引:按照数据的哈希值进行索引,用在哈希分片集群上
    • 文本索引:不建议使用,性能低下
    • 地理位置索引:
    • 唯一索引:确保索引字段不会存储重复值
    • TTL索引:提供一个过期机制,允许为每一个文档设置一个过期时间,当文档达到过期时间就会被删除,示例代码:` @Indexed(expireAfterSeconds = 60 * 60 * 24 * 30)private Date createdAt;
      • TTL索引只能用于单字段
      • _id 不支持TTL索引
      • 不能在上限集合(上限集合(Capped Collections)是一种特殊类型的集合,它有固定的大小。当集合达到其最大大小时,MongoDB会自动覆盖最旧的文档。)中删除文档
    • 覆盖索引查询:
      1. 所有查询字段都要求是索引的一部分
      2. 结果中返回的所有字段都在同一个索引中
      3. 查询中没有字段为null
  3. 集合:集合是动态的,可以把不同类型的文档归为一个集合
    • 盖子集合: 有上限的集合,用于日志,不支持crud,当超过上限时,会从最老的文档开始删除
      • db.createCollection("users.actions",{capped:true,size:16384,max:100})
    • TTL集合: MongoDB也允许在特定的时间后废弃文档数据,有时候叫做生存时间time-to-live (TTL)集合这个功能实际上是通过一个特殊的索引实现的 创建TTL索引的方式如下:
      • db.reviews.createIndex({time_field:1},{expireAfterSeconds:3600})
      • time_field字段会定期检查时间戳,与当前时间比较,如果时间差大于设置的时间,文档会被自动删除,单位是s
  4. 文档->集合->数据库->MongoDB
  5. mongosh是一个js解释器,可以使用js标准库或者运行函数
  6. 使用js语法,db变量代表当前选用的数据库
  • CRUD:
    • 查询可以使用正则表达式
    • 建立数据库直接use一个新的数据库即可
    • 变量 = {…},之后db.集合.insertOne(变量)或者insertMany()
    • db.集合.find()或者findOne()
    • updateOne()
    • db.集合.deleteOne/Many()
    • db.dropDatabase()删除当前数据库
    • db.collection.drop() 删除指定集合
    • db.col.update({‘title’:’MongoDB 教程’},{$set:{‘title’:’MongoDB’}}) 对应的变量名({ “nMatched” : 1, “nUpserted” : 0, “nModified” : 1 },{multi:true}) 设置multi : true可以修改选定的所有文档
    • 想显式创建集合:db.createCollection(xxx),通过size字段可以预分配空间的字节大小
  • 创建索引:db.products.createIndex({slug: 1}, {unique: true})
    • 1代表是升序,unique是指定索引的选项,指定了索引是唯一的,slug一般用于存储URL
  • 聚合管道: 类似stream流和channel管道,可以执行一系列的handler,最后返回结果

Mybatis面试

  • mybatis执行流程:
    • 读取配置文件
    • 构建会话工厂:会话工厂全局一个,生产sqlSession
    • 创建会话:项目与数据库的会话,包含了执行sql语句的所有方法,每次操作一个会话,有多个
    • Executor执行器
    • 返回结果
  • 延迟加载:懒加载,旨在需要使用数据的时候才进行实际的SQL查询
    • 比如我们在关联查询中,我们会自动的把关联的用户数据也查询出来了,但是我们并不需要用户的信息,所以我们可以使用延迟加载,只有在我们使用用户信息的时候才会把结果查询出来
  • 缓存:本地缓存
    • 一级缓存:sqlsession ,实现方式是PerpertualCache,当Session进行flush或者close时会刷新缓存
    • 二级缓存:mapper级别的

虚拟机设置静态ip

https://wangyi.one/vmware%E4%B8%AD%E9%85%8D%E7%BD%AEubuntu%E9%9D%99%E6%80%81ip/
Emacs后续学习
常用键位

替换  : %s/原/修改之后的/g   g代表修改整个文件
查找  : 使用 n 查找下一个, N 下一个
: 首行,尾行 s/原/新/g
: .,+ns/旧/新/g 当前行和接下来的n行
. 重复之前的操作   ###

快捷键之类的,大部分都是可以组合使用的

键位练习:命令行输入 vimtutor #####
COMMAND模式
:set ic / noic  查找时不/区分大小写
使用v选中之后使用:
会看见'<'>之后可以通过 w + filename 将选中的字保存为新的文件
:! + shell 命令,在不退出vim的情况下使用shell指令 ######
:! + sh 再开一终端,暂时退出当前文件的页面,使用ctrl- d返回文件 #########
:r + filename 将filename文件中的文字写入当前光标所在位置
:set number 显示行号
:s/old/new/g  替换一行中的old为new,加上g会对整行都起作用,不加g只会修改第一个old
:sp 打开当前文件打开多个窗口
:qa 关闭所有窗口
normal模式
A        		append  移动到末尾进行insert ###
I                        移动到这一行的开头进行insert ###
J                        将下一行和这一行连接在一起
a                光标之后插入内容
ctrl + r redo
u   撤销
hjkl 左下上右
v     			进入选中块模式
V    			选中模式,每次选中一行
ctrl + w         切换window 
ctrl + v         选中一个矩形
~				选中之后,使得大小写互换 ### 
f + 要查找的字符   find 这一行中光标之后第一个关键字
F + xxx           find 反向查找
t + xxx           移动到这个字符的前面一个字符
T + xxx           反向查找,移动到字符之后一个字符
d + 移动的键位     删除  d + $ 删除到(一行)结尾, d + 0 删除到(一行)开头
c + 移动的键位     change 删除并进入insert进行修改
dd       		 删除一行
cc				删除一行并进入insert
x            	 删除当前字符
r + 字符          替换当前一个字符
R                 连续替换 ###
w  word 向后移动一个单词
b  向前移动一个单词
e  end 移动到下一个单词的最后一个字母
o  下方开一个新行
O  上方开一个新行
0  移动到行首
%  从{/[/( 移动到)/]/}
$  移动到行末
数字 + G         跳转到指定行
ctrl + g         显示当前的行数
G  最后一行
gg 第一行
y + 移动的键位   复制
yy     		    复制一行
p			   粘贴
L/M/H 当前页面的lowest/middle/highest
ctrl + u / d 向上滚动/向下滚动
数字 + 键位   执行几次这个键位操作
例如:
7dw          删除七个单词
修饰词
a 删除所在的整体
例如:
{djsafljaflsjlf}123
使用di{
只剩下了
123F
i 内部,例如
{skdajsldjas}想要删除{}内的东西只需要
ci{

/ + 内容      全文查找
  • 好用的操作
  1. 一键取消注释,ctrl + v进入块选择模式,然后使用方向键选择所有的注释,按d删除即可

Git

切换分支和版本号实际上就是指针的切换

工作区:磁盘目录

使用git add把工作区代码加入暂存区 临时储存

git commit

将暂存区代码提交到本地库,得到历史版本(代码删除不了了)

push

推送到远程库

github是远程库

git init 初始化仓库
git status 查看仓库状态
##Untracked files: 是未追踪的文件,也就是文件只处于工作区,不处于暂存区和本地库,红色的文字
##绿色的文件名是存在于暂存区
git add + filename 添加到暂存区
git add -A    全部提交
git add -i    添加到暂存区,但是会有提问
git rm --cached filename 删除暂存区的文件
git commit -m "日志信息" filenaem 提交到本地库
git commit --amend + 日志 #覆盖最新的一次提交日志
##提交之后git status 会显示为没有提交
git reflog 查看提交记录
git log    查看日志,包括提交记录和提交用户
git log --author=xxx  只查看某人的提交记录
git log --pretty=oneline 每一个提交记录只占一行
git log --graph --oneline --decorate --all 通过 ASCII 艺术的树形结构来展示所有的分支
git log --name-status 看哪个文件改变了
-n n为数字 //最新n个提交
##版本穿越,修改HEAD指针 本地原文件一并会被修改
git reset --hard + 版本号
git tag xxx 提交ID前10个字符,创建一个标签   
git cherry-pick +xx xxx xx 把某几个分支复制到当前分支上

分支操作

git branch 创建分支
git branch -v 查看有哪些分支
-m 改名
-d 删除
git checkout  + name 切换分支
git merge  + name    将当name合并在当前分支
git reset    撤销提交记录,但是撤销之前的还是存在的,只是处于未加入缓冲区的状态,仅限本地
git revert   撤销更改分享给别人

分支冲突:两个人同时对同一个内容 进行了两个不同的修改,git无法决定新的文件手

手动修改后,commit 不要再加上文件名了

HEAD指向的是当前分支

远程开发:

git remote -v 查看所有远程地址别名
git remote add 别名 + 远程地址    给远程地址起一个别名,方便切换
git push +别名/库地址 + 分支名    把这个分支推送到远程库
git pull +别名/库地址 + 分支名    把远程的分支名拉取
git clone + 库地址               克隆到本地  会进行:1.拉取代码2.初始化本地库3.创建别名
###############
git clone -b <branchname> <remote-repo-url> 克隆指定分支
###############
git fetch + xxx 
git reset --hard origin/master  获取服务器上最后一次改动,并将本地主分支指向它,实现放弃本地所有改动

其他知识

gitk 内建的图形化
##显示历史记录时,每个提交的信息只显示一行:
git config format.pretty oneline

其他:

git count-objects -vH #查看仓库大小
git log --reverse  #从旧到新查看提交记录

.gitignore配置

  • 所有以#开头的行会被忽略
  • 可以使用glob模式匹配
  • 匹配模式后跟反斜杠(/)表示要忽略的是目录
  • 如果不要忽略某模式的文件在模式前加”!”

  比如:

# 此为注释 – 将被 Git 忽略
.a # 忽略所有 .a 结尾的文件
!lib.a # 但 lib.a 除外
/TODO # 仅仅忽略项目根目录下的 TODO 文件,不包括 subdir/TODO
build/ # 忽略 build/ 目录下的所有文件
doc/
.txt # 会忽略 doc/notes.txt 但不包括 doc/server/arch.txt

工作中常用

git stash //将当前的工作暂存,但是不进行提交
git stash show //显示暂存的内容,哪些被修改了,可指定序号
git stash list //已经暂存的列表和序号
git stash apply + stash@{数字} //切换回来继续工作 
//可以加上--index 来回到原来文件的暂存状态
git stash drop //丢弃指定的stash
git stash pop //apply + drop

Scoop

Scoop是一个Windows系统管理包的开源软件,相比从百度上搜索,从几十条垃圾信息中筛选出一个能用的安装链接,Scoop可以只使用一行代码进行安装

安装环境:

本人使用的是Windows11系统,已经内置PowerShell了,如果是Windows7系统,需要手动安装新版本的PowerShell

Scoop 源文件在GitHub上,推荐使用魔法

步骤:

使用快捷键win+ x 打开 Windows终端,不要打开管理员的终端

Scoop

之后输入

# 设置 PowerShell 执行策略
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
# 下载安装脚本
irm get.scoop.sh -outfile 'install.ps1'
# 执行安装, --ScoopDir 参数指定 Scoop 安装路径 ' ' 内的是自定义的安装目录
.\install.ps1 -ScoopDir 'D:\Scoop'

使用技巧

官方文档快速入门

大佬的博客

scoop社区维护的安装路径大部分在国外,所以下载时建议使用魔法

scoop help  命令参考说明
scoop + 动作 + 对象, 对象可以省略
scoop +
search 搜索软件名
install 安装软件
update  更新软件
status  查看软件装填
uninstall 卸载软件
info     查看软件详情
home     打开软件主页

举例:

  • 查看本机有无安装typora
scoop search typora

Scoop下载软件的安装路径是自定义下载路径中的apps文件夹

正则表达式:

$ 匹配末尾位置  
^ 匹配开头位置
* 匹配前一个字符的0次或n次  例如: zo* 会匹配z zo zoo zooo z......  等
+ 匹配前一个字符一次或多次u        zo+ 匹配 zo zoo zooo zo...... 等
? 匹配前一个字符0次或1次           zo?      z  zo
{n,m}  匹配 n 到 m 次
{n}      n 次
? + 其他限定符,表示匹配非贪心,默认的匹配会尽力匹配较长的满足条件的字符串,使用这个后尽力匹配较短的字符串
.  匹配任意单个字符
//
(pattern) 匹配pattern 并捕获他的子表达式
(?:pattern) 不捕获子表达式
(?=pattern) 前面的字符匹配到之后,判断后面的是否能够匹配pattern 如果能那么就捕获,否则不匹配
(?!pattern) 反向捕获,不捕获包含patter的字符串
//规则集
x|y 匹配x或y
[xyz] 字符集,匹配三者的任一字符
[^xyz] 反向捕获
[a-z]  匹配a-z 中的任一字符
[^a-z] 反向匹配不包含的
//
\b     border 以这个符号之前的字符(串)为边界的匹配
\B     非边界匹配
\cx    x 为[a-z] 或 [A-Z] 匹配ctrl + x
\d     数字匹配,十进制[0-9]
\D     [^0-9]
\f     匹配换页符
\n     换行符
\r     匹配回车
\w     匹配字类字符 [A-Za-z0-9]
\W     非字类字符

Maven

坐标

  • groupId(必须): 定义了当前 Maven 项目隶属的组织或公司。groupId 一般分为多段,通常情况下,第一段为域,第二段为公司名称。域又分为 org、com、cn 等,其中 org 为非营利组织,com 为商业组织,cn 表示中国。以 apache 开源社区的 tomcat 项目为例,这个项目的 groupId 是 org.apache,它的域是 org(因为 tomcat 是非营利项目),公司名称是 apache,artifactId 是 tomcat。
  • artifactId(必须):定义了当前 Maven 项目的名称,项目的唯一的标识符,对应项目根目录的名称。
  • version(必须):定义了 Maven 项目当前所处版本。
  • packaging(可选):定义了 Maven 项目的打包方式(比如 jar,war…),默认使用 jar。
  • classifier(可选):常用于区分从同一 POM 构建的具有不同内容的构件,可以是任意的字符串,附加在版本号之后。

依赖

  • dependencies:一个 pom.xml 文件中只能存在一个这样的标签,是用来管理依赖的总标签。
  • dependency:包含在 dependencies 标签中,可以有多个,每一个表示项目的一个依赖。
  • groupId,artifactId,version(必要):依赖的基本坐标,对于任何一个依赖来说,基本坐标是最重要的,Maven 根据坐标才能找到需要的依赖。我们在上面解释过这些元素的具体意思,这里就不重复提了。
  • type(可选):依赖的类型,对应于项目坐标定义的 packaging。大部分情况下,该元素不必声明,其默认值是 jar。
  • scope(可选):依赖的范围,默认值是 compile。
  • optional(可选):标记依赖是否可选
  • exclusions(可选):用来排除传递性依赖,例如 jar 包冲突

著作权归JavaGuide(javaguide.cn)所有 基于MIT协议 原文链接:https://javaguide.cn/tools/maven/maven-core-concepts.html

著作权归JavaGuide(javaguide.cn)所有 基于MIT协议 原文链接:https://javaguide.cn/tools/maven/maven-core-concepts.html

xml

eXtensible Markup Language 即可扩展标记语言。
一般用于做数据存储,支持自定义标签

通过 XML,数据能够存储在独立的 XML 文件中。这样您就可以专注于使用 HTML/CSS 进行显示和布局,并确保修改底层数据不再需要对 HTML 进行任何的改变。
通过使用几行 JavaScript 代码,您就可以读取一个外部 XML 文件,并更新您的网页的数据内容。

基础语法:

//文档声明:
<?xml version = "xxx" ? encodiing standlone>
encoding 指定文档的编码 UTF-8等
standalone 文档是否独立 yes or no

注释:
<!--comment -->

标签内要包含要传递的信息
<message> <message/>
  • 示例: Bob写个Tom的信
    <?xml version "1.0" encoding = "UTF-8"> <!--声明-->
    <note> <!-- 根元素,来表示本文档的类型-->
    <to> Tom </to>
    <from> Bob </from>
    <heading> Reminder </heading>
    <body> Dont' forget me this weekend </body>
    </note>
  • xml文档必须包含一个根元素,且只能有一个根元素,其他所有元素都是根元素的子元素
  • 所有的标签都有一个闭标签 </…>
  • xml 标签对大小写敏感
  • 属性值必须加引号
    <note date="12/11/2023">
  • 实体引用,用于转义某些符号

<  <  less than
>  >  greater than
& &  ampersand
&apos; ' apostrophe
" " quotation mark
  • 标签命名: 尽量用 __ 来进行命名 first_name
  • 属性Attribute : 提供有关元素的额外信息 (尽量减少属性的使用,将属性作为一个新的标签即可)
  • DTD 用于定义xml文档结构 Schema 基于xml的DTD替代
  • XSLT来显示XML

XMLHttpRequest 对象(JavaScript)

功能:

在不重新加载页面的情况下更新网页
在页面已加载后从服务器请求数据
在页面已加载后从服务器接收数据
在后台向服务器发送数据

yaml

特点:

  1. 使用缩进表示层级关系
  2. 缩进不能使用tab键
  3. 只要相同层级的元素左对齐即可,不需要控制多少空格数
  4. 表示注释

示例:

key:
	child-key: value # 给予值的时候要空一个额格

#较为复杂的对象格式,可以使用问号加一个空格代表一个复杂的 key,配合一个冒号加一个空格代表一个 value:

?  
    - complexkey1
    - complexkey2
:
    - complexvalue1
    - complexvalue2

意思即对象的属性是一个数组 [complexkey1,complexkey2],对应的值也是一个数组 [complexvalue1,complexvalue2]
  • 数组
     # 多维数组
    - a
    - b
    - c
      - d
      - e
        - f
    
    
    companies:
        -
            id: 1
            name: company1
            price: 200W
        -
            id: 2
            name: company2
            price: 500W
    

复合结构

数组和对象可以构成复合结构,例:

languages:
  - Ruby
  - Perl
  - Python 
websites:
  YAML: yaml.org 
  Ruby: ruby-lang.org 
  Python: python.org 
  Perl: use.perl.org

转换为 json 为:

{ 
  languages: [ 'Ruby', 'Perl', 'Python'],
  websites: {
    YAML: 'yaml.org',
    Ruby: 'ruby-lang.org',
    Python: 'python.org',
    Perl: 'use.perl.org' 
  } 
}

Docker

  1. 安装,并修改安装路径(windows)
    此方法官方文档里有
    先下载安装包
    在想安装的地方建立文件夹Docker即可
    然后打开cmd,输入
    "Docker Desktop Installer.exe" install --installation-dir="E:\Program Files\Docker"
    后面的是你自己的路径,根据实际修改即可,等待安装完毕即可

基础概念

容器是镜像的实例化,容器是一个小型的os,包含应用和其本身所需要的环境,镜像是只读的,而容器是可以运行的可写的,其中的容器处于运行状态

命令

  • 基础镜像命令(与git类似)
  • ls , tag , inspect
    docker tag 旧 新
    docker inspect 获得该镜像的详细信息.
    -f 后面加上想获得的某一项的key就可以单独获得这一项的内容了
    格式为{{".key名"}}
    docker history 查看某一镜像的历史
    docker search -f=is-official=true --limit 5 --no-trunc  nginx  
    -f 过滤条件
    limit 限制输出的数量
    --no-trunc 不截断输出结果
    docker rmi  -f  myubuntu 当存在多个标签时,删除的只是标签,当仅仅剩下一个标签时会把这个镜像直接删除 -f
    强制删除
    docker image prune 清理临时镜像文件,虚悬文件,指的是docker images 中没有名字的文件
    -a 删除所有无用image 不光是临时镜像
    -filter 只删除符合过滤条件的镜像
    	- `until`: 根据镜像的创建时间来筛选。例如,`until=24h`将删除24小时之前创建的镜像。
        
    	- `label`: 根据标签来筛选。例如,`label=myapp`将删除带有标签为"myapp"的镜像。
        
    	- `dangling`: 筛选出悬挂(无用)镜像。使用`dangling=true`来删除悬挂镜像。
    -f 强制删除
    
    创建镜像
    docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]
    docker commit [选项] <容器ID或容器名> [<仓库名>[:<标签>]]
     docker commit \
        --author "Tao Wang <twang2218@gmail.com>" \
        --message "修改了默认网页" \
        webserver \
        nginx:v2
    //
    - `CONTAINER`是要保存状态的容器的名称或容器ID。
    - `REPOSITORY[:TAG]`是新镜像的名称和标签。如果不提供标签,将默认使用"latest"标签。
    docker commit -m "xxx" 被提交的容器
    
    //导入导出镜像
    docker save -o my_images.tar image1:tag image2:tag image3:tag  保存多个到镜像到指定的文件夹
    
    docker load -i my_image.tar 

容器操作

docker create  xxx
可选。。。
-i 默认打开标准输入
-t分配一个伪终端
docker start xx  启动一个容器
docker run --name 新名字 options 镜像 创建并启动一个容器
可选
-d 以守护态后台运行,容器是否会长久运行,是和 `docker run` 指定的命令有关,和 `-d` 参数无关。
-t 分配一个伪终端
-i 让容器的标准输入保持打开
docker restart 关闭容器并重新启动
docker logs 
-details
-f follow 持续保持输出
since string 从某一个时间开始日志
-tail string 输出最近的若干日志
-t timestamps 显示时间戳信息
-until string 输出某个时间段之前的信息

docker exec -it xxx 进入一个后台的容器 使用这个之后再容器内部执行exit 时不会导致容器停止,但是使用 docker attach 进入容器并退出的话会导致容器停止并退出
docker rm 
-f 强制终止并删除一个容器
-l 删除容器的链接,但保留容器
-v 删除容器挂在的数据卷
docker top 容器名称 查看容器的进程
docker inspect 容器名称 查看容器信息
docker cp <本地文件/目录路径> <容器ID或名称>:<容器内部路径>
docker cp <容器ID或名称>:<容器内部路径> <本地文件/目录路径>
-a 打包
-L 跟随软连接
docker pause id 暂停
    1. 创建卷: 您可以使用以下命令创建一个卷:

    bashCopy code

    docker volume create my_volume

    这将创建一个名为 my_volume 的卷。

  1. 查看卷列表: 要查看系统中的所有卷,可以运行:

    bashCopy code

    docker volume ls

  2. 删除卷: 要删除一个不再需要的卷,可以运行:

    bashCopy code

    docker volume rm my_volume

  3. 挂载卷到容器: 在运行容器时,使用 -v--volume 标志来将卷挂载到容器内部。例如:

    bashCopy code

    docker run -d -v my_volume:/path/in/container my_image

    这将把 my_volume 卷挂载到容器内部的 /path/in/container 目录。

  4. 挂载主机目录到容器: 您还可以将主机上的目录挂载到容器内。例如:

    bashCopy code

    docker run -d -v /host/path:/path/in/container my_image

    这将把主机上的 /host/path 目录挂载到容器内的 /path/in/container

  5. 查看容器的挂载卷: 要查看正在运行的容器挂载了哪些卷,可以使用以下命令:

    bashCopy code

    docker inspect -f '{{ .Mounts }}' container_name_or_id

  6. 复制文件到卷: 如果需要将文件复制到卷中,可以运行一个临时容器,然后将文件复制到挂载卷的路径。例如:

    bashCopy code

    docker run --rm -v my_volume:/path/in/container -v /local/path/to/file:/data busybox cp /local/path/to/file /path/in/container

    这将复制 /local/path/to/filemy_volume 卷的 /path/in/container

  7. 卷数据备份和恢复: 您可以使用工具如 docker cpdocker export 来备份卷数据,然后使用 docker createdocker start 来恢复它们。备份和恢复数据的确切方法取决于您的需求和容器的情况。

    挂载数据卷
    1.建立数据卷
    docker run -it -v /db --name db0 ubuntu
    docker run -it --volume-from db --name db1 ubuntu
    docker run -it --volume-from db --name db2 ubuntu
  • VOLUME 的使用
    将数据读写存储在数据卷中,使得容器尽量不发生读写操作,dockerfile 中的VOLUME是可以呗docker run覆盖的

dockerfile

指令详解

Dockerfile 指令 说明
FROM 指定基础镜像,用于后续的指令构建。
LABEL 添加镜像的元数据,使用键值对的形式,方便后续进行filter来筛选。
RUN 在构建过程中在镜像中执行命令。
CMD 指定容器创建时的默认命令。(可以被覆盖)
ENTRYPOINT 设置容器创建时的主要命令。(不可被覆盖),
EXPOSE 声明容器运行时监听的特定网络端口,不会自动映射,只是声明,需要自己配置
ENV 在容器内部设置环境变量。
ADD 将文件、目录或远程URL复制到镜像,自动解压
COPY 将文件或目录复制到镜像中。
VOLUME 为容器创建挂载点或声明卷。
WORKDIR 设置后续指令的工作目录。
USER 指定后续指令的用户上下文。
ARG 定义在构建过程中传递给构建器的变量,可使用 “docker build” 命令设置。
ONBUILD 当该镜像被用作另一个构建过程的基础时,添加触发器。
STOPSIGNAL 设置发送给容器以退出的系统调用信号。
HEALTHCHECK 定义周期性检查容器健康状态的命令
SHELL 覆盖Docker中默认的shell,用于RUN、CMD和ENTRYPOINT指令。
RUN 运行指定命令
CMD 启动容器时指定默认执行的命令
ADD 添加内容到镜像
COPY 复制内容到镜像

实际操作

推荐使用基础镜像:
BusyBox 集成Linux的命令
Alpine 在BusyBox基础上减小体积和消耗,并提供了apt管理工具

  • 使用dockerfile为镜像添加ssh服务
    #设置继承镜像
    FROM myub
    
    #提供作者信息
    MAINTAINER docker_user (user@docker.com)
    
    #执行命令
    RUN apt-get update
    RUN apt-get install -y openssh-server
    RUN mkdir -p /var/run/sshd
    RUN mkdir -p /root/.ssh
    RUN sed -ri 's/session required pam_loginid.so/#session required pam_loginuid.so/g' /etc/pam.d/sshd
    
    #复制配置文件到对应位置,并赋予其可执行权限
    ADD authorized_keys /root/.ssh/authorized_eys
    ADD run.sh /run.sh
    RUN chmod 755 /run.sh
    #开放端口
    EXPOSE 22
    #设置自启动命令
    CMD ["/run.sh"]
    

FROM scratch

如果你以 scratch 为基础镜像的话,意味着你不以任何镜像为基础,接下来所写的指令将作为镜像第一层开始存在。

不以任何系统为基础,直接将可执行文件复制进镜像的做法并不罕见,对于 Linux 下静态编译的程序来说,并不需要有操作系统提供运行时支持,所需的一切库都已经在可执行文件里了,因此直接 FROM scratch 会让镜像体积更加小巧。使用 Go 语言 开发的应用很多会使用这种方式来制作镜像,这也是有人认为 Go 是特别适合容器微服务架构的语言的原因之一。

每一个RUN会建立一层,所以要进行多重操作的时候不要每一行都建立一个RUN而是将他们合在一层

FROM debian:stretch

RUN set -x; buildDeps='gcc libc6-dev make wget' \
    && apt-get update \
    && apt-get install -y $buildDeps \
    && wget -O redis.tar.gz "http://download.redis.io/releases/redis-5.0.3.tar.gz" \
    && mkdir -p /usr/src/redis \
    && tar -xzf redis.tar.gz -C /usr/src/redis --strip-components=1 \
    && make -C /usr/src/redis \
    && make -C /usr/src/redis install \
    && rm -rf /var/lib/apt/lists/* \
    && rm redis.tar.gz \
    && rm -r /usr/src/redis \
    && apt-get purge -y --auto-remove $buildDeps
  • docker 不是虚拟机不存在后台运行,其中的所有应用都是在前台运行的,所以dockerfile 中直接执行这个应用即可,然后退出容器,让容器在后台运行即可

    CMD ["nginx", "-g", "daemon off;"]

    这是因为当存在 ENTRYPOINT 后,CMD 的内容将会作为参数传给 ENTRYPOINT,而这里 -i 就是新的 CMD,因此会作为参数传给 curl,从而达到了我们预期的效果。

  • ENV 设置

    ENV key1=value1 key2=value2 .....
  • ARG 设置变量 等于 docker build 中用 --build-arg <参数名>=<值>

  • ARG 指令有生效范围,如果在 FROM 指令之前指定,那么只能用于 FROM 指令中。

    # 只在 FROM 中生效
    ARG DOCKER_USERNAME=library
    
    FROM ${DOCKER_USERNAME}/alpine
    
    # 要想在 FROM 之后使用,必须再次指定
    ARG DOCKER_USERNAME=library
    
    RUN set -x ; echo ${DOCKER_USERNAME}
  • HEALTHCHECK

  • --interval=<间隔>:两次健康检查的间隔,默认为 30 秒;

  • --timeout=<时长>:健康检查命令运行超时时间,如果超过这个时间,本次健康检查就被视为失败,默认 30 秒;

  • --retries=<次数>:当连续失败指定次数后,则将容器状态视为 unhealthy,默认 3 次。

FROM nginx
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
HEALTHCHECK --interval=5s --timeout=3s \
  CMD curl -fs http://localhost/ || exit 1
  • 多阶段构造镜像
    1. 多个FROM 和 构造过程写在一个dockerfile 中
    2. 使用as 来为某一阶段的构造命名 FROM golang:alpine as builder之后构造时直接指定名字即可, $ docker build –target builder -t username/imagename:tag .
  • docker 导入导出容器
    示例:
    docker export 7691a814370e > ubuntu.tar
    cat ubuntu.tar | docker import - test/ubuntu:v1.0 # 将文件读入到标准输入流,再将其导入到镜像中
    # 或者使用URL 来导入也行

docker 查看容器ip

docker inspect 容器ID | grep IPAddress

Nexus 容器

可以方便进行对Maven , Docker ,Yum,PyPI的管理

Lua 一种可以嵌入程序的简便语言

一般只适合在linux上使用
安装:
apt-get install luaxxx(版本号)
执行
lua xxx.lua

交互式编程

Lua 提供了交互式编程模式。我们可以在命令行中输入程序并立即查看效果。

Lua 交互式编程模式可以通过命令 lua -i 或 lua 来启用

  1. 基本语法
    • 注释 – 单行 --[[xxxx --]] 多行注释
    • 默认为全局变量,全局变量不需要声明,不需要时置为nil即可,局部变量使用 local 来指定
    • 基本数据类型: nil ,boolean , number(为double类型) , string ,userdata (任意存储在变量中的数据结构)
    • userdata 是一种用户自定义数据,用于表示一种由应用程序或 C/C++ 语言库所创建的类型。 可以将任意 C/C++ 的任意数据类型的数据(通常是 struct 和 指针)存储到 Lua 变量中调用
    • function(由C 或Lua编写的函数) , thread(线程), table(关联数组)
    • 使用[[]] 来跨越多行赋值
    • Lua可以对多个变量同时赋值,变量列表和值列表的各个元素用逗号分开,赋值语句右边的值会依次赋给左边的变量
      html = [[
      <html>
      <head></head>
      <body>
          <a href="https://www.twle.cn/">简单编程</a>
      </body>
      </html>
      ]]
  • lua会尝试将字符数字转化为数字来进行数字计算,字符串连接符是 ..
  • string 来计算字符串的长度

  • 默认索引是从1开始
  • function 可以以匿名函数(anonymous function)的方式通过参数传递
    -- !/usr/bin/lua
    -- -*- encoding:utf-8 -*-
    -- filename: main.lua
    -- author: 简单教程(www.twle.cn)
    -- Copyright © 2015-2065 www.twle.cn. All rights reserved.
    
    function testFun(tab,fun)
        for k ,v in pairs(tab) do
            print(fun(k,v));
        end
    end
    
    tab={key1="val1",key2="val2"};
    testFun(tab,
    function(key,val)--匿名函数
        return key.."="..val;
    end
    );
  • table变量可以使用索引,key,或者 . 来获取值
  • 注意 : Lua 中 0 为 true
  • 多返回值Lua 中的函数可以返回多个结果值,例如 string.find 返回匹配串 “开始和结束的下标”(如果不存在匹配串返回 nil )
    > s, e = string.find("www.twle.cn", "twle") 
    > print(s, e)
    5   10
  • 可变参数 使用 … 来代表
  • 算术运算符 有 ^ 幂运算哦
  • 不等于是 -= 不是 !=
  • 逻辑运算 and , or ,not
  • 返回字符串个数或者传入参数个数..

\a 响铃(BEL) 007
\b 退格(BS) ,将当前位置移到前一列 008
\f 换页(FF),将当前位置移到下页开头 012
\n 换行(LF) ,将当前位置移到下一行开头 010
\r 回车(CR) ,将当前位置移到本行开头 013
\t 水平制表(HT) (跳到下一个TAB位置) 009
\v 垂直制表(VT) 011
\ 代表一个反斜线字符’’' 092
' 代表一个单引号(撇号)字符 039
" 代表一个双引号字符 034
\0 空字符(NULL) 000
\ddd 1到3位八进制数所代表的任意字符 三位八进制
\xhh 1到2位十六进制所代表的任意字符 二位十六进制
  • 常用字符串方法 string + . +

    • upper 大写 lower 小写
    • gsub(string , findstring , replacestring ,num) 从string中查找findstring,并替换为replacestring ,num是替换的次数
    • find () 返回首次出现该字符串的前后位置,包括开始和结束的位置
    • reverse 反转
    • char(num1,num2…) 将数字变为字母,ascii码
    • byte(字母串,指定某个字符) 将字母变为数字
    • len 长度
    • rep(string,num) 返回重复num次的string
    • gmatch(string,pattern) 返回一个迭代器,返回一个符合pattern的字串,可以使用
    • format ()格式化 转义码
  • 数组 lua的数组可以从负数开始遍历索引从1开始

    • for i= -1 , 10 do ...
    • 迭代器:
      -- pairs用于匹配键值对
      local myTable = {a = 1, b = 2, c = 3}
      for key, value in pairs(myTable) do
          print(key, value)
      end
      
      --ispairs 用于遍历数组,字符串等
      local myArray = {10, 20, 30}
      for index, value in ipairs(myArray) do
          print(index, value)
      end
      --自定义迭代器,实际就是自己写的遍历算法。。
      function myIterator(collection)
          local index = 1
          local size = #collection
          return function()
              if index <= size then
                  local value = collection[index]
                  index = index + 1
                  return value
              end
          end
      end
      
      local myArray = {100, 200, 300}
      local iter = myIterator(myArray)
      for value in iter do
          print(value)
      end
      
  • table常用方法 table + . +

    • concat 是 concatenate (连锁, 连接) 的缩写. table.concat() 函数列出参数中指定 table 的数组部分从 start 位置到 end 位置的所有元素, 元素间以指定的分隔符(sep)隔开
    • insert(table,pos,value) 在指定位置添加一个value ,如果未指定位置,默认从尾部加入
    • remove(table,post) 返回并移除table 位于pos位置的元素,后面的元素会自动向前
    • sort 进行升序排序
  • lua的模式匹配
    开源中国中正则表达式列表
    lua模式匹配

  • % 用于将后面的字符转义为字面量而不是特殊字符,例如

  • %。 匹配的是。而不是其他的

  • 有时也会有特殊含义: %a 代表的是匹配一个字母,%d 代表匹配一个数字

  • 字符类 %d 匹配任意数字,所以可以使用模式串 ‘%d%d/%d%d/%d%d%d%d’ 搜索 dd/mm/yyyy 格式

  • 模块和导包

  • 导包require(模块名),甚至可以加个.var赋给一个变量来调用模块

  • 元表?

  • 协程

  • 文件

nginx

一种高性能的 HTTP 和 反向代理的服务器,默认监听端口是80不是8080

  • 基本命令
    nginx 启动
    nginx -s quit 优雅的退出,完成所有任务后退出
    nginx -s stop 强制退出
    nginx -s reload 重启,来刷新配置文件
    nginx -t 测试配置文件是否可用,同时会显示配置文件的路径
    
  • 配置文件
    配置文件分为三类
  1. 全局块,服务器配置,日志存放等
  2. events 主要影响服务器和用户的网络连接
  3. http 可以分很多块
    • server块 全局块 本机监听配置和ip配置

    • location 局部块 控制地址定向,转发等 , 在server块内来匹配新的转发路径

用法一:反向代理,作为另一个服务器的转发服务器来隐藏另一个服务器
使用docker 启动一个tomcat服务器,并进入并启动服务

docker run -itd tomcat /bin/sh
进入tomcat/bin目录
./startup.sh

错误情况:

docker tomcat无法启动

  1. 可能没有映射端口或者没有关闭防火墙
  2. 把webapps.dist目录换成webapps
  3. 将文件扔到webapps即可

测试第一个方法,解决问题!

进入容器内部

docker exec -it mytomcat /bin/bash

复制

rm -rf webapps
mv webapps.dist webapps

复制

重启tomcat

docker restart mytomcat

查看docker的tomcat ip
docker inspect ID | grep IPAddress
配置文件:

server {
    listen       8888 ; ##设置我们nginx监听端口为8888
    server_name  [服务器的ip地址]; # 不带http

    # Load configuration files for the default server block.
    include /etc/nginx/default.d/*.conf;

    location / {
        proxy_pass http://tomcat服务器ip:8080; ##需要代理的服务器地址
        index index.html;
    }

    error_page 404 /404.html;
        location = /40x.html {
    }

    error_page 500 502 503 504 /50x.html;
        location = /50x.html {
    }
}

打开浏览器输入服务器的ip和代理端口,就可以看到docker容器中tomcat的主界面

  • 实现服务器分发
    配置

    server {
        listen       8888 ; ##设置我们nginx监听端口为8888
        server_name  [服务器的ip地址];
    
        # Load configuration files for the default server block.
        include /etc/nginx/default.d/*.conf;
    
        location /hi/ {
            proxy_pass http://服务器2; ##需要代理的服务器地址
            index index.html;
        }
        
        location /hello/ {
            proxy_pass http://服务器2; ##需要代理的服务器地址
            index index.html;
        }
    
        error_page 404 /404.html;
            location = /40x.html {
        }
    
        error_page 500 502 503 504 /50x.html;
            location = /50x.html {
        }
    }
    
  • location 匹配

    location [ = | ~ | ~* | ^~ ] /URI {}
    location @/name/ {}
    ########### 顺序
    1. location =    # 精准匹配
    2. location ^~   # 带参前缀匹配
    3. location ~    # 正则匹配(区分大小写)
    4. location ~*   # 正则匹配(不区分大小写)
    5. location /a   # 普通前缀匹配,优先级低于带参数前缀匹配。
    6. location /    # 任何没有匹配成功的,都会匹配这里处理
    参数 解释
    location 后没有参数直接跟着 标准 URI,表示前缀匹配,代表跟请求中的 URI 从头开始匹配。
    = 用于标准 URI 前,要求请求字符串与其精准匹配,成功则立即处理,nginx停止搜索其他匹配。
    ^~ 用于标准 URI 前,并要求一旦匹配到就会立即处理,不再去匹配其他的那些个正则 URI,一般用来匹配目录
    ~ 用于正则 URI 前,表示 URI 包含正则表达式, 区分大小写
    ~* 用于正则 URI 前, 表示 URI 包含正则表达式, 不区分大小写
    @ @ 定义一个命名的 location,@ 定义的locaiton名字一般用在内部定向,例如error_page, try_files命令中。它的功能类似于编程中的goto。
  • 顺序

  1. 先精准匹配 = ,精准匹配成功则会立即停止其他类型匹配;
  2. 没有精准匹配成功时,进行前缀匹配。先查找带有 ^~ 的前缀匹配,带有 ^~ 的前缀匹配成功则立即停止其他类型匹配,普通前缀匹配(不带参数 ^~ )成功则会暂存,继续查找正则匹配;
  3. = 和 ^~ 均未匹配成功前提下,查找正则匹配 ~ 和 ~* 。当同时有多个正则匹配时,按其在配置文件中出现的先后顺序优先匹配,命中则立即停止其他类型匹配;
  4. 所有正则匹配均未成功时,返回步骤 2 中暂存的普通前缀匹配(不带参数 ^~ )结果
  • 负载均衡
    配置文件(server块)
    http {
    
    ###此处省略一大堆没有改的配置
    
    
        ##自定义我们的服务列表
        upstream myserver{
    	    #### 可以使用 ip_hash 来进行hash 分配,可以与weight一起使用
    	   ip_hash;
           server 服务器1及端口; # 可加权值来表示优先级,server 服务器1及端口 + weight = 数字; 数字越大,权重越高
           server 服务器2及接口;
    	   ……
         }
    
    
       server {
           listen       8888 ; ##设置我们nginx监听端口为8888
           server_name  [服务器的ip地址];
    
           # Load configuration files for the default server block.
           include /etc/nginx/default.d/*.conf;
    
           location / {
               proxy_pass http://myserver; ##叮,核心配置在这里
               proxy_connect_timeout 10; #超时时间,单位秒
           }
    
           error_page 404 /404.html;
               location = /40x.html {
           }
    
           error_page 500 502 503 504 /50x.html;
               location = /50x.html {
           }
       }
    
    }
    
    
  • 关闭一个tomcat之后,发现仍然可以访问,但关闭所有的tomcat时发现无法访问了,说明实现了负载均衡

Linux基本操作

常用操作:

lsof -i:9999 查看端口占用情况
top/htop 查看系统资源占用情况

Bash界面

快捷键

Ctl-U   删除光标到行首的所有字符,在某些设置下,删除全行
Ctl-W   删除当前光标到前边的最近一个空格之间的字符
Ctl-H   backspace,删除光标前边的字符
Ctl-R   匹配最相近的一个文件,然后输出

用户操作

useradd -m username 创建用户,-m是会创建对应的用户目录到/home下,不加的话不会创建这个目录,-d 参数用来指定用户目录的位置
userdel -r 直接把用户目录删掉了不加r就不删除目录
who am i 看当前账号是什么
groupadd 创建一个用户组
useradd -g 用户组 用户名   把这个用户加入到用户组
usermod -g 用户组 用户名  修改
usermod -d 目录名 用户名 改变该用户登录的初始目录

其他指令

mkdir -p 创建多级目录
cp -r 递归的复制整个文件夹
cat -n 显示行
head -n 数字 默认看前10行 , 加上-n 之后可以指定查看多少行
tial -n 数字 查看最后几行 
tail -f 实时追踪该文件的所有更新
> 将内容覆盖在文件中
>> 在后面追加
ln -s 文件 链接的地方 
history 
! + history 中的指令编号,重新执行这条指令
date
cal 日历
find 路径 
-name 匹配文件名
-user 属于某个用户的
-size +n /-n /n 匹配文件大小 + 代表大于,-代表小于,不加代表等于 可加单位 K/M/G
locate 通过自建的数据库来进行查找,速度迅速,但是需要自己更新数据库
限制性updatedb


grep 
|grep + -n 显示行号/ -i 忽略字母大小写匹配 + 要匹配的关键字
权限
1-9位依次是 user group other的武安县
chown  -R 用户 文件 改变所属 -R来把里面的文件也执行这种修改
chgrp  组名 文件名 改变所有组

任务按时调度

crond -l 显示当前用户的定时任务
crontab -e 来编辑内容
语法:
* * * * * /path/to/command 也可以直接写一行shell脚本

1. 第一个星号:分钟(0-59)
2. 第二个星号:小时(0-23)
3. 第三个星号:日期(1-31)
4. 第四个星号:月份(1-12或使用缩写,如1代表一月,2代表二月,以此类推)
5. 第五个星号:星期几(0-7或使用缩写,0和7都代表星期日,1代表星期一,以此类推)

使用这些星号,您可以定义定时任务的执行时间。例如,以下是一些示例:

- `* * * * *`:每分钟都执行任务。
- `0 * * * *`:每小时的开始时执行任务。
- `0 0 * * *`:每天的午夜(凌晨12点)执行任务。
- `0 0 1 * *`:每个月的第一天(日期1)的午夜执行任务。
- `0 0 * * 5`:每个星期五的午夜执行任务。

如果您想要更具体的时间表,可以将具体的数字替换星号,例如 `30 8 * * 1-5` 表示每个工作日的上午8点30分执行任务。

at 执行一次性任务只会在指定的时间点执行但是不会重复执行措辞
用法
1. at + 时间  hh:mm
2. 输入要执行的指令然后按 ctrl + d 保存
atq 查看计划中的任务
删除计划任务
atrm  id 
crontab -r 删除定时任务

磁盘操作

lsblk 显示所有的磁盘和分区和挂载情况
fdisk 指定目录下的磁盘
mkfs 格式化磁盘
mount 磁盘目录 挂载目录 挂载分区
umount 取消挂载
df-h 查看磁盘状态 -T 查看磁盘类型

计算机网络

ifconfig 查看ip
netstat  查看系统网络情况
-an 按照一定顺序排序输出
-p  不加参数 显示哪个进程在调用

进程管理

ps 
-a 显示当前所有进程信息
-u 以用户的格式显示进程信息
-x 显示后台运行的参数
-e 显示所有进程 包括父进程等等
-f 全格式
kill  + pid 删除进程
-9 强制
killall 进程名称(支持通配符匹配)
pstree 查看进程树
-p 显示pid
-u 显示进程所属用户
/*service*/
systemctl +
start xxx 
stop
restart
status 查看某个服务的状态
enable 启动一个服务,使其自启动
list-unit-files --type=service | grep enabled 查看所有已启动的服务
get-default 查看系统的默认目标
set-default 设置默认目标
list-units 列出正在运行的单元

top 和ps相似,但是可以实时更新正在运行的的进程
-d + 秒数 每隔多少秒更新一次
-i 使top不显示任何僵死进程
-p + pid 监视某个指定进程的状态
top中使用
P 按照cpu使用率排序,默认
M 以内存使用率
N 以PID
q 退出

shell 编程

  • 重定向
  • here document
    #!/bin/bash
    cat << END
    "这里是ls的使用方法"
    END
    ls /root/shell_test
    
    输出>>>
    "这里是ls的使用方法"
    hell.sh  ls.sh	say.sh
    
  • 管道
    |&    将标准输出和错误信息一起传递给后面的命令
  • 命令分组
    {
    	commands
    	.......
    } > 文件
    使用 () 会将命令由子shell来进行执行
  • : 等于true
  • echo
    echo 
    -e 后面就可以加上换行符和制表符等特殊符号了
  • printf
    printf 格式(%s等) 参数
  • set / unset 设置/取消 环境变量
  • read 从标准输入读入放入后面给出的变量
  • wait 控制多进程
  • eval , exec , sed
  • source 执行shell脚本

k8s kubernetes

解决容器编排问题,实现分布式部署和替换,实现服务器集群

kubernetes组件
一个kubernetes集群主要是由控制节点(master)、**工作节点(node)**构成,每个节点上都会安装不同的组件。

  • master:集群的控制平面,负责集群的决策 ( 管理 )

ApiServer : 资源操作的唯一入口,接收用户输入的命令,提供认证、授权、API注册和发现等机制

Scheduler : 负责集群资源调度,按照预定的调度策略将Pod调度到相应的node节点上

ControllerManager : 负责维护集群的状态,比如程序部署安排、故障检测、自动扩展、滚动更新等

Etcd :负责存储集群中各种资源对象的信息

  • node:集群的数据平面,负责为容器提供运行环境 ( 干活 )

Kubelet : 负责维护容器的生命周期,即通过控制docker,来创建、更新、销毁容器

KubeProxy : 负责提供集群内部的服务发现和负载均衡

Docker : 负责节点上容器的各种操作

  • 基础概念
    Master:集群控制节点,每个集群需要至少一个master节点负责集群的管控

Node:工作负载节点,由master分配容器到这些node工作节点上,然后node节点上的docker负责容器的运行

Pod:kubernetes的最小控制单元,容器都是运行在pod中的,一个pod中可以有1个或者多个容器

Controller:控制器,通过它来实现对pod的管理,比如启动pod、停止pod、伸缩pod的数量等等

Service:pod对外服务的统一入口,下面可以维护者同一类的多个pod

Label:标签,用于对pod进行分类,同一类pod会拥有相同的标签

NameSpace:命名空间,用来隔离pod的运行环境

Jenkins

Jenkins原理

Gradle

与Maven相比,Maven更侧重于包的管理,而Gradle侧重于大项目构建

SVN

一种不同于git 的版本控制系统
SVN主要是集中式的版本控制系统:所有的文件版本信息都存储在中央服务器,用户进行工作时是创建一个副本,所以版本的历史是线性的
目录也是版本控制的一部分,分支和标签是目录的复制
而git是每个人的电脑上都是一个具体的版本库,版本的历史不是线性的,分支处理更加高效。
SVN的分支实际上就是新建一个目录,然后把原来的文件复制一份进去。
优缺点对比:

  • SVN:
    • 优点 更加高效的处理二进制文件,版本历史线性,容易理解,更好的处理权限访问和控制。
    • 缺点:提交和更改需要联网,然后分支合并更加麻烦,速度慢,每次操作都需要与服务器通信。
  • Git
    • 优点:分布式控制,离线也可以进行开发,速度更快
    • 缺点:学习比SVN复杂,对二进制的处理不好,容易造成性能问题。

Go学习

面经

基本数据类型

bool,string,int,byte(int8),int16,rune(int32),float32,float64,complex64/128

Go比起Java和C++ ,他与C的关系更密切

变量:

//声明多个变量
var (
	v1
    v2
)
//变量初始化,无需声明变量类型
v1 := 1
//多重赋值
v1 , v2 = v2 , v1 //实现交换两个变量,但是只能交换两个同一类型的变量

^75e37b

类型:

float32 //等于c中的float
float64 // double
//复数类型,和数学上的表达一样
var (
		v1 = 1 + 5i
		v2 = -6i
	)
	fmt.Println(v1 + v2)
//输出为
(1-1i)
//中文字符在UTF-8中占三个字节
s := "你好"
fmt.Println(len(s)) 
//数组通过range来遍历,range有两个返回值所以,第一个返回的是数组的下标,第二个是返回的数组的数值,所以要用两个变量来存储
s := "0123456"
	for i, value := range s {
		fmt.Println(i, value-'0')
	}
// 单个字符
var b byte = 'a'
注意:禁止字符串转int
rune 是单个Unicode 字符
//同时注意数组的初始化的方式是这这样的
s := [7]int{0, 1, 2, 3, 4, 5, 6} 
//当不需要某个返回类型时使用_来跳过这个返回类型即可
for _, value := range s {
		fmt.Println(value)
}

类似 type of的方法

type 别名 = 原来的类型名
const 常量声明

itoa 用于记录常量

字符转换

strconv.xxxx即可

数组

数组定于的一种方式,会根据给定的初始化的元素决定数组的长度

a := [...]int{1}

数组切片,类似于vector

切片创建方式有:

  1. 基于原有的数组来创建

    a := [7]int{0, 1, 2, 3, 4, 5, 6}
    var s []int = a[:]
    //需要使用Var 来声明切片数组的类型,等号的右侧可以时 array[first , end] ,想要截取的首位位置
  2. 直接创建

    //使用make进行直接创建
    s := make([]int , 5 , 10)//建立一个初始原为5个但是预留十个元素的空间的切片
    //直接在建立的过程中赋予初值
    s := []int{1, 2, 3, 4, 5}

切片的部分常用函数

  • len() 返回已经存储的元素的个数
  • cap() 返回切片分配的空间大小
  • append() 追加元素
  • copy() 复制

数组也可以定为接口数组,结构体数组,管道数组等等

//接口数组
var unknown [2] interface{}
var unknown [...] interface{123,"你好"}
//管道数组
var chanList = [2] chan int{}

定义空数组:

var a [0] int

a := [...]int{1, 2, 4, 5, 6}
fmt.Println(a)

map类型

Go中将其变为基本类型,可以直接使用,不需要引入库

声明:

var myMap map[键的类型]值的类型

创建:初始化创建时要注意在初始化的最后一个数据的后面加上逗号才代表初始化完成,否则会报错

//指定容量
myMap = make (map[], capacity)
//直接初始化
myMap = map[string]int{
		"1234": 1234, //一定要加上逗号
}
//直接声明+初始化一起
myMap := map[string]int{
		"1234": 1234,
}

删除

delete (myMap , key)

查找

value, ok := myMap["4"]
	if ok {
		fmt.Println(value, ok)
	}
	{
		fmt.Println("No")
	}		

流程

  1. if else 结构

    • if else 的括号必须在一行,否则编译失败,当出现else时,else 必须和 ‘’} ‘’在一行

      //正确
      if ok {
      		fmt.Println(value, ok)
      	} else {
      		fmt.Println("No")
      	}
      //错误
      if ok {
      		fmt.Println(value, ok)
      	}
      	else {
      		fmt.Println("No")
      	}
      //所以也推荐使用省略else的语句
      if ok {
      		fmt.Println(value, ok)
      	}
      	{
      		fmt.Println("No")
      	}		
    • 条件不需要使用括号

    • 花括号必须存在

  2. switch : 不需要显性的使用break来退出判断,默认的已经带有break了

    t := time.Now();
    switch {//用来替代if 语句,比起嵌套if更好判断代码
    case t.Hour() < 12 :
        xxxx;
    default :
        xxxx;
    }
  3. 循环

    for i:= ; i < n; i++{
                          
    }
    // while循环
    for a > 0 {
    
            fmt.Printf("a = %d\n", a)
    
            a--
    
       }

    更强大的break break后面可以加上标签,使用方法与goto一致

  4. 函数

    Go的函数拥有多重返回值,可以更方便的把函数的执行结果返回

    注意:小写字母开头的函数只有本包可见,而大写字母开头的函数才可以被其他包使用

    一般语法

    func 函数名 (参数)(返回列表){
        
    }

    不定参数 在写形参列表的时候使用…来省略参数即可达到不定参数的作用

    例:

    func name(a ...int) int {
    	return 1
    }
    func main() {
    	fmt.Println(name(1))
    	fmt.Println(name(1, 2, 3))
    }
    

    概念: 这种使用方式是语法糖,语法糖对语言的功能没有用影响但是可以方便使用,也能够增加程序的可读性,减少出错的机会

    这里的 … 相当于一个数组切片,等价于下面的语句

    func name(args []int) int {
    	return 1
    }
    func main() {
        //调用的时候必须使用这种方式进行调用了
    	fmt.Println(name([]int{1}))
    	fmt.Println(name([]int{1, 2, 3}))
    }
    

    匿名函数:Go支持随时随地定义匿名函数

    //在定义的时候进行调用
    func main() {
    	fmt.Println(func(a int) int {
    		return a
    	}(1))
    }
    //匿名函数赋给变量
    func main() {
    
    	f := func(a ...int) int {
    		return a[0] // 注意此处的a相当于 args [] 所以a不是int型不可以直接返回
    	}
    	fmt.Println(f(1, 2))
    }

    闭包

    闭包是由函数及其相关的引用环境组合而成的实体(即:闭包=函数+引用环境)。

    匿名函数是一类闭包

    • 包含自由变量的代码块,这些变量不在这个代码块内或者任何全局上下文中定义,而是在定义代码块中的环境中进行定义的。要执行的代码块为自由变量提供绑定的计算环境(作用域)
    • 闭包每次调用都是新的实例
    • 闭包中的变量会保存,即使调用结束也会保存,,可以保证闭包中的变量的安全性,不会被外部函数修改

    闭包可以获得所在函数内的作用域,但是外部不可修改闭包的数据

    package main
    
    func main() {
    	b := 0
        //闭包
    	f := func(b int) {
    		b++
    		println(b)
    	}
    	f(b)
    	f(b) //每一次调用都是新的实例
    	println(b)
    }
    

    返回函数的函数

    package main
    
    import "fmt"
    
    func main() {
    	f := Add1()
    
    	fmt.Println(f(1, 2))
    	//fmt.Println(Add1()(1, 2)) 另一种方式
    }
    //返回了一个 匿名函数
    func Add1() func(a, b int) int {
    	return func(a, b int) int {
    		return a + b
    	}
    }
    
  5. 错误处理

    error接口是内置的,里面只有一个方法:

    type error interface {  
        Error() string
    }

    defer

    类似于析构函数,遵循先进后出,用于函数执行之后进行一些资源释放的收尾工作

    使用语法:

    defer + 执行语句
    要执行语句很多时可以写一个匿名函数来进行处理
    defer func (){
        回收工作
    }() //调用

    panic() 和 recover()

    panic ( ) 用于立刻终止程序,但是defer不影响,会正常执行,panic可以接受任意类型的数据

    recover ( ) 用于终止错误处理流程,一般放在defer中来截取错误信息

面型对象编程(OOP , Object Oriented Programming)

  1. 类型系统

    Go可以给任意类型(包括内置类型,但不包括指针类型)添加相应的方法

    type Integer int
    //Integer 与int 并无区别,只是我们认为给他加上了一个自带的方法
    func (a Integer) less(b Integer) bool {
    	return a < b
    }
    func main() {
    	var a Integer = 5
    	defer fmt.Println("回收了")
    	if a.less(2) {
    		fmt.Print(a, "<", 2)
    	} else {
    		defer fmt.Println("错误回收")
    	}
    
    }
    
  2. 面向对象只是换了一种方式来表达语法,所以实质上也是语法糖

    Go没有隐藏的指针

    • 方法的对象显式传递
    • 方法的对象不需要是指针,也不需要是this
  3. 对于public 类, 需要采用类名首字母大写

    package main
    
    import "fmt"
    
    type Base struct {
    	Name string
    }
    
    func (base *Base) Foo() {
    	fmt.Println(1)
    }
    func (base *Base) Bar() {}
    
    type Foo struct {
    	Base
    }
    
    func (foo *Foo) Foo(name string) {
    	foo.Name = name
    }
    func (foo *Foo) Bar() {
    	println(foo.Name)
    }
    func main() {
    	base := new(Base)
    	base.Foo()
    	foo := new(Foo)
    	foo.Foo("123")
    	fmt.Println(foo.Name)
    }
    
  4. 接口

    其他语言在使用接口时必须要先从接口进行继承,才能进行实现接口

    简言之,必须知道接口要实现什么才能定义接口,但实际情况是,不知道接口要实现什么

    例如

    //	Java 语法
    interface IFoo{
        void Bar();
    }
    class Foo implements IFoo {
        //....
    }

    问题:

    1. 提供哪些接口好呢?
    2. 如果两个类实现了相同的接口,应该把接口放进哪个包内?

    Go的接口是非侵入式的,只要类实现了接口要求的所有函数,就算是实现了这个接口,可以直接进行赋值

    package main
    
    import "fmt"
    //实现接口的类
    type File struct {
    }
    
    // 实现类方法,也就是接口的方法
    func (f *File) Print(str string) {
    	fmt.Println(str)
    }
    //两个以后出现的接口
    type IFile interface {
    	Print(str string)
    }
    type IPrint interface {
    	Print(str string)
    }
    
    func main() {
    	var file1 IFile = new(File)
    	var file2 IPrint = new(File)
    	file3 := new(File)
    	file1.Print("IFile接口实现")
    	file2.Print("IPrint接口实现")
    	file3.Print("实现接口的类,并不需要提前知道有哪些接口,只要能够实现了以后会出现的接口的函数即可直接使用")
    
    }
    

    但是 Go 语言里有非常灵活的 接口 概念,通过它可以实现很多面向对象的特性。接口提供了一种方式来 说明 对象的行为:如果谁能搞定这件事,它就可以用在这儿。

    接口定义了一组方法(方法集),但是这些方法不包含(实现)代码:它们没有被实现(它们是抽象的)。接口里也不能包含变量。

    type Namer interface {
        Method1(param_list) return_type
        Method2(param_list) return_type
        ...
    }

    使用接口时,如果某个类实现了接口的方法,那么接口类型的变量可以赋值为这个类的变量

    如果接口接口A和接口B的方法是一致的,那么在Go中属于完全等价

    如果A接口的方法是B接口方法的子集,那么B接口可以赋值给A接口,而A接口不能赋值给B接口

    接口查询:用于查询某个接口是否属于某个类型

    实例 num2 是否属于接口 Number1,可以这么做:

    var num1 Number = 1;
    var num2 Number2 = &num1;
    if num3, ok := num2.(Number1); ok {
        fmt.Println(num3.Equal(1))
    }

    如果num2 实例所指的对象是属于Number1的,那么ok值为1 ,num3 转化为 Number1的一个实例,之后执行条件内的代码

    类型查询:用于查询接口指向的对象实例的类型

    语法:

    switch v := v1.(type){ 	
    	case int:
        case string:
        ....
    }

    两者一般搭配使用

    Any类型

    任何对象实例都满足空接口 interface { } 所以interface { } 可以作为一个可以指向任何对象的Any类型,当函数可以接受任何实例时,可以将其声明为interface { }

    例如: fmt库中的Print函数

    func Print (fmt string , args ...interface{})

并发编程

主流实现模型:

  1. 多进程

    操作系统层面进行的

  2. 多线程

    操作系统之上的调度

  3. 基于回调的非阻塞/异步IO

    通过事件驱动的方式使用异步IO,使服务器持续运转并且尽可能的少用线程,降低开销,代表有Node.js,会对流程进行分割

  4. 协程

    本质是用户态线程,不需要抢占式调度,寄存于线程,缺点是需要语言支持

gorountine

通过在代码中加上go关键字启用协程,主程序结束时,写成会被结束

通信方式:消息机制和共享内存

消息机制:每个并发单位都是自包含、独立的个体,都有自己的变量,并且单元间的变量不共享,每个单元的输入输出只有一种那就是消息。不同进程依靠消息来进行通信,他们不会共享内存

channel

Go提供的goroutine间的通信方式,是进程内的通信方式,不适合进程之间进行通信

一个channel 只能传递一种类型的值

基本语法

//声明
var chanName chan ElemenType 
//例如 声明一个传递类型为int的channel
var ch chan int 
//定义
ch := make(chan int)
ch := make(chan int , 2) //缓冲大小为2
//写入channel
ch <- value //写入数据会导致陈孤虚阻塞,直到有其他goroutine 从channel中读取数据
//读取
value := <- ch // 如果channel中没有数据也会进行堵塞

示例:

package main

import "fmt"

func Count(ch chan int) {
	fmt.Println("Counting")
	ch <- 1 //写入数据,导致这个goroutine阻塞,直到有其他goroutine从中读取数据
}
func main() {
	chs := make([]chan int, 10) // 储存十个goroutine的channel数组
	for i := 0; i < 10; i++ {
		chs[i] = make(chan int)
		//启动十个协程
		go Count(chs[i])
	}
	for _, ch := range chs {
		<-ch //读取channel
	}
}

select

文件发生IO动作,select会被调用并返回

语法: 条件必须是IO操作

select {
    case <- chan1 : //从chan1成功读取到数据
    case chan2 <- 1 : //成功向chan2写入数据
    default
}

示例:

package main

import "fmt"

func main() {
	ch := make(chan int, 1)
	for j := 0; j < 3; j++ {
		//随机向ch 中写入一个1或者是0
		select {
		case ch <- 0:
		case ch <- 1:
		}
		i := <-ch
		fmt.Println("Value Received :", i)
	}
}

缓冲机制

建立channel数组即可实现缓冲

ch := make (chan int , 1024)

超时机制

Go中没有提供查实处理机制,但是可以使用select来处理,因为select只要有一个case已经完成即可继续进行下去

package main

import (
	"fmt"
	"time"
)

func main() {
	ch := make(chan int) //未写入任何数据,所以会把程序堵塞
	timeout := make(chan bool, 1)
	go func() {
		//等待一秒钟
		time.Sleep(1e9)
		timeout <- true
	}()
	select {
	case <-ch:
		fmt.Println("读取中")
	case <-timeout:
		fmt.Println("超时了")
	}
}

channel 传递,可以用来实现pipe 待补充

单向channel 用于防止写入某些不需要的数据或者被不需要的函数读取

只能读或者只能写

定义

var ch1 chan int //正常的channel
var ch2 chan <- float64 // 只能写
var ch3 <- chan int //只能读
//初始化
ch4 := make (chan int) // 正常
ch5 :=  <- chan int (ch4) // 从正常的channel进行类型转化为单项读取的channel
ch6 :=  chan <- int (ch4) // 转化为单项写入的channel

关闭channel

使用close()即可

如何判断已经关闭?

多重返回值的方式进行帕努的那

x , ok := <- ch
//只需要看ok即可,如果ch已经关闭,那么读取失败,ok的返回值为false 

同步问题

同步锁

sync包中提供

Mutex锁

当一个goroutine 获得了Mutex 后,其他的goroutine 只能等待这个释放锁

RWMutex 锁

单写多读 , 在读锁占用时,会阻止写,但是不会阻止读

全局唯一性操作

使用Once类型,当别的goroutine

网络编程

Socket编程 IP 层

socket是什么?

Socket是对TCP/IP协议的封装,自身并非协议而是一套调用的接口规范(API)。通过套接字Socket,才能使用TCP/IP协议。

传统的Socket编程主要步骤:

  1. 建立Socket: 使用socket() 函数
  2. 绑定Socket :使用bind() 函数
  3. 监听: 使用listen() 函数,或者connec() 函数
  4. 接受连接: accept() 函数
  5. 接收: receive() 函数 ,发送send() 函数

Go的:

Go语言标准库对此过程进行了抽象和封装,无论我们期望使用什么形式的连接,只需要调用net.Dial()即可

Dial原型

func Dial ( net , addr string ) ( Conn , error) 
/*
net : 网路协议名称
addr IP地址或域名,端口以":" 的形式跟在后面,端口号可选
Conn 是否成功连接
*/

工程管理

代码格式化

格式化之前的:

package main

import "fmt"

func Foo ( a , b int)(ret int ,err error){
if a>b {
return a , nil	
}else {
	return b , nil
}
return 0,nil
}
func 
main()  {
	i , _ := Foo(1,2)
fmt.Println("Hello,word" ,i)	
}

使用命令行: go fmt xxx.go得到的代码,也可以直接go fmt 会格式化所有*.go的文件

package main

import "fmt"

func Foo(a, b int) (ret int, err error) {
	if a > b {
		return a, nil
	} else {
		return b, nil
	}
	return 0, nil
}
func main() {
	i, _ := Foo(1, 2)
	fmt.Println("Hello,word", i)
}

gotool

生成exe

gobuild 后面可以加上-o + name 指定执行文件的名字

格式化输出: Printf

%v	按值的本来值输出
%+v	在 %v 基础上,对结构体字段名和值进行展开

生成随机数需要设置随机数种子

func main() {
	v := 100
	rand.Seed(time.Now().UnixNano())
	a := rand.Intn(v)
	fmt.Println(a)
}

依赖管理

Gin

前言:

Github对学生有很多优惠,至于哪些可太多了,所以有必要申请一个

这个大佬写的很详细了,我在这里加一些我踩得坑,如果跟大佬的步骤走没通过的话,可以回来看看有没有我踩过的坑

2023.3申请github copilot x 学生认证以及Jetbrain专业版学生教育免费教程 - 知乎 (zhihu.com)

前提条件:

  1. 一个好的浏览器(支持定位的浏览器)
  2. 一份学信网学籍认证 和 一个教育邮箱(更容易通过 ,可以去企业微信里看看有没有开通)
  3. 修改pdf文件中的信息
  4. github profile 中修改个人信息
  5. 认证并且全程不要挂梯子

1.好的浏览器

这是重中之重,我在申请的时候不知道Chrome怎么了,一直定位不成功,换成 edge之后就成功了

2.学信网认证

进入学信网,下载电子报告即可,网上都有教程,这里不做赘述了

中国高等教育学生信息网(学信网) (chsi.com.cn)

3.修改pdf文件信息

因为github不是国内的,所以中文档案是不能通过的,而且pdf编辑好像很难搞,这里给一个下载链接,使用这个进行编辑会方便些

密码:tko9

Adobe CC 2022 免费高速下载 | 天翼云盘 珍藏美好生活 家庭云|网盘|文件备份|资源分享 (189.cn)

下载安装即可

修改内容:

2023.3申请github copilot x 学生认证以及Jetbrain专业版学生教育免费教程 - 知乎 (zhihu.com)

可以看这篇文章

4.修改github profile 中的信息

  • 名字改为英文名
  • Bio 修改为 student of 学校英文名 + @ 学校英文名
  • company 修改为学校英文名

5.申请:

网站:Request a discount - GitHub Education

全程不要挂梯子

  1. 添加教育邮箱

    添加邮箱

  2. 填写申请理由,理由随便写,可以参考我的

    I would like to use the Github Student Pack to further explore more features of Github, such as Github Copilot and Educative's learning courses.
  3. 点击continue

    点击之后会询问你是否允许访问位置,同意即可

    如果点击之后,一直卡在这个页面,那么是浏览器的问题,可以换个浏览器试试或者过几天再试试

    我就是换了好几个不成功,最后莫名其妙就能访问了

    之后进入上传图片的页面,右下角有上传图片 (如果你没有的话,说明你上梯子了或者离学校太远了,直接重新认证一遍吧)

    上传修改之后的学信网认证截图

    注意:上传图片格式是jpg 不支持png!!!

  4. 点击提交,如果按以上流程走的话应该是秒通过的,看看邮箱是否收到邮件,或者返回认证申请的第一页,看看右侧是否通过

    Request a discount - GitHub Education

写在最后:

一定要有耐心,不挂梯子的话,github的访问基本上是时有时无的,所以要有耐心,多试试,过几天再试试,推荐在学校里申请,或者使用学校的vpn访问网站

祝通过!!!

Elysia会祝福你的

新坑 到P83页了。。。

HTML5基本语法

<hr> 一个横线
<label> </label> 用于为input 元素进行标注 其 for 属性应与要绑定的元素的id相同,form属性用于规定其属于哪个或者多个表单
<!DOCTYPE html> <!--文档声明,目的是让浏览器知道这是什么类型的文件-->
<html > <!--文档的开始,没有什么实际含义-->
    <head> <!--头标记,放在html 内来防止这个文件中的信息,比如提供索引信息或者定义CSS样式等-->
         <!--标题标记,网页的标题,也就是上方的标~ 题,不是内容的标题-->            
        <title> 
           
        我的测试文件
        </title>
    </head>
    <body> <!--文档正文的开始--> <!--文本属性中可以加入text 来表示文本的颜色,bgcolor来表示背景的颜色-->
        <!-- <body text="red" bgcolor="blue">-->
        <!--hn 各种大小标题-->
        <h1>
            <font color = "blue"> <!--设置字体颜色-->
                蓝色的一级标题
            </font>
            
        </h1>
        <p>
            <!--段落标记-->
            这是我的第一个测试文件
        </p>
        <!--文字样式标记-->
        <b> <!--加粗-->
         <i>
            <!--斜体-->
            这是加粗斜体字
         </i>
        </b>
        <br>
        <!--删除线-->
        <s>删除线</s> 
        <br>
        <!--下划线-->
        <u>下划线</u>
        <br>
        <!--放大缩小,加强强调,强调,现实电子邮件或者地址,写代码-->
        <big>放大</big>
        <br>
        <small>缩小</small>
        <br>
        <!--加强强调-->
        <strong>加强强调</strong>
        <br>
        <em>强调</em>
        <br>
        <address>2354796263@qq.com</address>
        <br>
        <code>
            #include <bits/stdc++.h>
            using namespace std;
            int main(){
                cout << "Hello World!"<< endl;
                return 0;
            }
        </code>
        <!--文字上浮和下浮-->
        <p>文字<sup>上浮</sup> <sub>下浮</sub> </p>
        <!--文字样式标记-->

        <!--文本的位置-->
        <center> <font color = "red" face = "宋体" size = "7"> 实现居中</font></center><br> <!--font 还可以设置字体和大小,属性是 face 和 size-->
        <h4  align = "right"> <font color = "blue">实现右侧</font> </h4>
        <br>
        <!--照片的操作-->
        <img src="source/image/Elysia.jpg" alt="图片丢失时显示" title="鼠标放在上面可以看到">
    </body>
</html>

导航菜单

使用
<ul> + <li> + <a> 即可,之后在css中进行设置样式

设置表格:

<table>表格 <tr> 定义一行 <col> 定义一列 <td> 定义一个单元格  <caption> 表格的大标题 <th> 表头,主要用于行或列的名称,<th><td> 相似
实例:
     <table border="2" cellspacing ="2" bgcolor = "#eeeeee">
        <caption>成绩表</caption>
        <tr> <!--开始定义表头-->
            <th>姓名</th> <th>性别</th> <th>成绩</th>
        </tr>
        <tr><th>张三</th> <td></td> <td>114</td></tr>
        <tr><th>李四</th> <td></td> <td>514</td></tr>
    </table>
    使用<td> 中的rowspan 和 colspan 来合并单元格

设置表单

<form><input> 两个标签
实例:
    <form action="">
        姓名:<input type="text">
    </form>
    type = "text"
    name 设定文本框的名称,在交互程序中可以用到
    size 数值,设定此一栏位要显示的宽度
    value 预设内容
    maxlength 设置文本可输入的最大最大文字长度 
    type 属性内容:
    type = "radio“ 单选
    可选属性:
    checked 设置某个选项为默认选
    name  name属性内容相同的两个选项只能选中一个
    type = "checkbox" 多选
    可选
    checked
    name 需要将一组选中中的所有复选按钮设置为相同的名称才能让服务器处理时知道这几个选项是一个组的
    type = "password" 密码框
    属性与文本类型相同
    type = "submit" 提交按钮
    type = "reset"  重置按钮
    type = "image"  提交图片
    type = "textarea" 多行文本框
	可选:
    cols 定义文本框的宽度,字符的列数
    rows 字符行数
    wrap 定义换行方式 主要有 off 文字不自动换行, virtual 输入文字时会自动换行,但是如果没有自己按下回车键换行,提交到服务器时结果就时没有换行
    physical 自动换行,提交到服务器结果也是换行
    列表框:
    在form中使用 <select> 标签
    在<select> 中使用<option>来设置选项</option>

## class 的用法:

- 一个对象可以包含多个class(方便了在CSS中定义)

  ```css
   <style type="text/css">
          .Ely{
              font-size:larger;
          }
          .red{
              color : purple;
          }
      </style>
  
  <p class ="Ely red" align = "center" >Elysia  <small></small>  </p> 

CSS

  1. 基本规则:

    张飞{
        身高:
        体重:
        性别
        民族
    }
  2. 选择器

    对网页中指定的对象进设置

    • 标记选择器:所有同名的标签都会被选择

      使用html 的标记标签进行选择,例如:<p>

      基本语法:

      <style>
      h1 {     标签选择
          声明
          color :red ;  属性 + 值
          font_size :25px;
          
      }
      </style>
    • 类别选择器:实现只修改指定的标签class

      基本语法:

      .class { . + 类别名(自定义的名字)
       	声明   
      }
    • ID选择器,**id在HTML中具有唯一性,不可重复,尽量做到一个标签一个id**

      ヽ(≧□≦)ノ

  3. HTML中使用方法:

    • 行内央视,直接使用 <style>属性 注意,每一个声明之后要加入分号

      <p style="color : purple; font-size :larger" align = "center">Elysia    </p> 
    • 内嵌式一般放在 <head> 标签之间

    <head>
        <style type="text/css">
            .Ely{
                font-size:larger;
            }
            .red{
                color : purple;
            }
        </style>
    </head>
    • 链接式,将CSS与HTML文件分离 在html中使用 <link> 标签进行 导入

      <link rel="stylesheet" href="文件地址">
    • 导入式 使用@import 在html初始化时就会进行导入,而使用链接式只有在需要格式时才进行导入

      /*需要放在style 标签中*/
      <style>
              @import "1.css";
          </style>
  4. Tips:CSS中遵循覆盖规则,对于同一个被选择的对象,后面的定义会覆盖前面的定义,所以可以使用标签进行全局定义,在使用class 来对指定的对象进行微调

  5. Tips2:优先级

    最后面导入的样式 > 最后面的连接式 , 链接式与 <style> 遵循先后顺序, 同一个 <style> 中内嵌式 > 导入式

  6. CSS3新特性:

    交集选择器:

    Ely{
        color :purple;
    }
    p{
        color: red;
    }
    p.Ely{
        color :blue;
    }
  7. 并集选择器:使用 , 进行隔开即可

    p,.Ely,h5{
        color :blue;
    }
  8. 全局选择器:* 使得所有HTML标签都遵循这个规则

    *{
        color :blue;
    }
  9. 后代选择器,空格隔开即可

    li ul {
        color :blue;
    }
    li ul ul {
        color : red;
    }
    li ul ul ul {
        color : purple;
    }
  10. 只对直接后代有用的选择器: p > 可搭配*使用

  11. ID 选择器:#+id

  12. 相邻选择器:A+B 直接兄弟,紧跟着的后面那个

  13. 相邻兄弟组选择器:A~B 后面的所有兄弟

  14. 属性选择器: 使用中括号进行选择,属性是自定义的

    <p ely = "123">最喜欢爱莉希雅了</p>  /*这里ely是自定义的属性,它的值是123*/
    
    
    
    
    p[ely]{ /*使用这个进行选择,即可*/
        color : red;
    }
    /*也可以使用属性+属性的值来进行选择*/
    /*需要属性的值必须是给定的这个值*/
    p[ely = "123"]{
        color : red;
    }
    /*属性包含选择器,只需要属性的值包含给定的值的即可*/
    p[ely *= "1"]{
        color : red;
    }
    
    
    <p ely = "123 4 5 6">最喜欢爱莉希雅了</p>
    /*单词包含选择器 , 只要包含给定单词即可*/
    p[ely ~= "123"]{
        color : red;
    }
    /*以某个单词或者字母开开头的选择器*/
    p[ely ^= "1"]{
        color : red;
    }
    /*以什么结尾的选择器,只要以某个单词结尾或则和某个字母结尾即可,数字也行*/
    p[ely $= "6"]{
        color : red;
    }
  15. 结构伪类选择器

    /*
    基本语法: E是父元素
    E : nth - child 第几个元素
    E : root 		根元素,HTML中根元素始终为html
    还有很多。。。自己去看吧 
    给个链接:https://juejin.cn/post/7001869576069873695
    */
  16. 伪元素选择器 待补

CSS继承和层叠特性

子标记会继承夫标记的所有样式风格,但是子标记的改变不会影响父标记

层叠是出现冲突时的处理方式,越特殊的样式,优先级越高,

层叠优先级:行内样式> ID 样式 > 类别样式 > 标记样式

CSS设置文本样式

单位:

  1. 相对类型:

    px : 像素,根据设备类分辨率来进行调整,最常用的

    em: 设置以目前字符的高度为单位

    rem: root em 设置以网页跟元素HTML 字符的高度为单位, 一般浏览器莫默认单位为 1rem = 16px

    vw 和 vh 基于视图窗口的相对单, 1vw 等于视口宽度的1% 1vh 等于窗口高度的的1%

  2. 绝对类型

    in 英尺

    cm 厘米

    mm

    pt 点数

    pc 印刷单位 1pc = 12pt

颜色:

HTML中统一使用RGB模式,基本设置 rgb(red, green , blue , 透明度 )

基本设置:

font-family : 设置字体

font-style : 设置斜体

font-weight: 设置加粗,参数有 normal, bold(加粗) , bolder(更粗),lighter(更细), 100- 900 按每100为为一个阶段,数字越大,字体越粗

font-transform: 大小写转换

font-size:字体大小

text-decoration:文字的修饰,主要有underline 下划线,line-through 删除线,overline为文字加顶线,blink 文字闪烁(部分浏览器支持)

texr-indent:首行缩进,后面跟缩进的长度,一般使用2em来实现缩进两个字

letter-spacing: 控制字母间距

word-sacing:控制单词间距

line-height: 行高

border :添加边框,可以设置粗细,颜色和虚实

margin 外边框 后面两个参数分别是设置上下距离和左右距离

text-align : 设置文本的水平位置,left,right,center, justify(两端对齐)

background: 设置背景色

text-shadow: 给图片添加阴影效果,后面四个参数是x轴的偏移尺寸,y轴的便宜尺寸,阴影半径,阴影颜色

颜色

  • rgba () 第四个参数是透明度

CSS设置图片属性

border-width 设置边框的粗细

border-color 设置边框的颜色

border-style 线性,可以在一些预先定义好的线型中选择

也可以

border-上下左右-属性 来单独设置上下左右的边框

甚至可以直接border : 后面写大小类型和颜色

例如: border : plum 2px dotted;

实现文字环绕:

float : 将图片移动到页面左侧或者右侧从而使得文字能够从另一端来环绕

盒模型

DOM :document object model 文档对象模型 ^eb3dd6

padding -border-margin 模型 (顺序是上右下左)

  • Margin(外边距) - 清除边框外的区域,外边距是透明的。
  • Border(边框) - 围绕在内边距和内容外的边框。
  • Padding(内边距) - 清除内容周围的区域,内边距是透明的。
  • Content(内容) - 盒子的内容,显示文本和图像。

一个盒模型包括:content(内容) 、padding(内边距) 、border(内容的边框), margin (外边距)

标准文档流:normal document stream

div 区块容器标记,里面可以容纳各种HTML元素,可以将div看作一个对象

span 也是区块标记

二者区别: div 内的元素可以自动换行,

span 不会换行,没结构上的意义,仅仅是一个行内元素,当其他行内元素不适合的时候就可以使用span了

span 可以包含在div中,但是div不能包含在span 中,使用display来让行内元素表现的像块级元素

相邻的两个行内元素的margin 是第一个的right-margin 第二个的left-margin 之和

边距控制

  • border-bottom

背景

background-image : url() 插入图片,

一般情况下,图片会自动向水平和数值两个方向平铺,可以使·用以下属性来进行控制

background-repeat :

repeat 默认,沿水平和竖直方向平铺

no-repeat 不平铺,只显示一次

repeat-x 沿水平平铺

repeat-y 沿竖直方向平铺

background-position 设置背景的位置

也是可以合并为一行的: 直接background + 属性即可

background-radius: 来设置盒模型的四个角是否圆滑 , 后面的四个参数是从左上角开始,顺时针旋转的

box-shadow 给盒模型设置阴影, 后面四个参数分别是水平偏移量,垂直偏移量,模糊,阴影扩展尺寸,阴影颜色,外部阴影或者是内部阴影

background 函数

linear-gradient(direction,指定渐变方向,color-stop1,...)

链接:

a{/*通用*/
    text-decoration: none;
}
/*以下是使用了CSS伪类型属性来进行修饰*/
/*
设置顺寻时 LoVw HaTe 爱恨
link 
visited
hover
active
*/
a:link{ /*普通浏览*/
    color: black;
}
a:visited{/*点击过之后*/
    color: black;
}
a:hover{  /*鼠标指针经过时*/
   color: plum;
   text-decoration: underline;
}
a:active{/*点击时*/
    color: blue;
}

filter

https://developer.mozilla.org/zh-CN/docs/Web/CSS/filter
修饰鼠标指针

cursor :属性即可

修饰列表

list-style-type:

属性有:

disc 实心圆、

circle 空心圆

square :正方形

decimal : 十进制数字

upper-alpha 大写字母

lower-alpha 小写字母

upper-roman 大写罗马数字

lower-roman 小写罗马数字

none 不显示任何符号

JavaScript


~~比起c语法更像是Java~~

组成:

ECMAScript,DOM , BOM

1. ECMScript 语言
2. DOM 文档对象模型,使用节点来web
3. BOM 浏览器对象模型

### 语法:

区分大小写,变量无类型,只需要使用let进行声明即可,可初始化为任意类型的值
### 数据类型
```js
number 数字包括,整数小数,NaN Not a Number
string
boolean
null 对象未空
undefined 当声明的变量未初始化时该变量的默认值
使用typeof 来获取数据的类型

注释与C相同

HTML中的调取方式:

1.使用 script 标签直接调用

2.通过 script 中src属性进行调用外部文件

var和let的区别

var 声明是全局作用域或函数作用域,而 let 和 const 是块作用域。 var 变量可以在其范围内更新和重新声明; let 变量可以被更新但不能重新声明; const 变量既不能更新也不能重新声明。 它们都被提升到其作用域的顶端

判断

JavaScript中 == 在比较两侧对象时,如果不同吗,会将其转化为同一个类型再比较,而 === 严格等于不会强制类型转换

!= 不等于 !== 不严格等于

jQuery

是JavaScript的一个库,简化 了js的操作

包含功能:

  • HTML 元素选取
  • HTML 元素操作
  • CSS 操作
  • HTML 事件函数
  • JavaScript 特效和动画
  • HTML DOM 遍历和修改
  • AJAX
  • Utilities
    使用方式: 查询使用版本,F12打开控制台之后从console中输入$.fn.jquery 即可查询
    <script src = "本地的jQuery文件或者CDN的url即可"
  • 基础语法: 通过选取HTML元素对选取的元素执行操作
    $(selector).action() 
    selector 选择符 查询和查找HTML元素
    action() 执行对元素的操作
    //示例
    $(this).hide() 隐藏当前元素
    $("p").hide()  隐藏所有<p>元素
    $("p.test").hide() class = "test"
    $("#test").hide() id = "test"
    防止在文档未完全加在就绪之前就运行jQuery代码,可以使用以下两种方式之一来解决
    $(document).ready(function(){
    // 开始写 jQuery 代码... 
    });
    或者
    $(function(){ 
    // 开始写 jQuery 代码... 
    });
    
    选择器:
    • 所有元素
  • this
  • .class
  • “#id”
  • xxx:first 第几个xxx元素
  • 后代用空格隔开
  • “[herf]” 选取带有herf 属性的元素
  • “A[B=’C’]” 或者 “A[B!=’C’]” 选取所有B属性值为C或者不为C的A的元素
  • :button 选取所有type = button的元素
    常用的事件方法:
  • click()点击
  • dbclick()双击
  • mouseenter鼠标穿过元素时会发生
  • mouseleave鼠标离开元素
  • mousedown 鼠标移动到元素上并进行按下
  • mouseup 松开鼠标按键
  • hover悬停
  • focus 获得焦点
  • blur 失去焦点
    效果:
  • hide,隐藏,show显示
  • toggle 在hide和show之间进行切换
  • fadeIn淡入,fadeOut 淡出, fade Toggle()切换淡入淡出
  • fade To() 允许渐变给不透明度
  • $(selector).fadeTo(speed,opacity,callback);
  • 滑动 slideDown向下滑, slideUp向上滑, slideToggle()切换
  • 动画操作: $(selector).animate({params},speed,callback);
  • params定义动画CSS属性,可使用相对值, +xxx
  • 使用队列:
    $("button").click(function(){ var div=$("div"); div.animate({height:'300px',opacity:'0.4'},"slow"); div.animate({width:'300px',opacity:'0.8'},"slow"); div.animate({height:'100px',opacity:'0.4'},"slow"); div.animate({width:'100px',opacity:'0.8'},"slow"); });
  • 停止动画: stop()
  • 动画链: 连续点即可 $(“#p1”).css(“color”,”red”).slideUp(2000).slideDown(2000);

DOM操作

  • text 设置或返回所选元素的文本内容
  • html 设置或返回所选元素的内容 包括html标签
  • val 设置或返回表单字段的值
  • attr 获得属性值
  • append 在被选中元素的结尾追加内容
  • prepend 在被选中元素的开头插入内容‘
  • after 在被选中元素之后加入内容
  • before 在被选中元素之前加入内容
  • remove 删除元素
  • empty 从被选中元素删除子元素

操作CSS类 (要提前定义css样式才能添加)

  • addClass 向被选中元素添加一个或多个类
  • removeClass 从被选中元素删除一个或多个类
  • toggleClass 对被选中元素进行添加/删除的切换擦偶哦在
  • css 设置或返回样式属性 (可直接添加属性值)
  • $(“p”).css(“background-color”,”yellow”);
  • $(“p”).css({“background-color”:”yellow”,”font-size”:”200%”})
    尺寸
  • width , height , (context大小)
  • innerWidth , innerHight ,(内边框,padding)
  • outerWidth , outerHight (margin)
    遍历DOM树
  1. 向上遍历
    • parent
    • parent 向上到根元素
    • parentsUntil 返回介于两个给定元素之间的祖先元素
  2. 向下遍历
    • children 直接子元素
    • find 查找到最后一个元素
  3. 同级遍历
    • siblings 所有同级元素
    • next 下一个同胞元素
    • nextAll 下一个所有同胞
    • nextUntil
    • jQuery prev(), prevAll() & prevUntil()
  4. 过滤
    • first 被选元素的首个元素
    • last
    • eq返回都带有指定索引号的元素(索引从0开始)
    • filter 过滤条件
    • not

jQuery AJAX使用

  • load 从服务器加在数据并把返回的数据放入备选元素中
  • $(selector).load(URL,data,callback)
  • get() post()
    $("button").click(function(){ $.get("demo_test.php",function(data,status){ alert("数据: " + data + "\n状态: " + status); }); });
    ///////////////////////////////////////
    $("button").click(function(){ $.post("/try/ajax/demo_test_post.php", { name:"菜鸟教程", url:"http://www.runoob.com" }, function(data,status){ alert("数据: \n" + data + "\n状态: " + status); }); });

例如:

    let a = 1;
    let b = "1";
    console.log(a == b);
    console.log(a === b);
//输出为
true 
false

 	let a = 1;
    let b = "1";
    console.log(a != b);  //不进行类型转换
    console.log(a !== b); // 进行类型转换
//输出为:
false
true

for of 语句 (和foreach 一样)

for (a of b){
    
}

for in 枚举对象的的属性

函数:两种写法

function name (){
    
}.

//或者
name = function(){
    
}

通过使用argumens对象,可以 不用声明形参的名称

arguments[0] 即为第一个形参

arguments.length() 可以获得传入函数中的参量个数,从而用于判断。

箭头函数(类似于Lambda表达式)

let name = (参数) => {  }

类是具有相同属性和功能的“对象” 的抽象

对象是从类中创建的实例

属性是对象中的变量

方法是类中的函数

引用类型:Boolean , Number 和String 类比Java

对象:

var person = {
  xxx : xxx  
};

赋值

let myCar = {
   size : "large",
   color : "blue",
   eat : function () {
    console.log("吃吃吃");
   }
}
let {size , color} = myCar; // 同名可用这种方式一键赋值
console.log(size);
console.log(color);

集合

数组

数组进行复制:

let a = [1 , 2 , 3 , 4]
let b = [...a];;
//补充,对象复制
let obj = {name : "Bob" };
let c = {...obj};

JavaScript中数组内容的类型可以不一致

解构数组:

array的函数

  • push , pop

  • unshift() 从头部增加 shift() 从头部开始删除第一个元素,并返回

  • splice(start , deleteCount) 删除指定位置 第二个参数可选,并且代表要删除几个元素

  • sort

  • indexOf() 从头开始查找 lastIndexOf () 从尾开始查找,两个参数分别是要查找的内容和开始查找的位置

  • forEach() 为每一个元素执行一个函数

  • map() 返回一个由指定函数调用后得到的新数组

    let array = [1 , 2, 3, 4, 5];
      let b = array.map(e=>{
          return e *2 ;
       })//使用map建立一个新数组
       for (a of b){
        console.log(a);
       }
    
       let c = array.filter(x => x < 5)//filter用于过滤数组的某些数据
       for (a of c){
        console.log(a);
       }
       console.log(array[1]);//打印出了map[a[]] = b[]
  • every 每个元素都当作参数,所有都满足才返回true

  • some 部分满足即可true

  • slice 截取一部分

  • joint(‘,’) 将数组转化为字符串并返回

  • 展开运算符 数组[…] 即可展开

Map

  • set(“key” ,”value”)
  • for (let [key , value] of map)
  • for (let key of map) for (let value of map)
  • forEach(函数)
  • 合并 自己看文档喽

Set

  • add
  • 并集,交集,差集 自己看

BOM对象

  1. window对象,用于操作浏览器的窗口

    常用的有 第三个参数与新窗口有关,自己去看

    - moveBy(x , y) 将窗口向又或者向下移动
    - moveTo(x ,y) 移动到指定位置
    - resizeBy (dw , dh) 缩放
    - resizeTo (w ,h )  缩放到指定窗口大小
    - alert() 输出
    - prompt() 读入用户输入
    - confirm() 显示确认和取消 确定 返回值是true
    - setinterval() 按照指定周期来调用函数和表达式 参数未函数 + 毫秒数
    - setTimeout() 在指定毫秒数后调用函数计算表达式
    
  2. location 对象,用于分析和设置页面的URL,主要是window 和 document 对象的属性
    直接使用location . xxx 即可

    有以下属性

    - hash 如果URL包含书签#,则返回#后边的内容
    - host 服务器的名称
    - <u>**herf**</u> 当前载入的完整的URL 
    - pathname URL 中主机后面的部分
    - port 请求端口
    - protocol 协议
    - search 执行GET请求的URL中 ? 之后的部分
    

    补充:URL

    URL 代表着是统一资源定位符(Uniform Resource Locator)。URL 无非就是一个给定的独特资源在 Web 上的地址。理论上说,每个有效的 URL 都指向一个唯一的资源。这个资源可以是一个 HTML 页面,一个 CSS 文档,一幅图像,等等。而在实际中,也有一些例外,最常见的情况就是一个 URL 指向了不存在的或是被移动过的资源。由于通过 URL 呈现的资源和 URL 本身由 Web 服务器处理,因此 web 服务器的拥有者需要认真地维护资源以及与它关联的 URL。

  3. navigator 对象, 进行客户端检测

    - appCodeName 浏览器代码名的字符串表示
    - appName 官方浏览器名的字符串表示
    - appVersion 浏览器版本信息表示
    - javaEnabled() 是否启用了Java
    - platform 运行浏览器的计算机平台字符串表示
    - plugins 安装在浏览器中的插件组数
    - userAgent 用户代理头字符串的字符串表示 最常用
    
  4. screen 对象 也是 window对象的属性

    - availHeight 窗口可以使用的屏幕高度
    - availWidth
    - colorDepth 用户表示颜色位数
    - height 屏幕高度
    - width 
    
  5. history 导航历史记录

    - go( ) 前进后退几页,也可以使用字符串,导航到包哦含这个字符串的第一个也米娜
    - back()
    - forward()
    - length 历史记录中有多个条目,可以来判断当前是不是你的第一个页面
    

    DOM

    将标记语言的各个组成部分封装为对应的对象

    Document 整个文档对象
    Element 元素对象
    Text 文本对象
    Comment 注释对象

    js可以通过DOM对HTML进行操作和做出反应 ^6aefb6

组成

  1. [[Web学习#^6aefb6|Core DOM 所有文档类型的标准模型]]
  2. XML DOM XML 文档的标准模型
  3. HTML DOM HTML 文档的标准模型

常用函数

具体的标签对象的属性和函数去MDN里看看

//通过id值查找对象
var h1 = doucument.getElementById1('h1')
//以下函数无法查找特定的某一个对象,所以返回的一个对象数组
//根据标签名查找对象
var divs = document.getElementsByTagName('div')
//根据name来获取
getElementsByName('hobby')
//根据class 来获取
getElementsByClassName('cls')

事件绑定

  1. 通过HTML标签中的属性来绑定
    <input type = "button" onclick = "on()" value1 = "按钮1" >
    
    <script>
    	function on () {
    		alert ('我被点击了');	
    	}
    </script>
  2. 通过DOM元素来绑定
    <input type = "button" id = "btn" value = "按钮2" >
    <script>
    	document.getElementById('btn').onclick = function(){
    		alert('我被点击了');	
    	}
    	

React

JavaScript的一个第三方库

react app 由组件组成,react组件是返回html标签的JavaScript函数

react 的组件可以用作类似于html的标签,区别是React 组件必须以大写字母开头,HTML标签必须以小写字母开头

JavaScript的component实际上就是JavaBean

例如:

function MyButton() {
  return (
    <button>I'm a button</button>
  );
}

之后MyButton可作为标签使用

export default function MyApp() {
  return (
    <div>
      <h1>Welcome to my app</h1>
      <MyButton />
    </div>
  );
}

hotloader是好东西

React命名:

HTML中的类为 class ,而React中的class命名为className

<h4 className="Profile-subTitle">About Me</h4>

类名为:组件名(文件名) 扩折号 文件中的作用名,遵循驼峰

使用 import xxx from “xxx” 来在js文件中导入其他的component

使用 import “xxx.css” 来从导入css文件

使用 export default xxx 来导出这个组件,从而让其他文件能够调用这个组件

具体可看示例 catbook-react

JSON

使用js对象标记法书写的文本

格式:

//这个变量是一个字符串类型的,不是对象
var 变量 = '{"key1" : value1 , "key2" : value2}';//单引号来表名时变量类型
//JSON 字符串和js对象转化
var jsObject = JSON.parse(userStr)
var jsonStr = JSON.stringify(jsObject)

后端

HTTP

组成:URL + Methods + Request Headers + Request Body

URL 文件定位,网址 (定位)
URI主要用于标识网络资源名称(比如网址)标识唯一的资源 (身份证)

网络通信三要素

  • ip:计算机在网络中的唯一标识
  • 端口: 应用程序在计算机中的唯一表示
  • 传输协议:
    • tcp 安全协议,三次握手,四次挥手 教程
    • udp 不安全协议

HTTP协议

  • 请求行大写请求方式,URL,请求协议,版本号
  • 请求头存放请求参数和值,键值对的方式, 参数名:参数值
  • 请求空行一行来区分请求头和请求体
  • get方式没有请求体

Methods

  • GET 方法请求一个指定资源的表示形式,使用 GET 的请求应该只被用于获取数据。
  • HEAD 方法请求一个与 GET 请求的响应相同的响应,但没有响应体
  • POST 方法用于将实体提交到指定的资源,通常导致在服务器上的状态变化或副作用。
  • PUT 方法用有效载荷请求替换目标资源的所有当前表示。
  • DELETE 方法删除指定的资源。
  • CONNECT 方法建立一个到由目标资源标识的服务器的隧道。
  • OPTIONS 方法用于描述目标资源的通信选项。
  • TRACE 方法沿着到目标资源的路径执行一个消息环回测试。
  • PATCH 方法用于对资源应用部分修改。

响应部分

  • 响应行 : 协议,版本号,状态码,状态描述信息
  • 响应头 : 参数和值, 参数名 : 参数值
  • 响应体 : html 格式
    状态码:

1xx:信息

2xx:succeeded

3xx:redirect 重定向

4xx: 你请求出错

5xx:服务器出错

API

应用程序接口(英语:application programming interface[1]),缩写为API[2],是一种计算接口,它定义多个软件中介之间的交互,以及可以进行的调用(call)或请求(request)的种类,如何进行调用或发出请求,应使用的数据格式,应遵循的惯例等。它还可以提供扩展机制,以便用户可以通过各种方式对现有功能进行不同程度的扩展[3]。一个API可以是完全定制的,针对某个组件的,也可以是基于行业标准设计的以确保互操作性。通过信息隐藏,API实现了模块化编程,从而允许用户实现独立地使用接口。

目的:

@RestController //加上这个之后变为请求处理类  
public class HelloController {  
@RequestMapping("/hello")  
public String hello(){  
return "hello world!";  
}

一些参数的实现

形式良好的 XML 文档

“形式良好”的 XML 文档拥有正确的语法。

在前面的章节描述的语法规则:

  • XML 文档必须有一个根元素
  • XML元素都必须有一个关闭标签
  • XML 标签对大小写敏感
  • XML 元素必须被正确的嵌套
  • XML 属性值必须加引号
    在 XML 中,有 5 个预定义的实体引用:
< < less than
> > greater than
& & ampersand
&apos; apostrophe
" quotation mark

Servlet

是接口,使用时要自己实现

web.xml中配置(可用注解实现了,暴怒需要这么写了)

	<!-- Servlets -->
	<!-- 配置类名和路径-->  
<servlet>  
<servlet-name>demo1</servlet-name>  
<servlet-class>com.example.servelet_study.servlet.ServletDemo1</servlet-class>  
</servlet>  
<!-- 配置请求路径 -->
<servlet-mapping>  
<servlet-name>demo1</servlet-name>  
<url-pattern>/demo1</url-pattern>  
</servlet-mapping>
<!-- 配置servlet的init时间 -->
<load-on-startup> 数字 </load-on-startup> 数字为正数时可以在服务器启动时被创建,为 负数时可以在第一次访问的时候进行加载

注解

@WebServlet( "/...","/...","/...")

接口方法

init() 在第一次创建Servlet时被调用
service() 在被访问时会被调用,会适当的调用doGet等方法
destroy()
getSercletConfig() 获取配置文件
getServletInfo () 获取servlet版本信息
## 分层解耦
### 三层架构
1. controller : 控制层 ,接受前端发送的请求,对请求进行处理,并响应数据
2. service: 业务逻辑层,处理具体的业务逻辑
3. dao 数据访问层(Data Access Object ) (持久层),负责数据的访问操作,包括增删改查
### IOC & DI
```java
@Component 将当前类交给IOC 容器 //加在需要被new的对象所在的层
@Autowired 运行时IOC 容器会提供该类型的bean并赋值给变量 依赖注入 //加在controller层

加上之后就不需要自己进行new对象了,直接建立对象变量即可
//其他注解
@Component 声明bean的基础注解
//以下在web中常用
@Controller 标注在控制器上
@Service 标注在业务类上
//DI注解
@Autowired 默认是按照类型进行的,如果存在多个相同的类型的bean就会出现报错
@Primary 想要哪个bean被注入就直接加在那个类前面
@Qualifier("value") 在使用的时候加上这个,value为bean的名字
@Resource (name = "") 

HttpServletRequest中常用的方法

  • Map getParameterMap()
    获取包含所有请求参数及值的 Map 对象。需要注意,该 Map 的 value 为 String[],即一个参数所对应的值为一个数组。说明一个参数可以对应多个值。
  • Enumeration getParameterNames()
    获取请求参数 Map 的所有 key,即获取所有请求参数名。
  • String[] getParameterValues(String name)
    根据指定的请求参数名称,获取其对应的所有值。这个方法一般用于获取复选框(checkbox)数据。
  • String getParameter(String name)
    根据指定的请求参数名称,获取其对应的值。若该参数名称对应的是多个值,则该方法获取到的是第一个值。这个方法是最常用的方法。

获取客户端信息的方法:

  • getRequestURL方法返回客户端发出请求时的完整URL。
  • getRequestURI方法返回请求行中的资源名部分。
  • getQueryString 方法返回请求行中的参数部分。
  • getRemoteAddr方法返回发出请求的客户机的IP地址
  • getRemoteHost方法返回发出请求的客户机的完整主机名
  • getRemotePort方法返回客户机所使用的网络端口号
  • getLocalAddr方法返回WEB服务器的IP地址。
  • getLocalName方法返回WEB服务器的主机名
  • getMethod得到客户机请求方式
    request.setAttribute("test","hello"); 暂存req请求中的信息
    
    equest.getRequestDispacther("/test.jsp").forword(request,response); 转发
    
    
    response.sendRedirect("test.jsp"); 重定向

filter

由于表单的post 提交方式,所以要编写好编码的过滤器来支持中文存取

Vue

免除js中的 DOM操作简化书写

  1. 新建html页面引入Vue.js文件
  2. 创建Vue核心对象,定义数据模型
    <script>
    new Vue ({
    	el : "#app", //Vue要控制哪个区, #+id 类似css的选择器
    	data : {//数据模型
    		message : "hello Vue"	
    	}
    	method :{
    		//定义函数
    	}
    })
    </script>
  3. 编写视图
    <div id = "app" >
    	<input type = "text" v-model = "message"> //绑定数据模型
    	{{ message }} //直接获取数据模型中的数据进行展示
    </div>
    //
    {{}} 插值表达式,中间可以填入 变量,三元运算符,函数调用,算数运算
    v-model = "" 模型绑定

常用指令

v-bind 为HTML 标签绑定属性值,例如设置href , css 样式
或者直接 : + href ...
v-model 在表单元素上实现双向数据绑定
v-on 为HTML绑定事务 v-on:click 可简写为@click
//

//
v-if 
v-else-if 条件性的渲染某元素
v-else
v-show 根据条件显示某元素,区别是切换的是display属性的值
v-for 列表渲染,遍历容器的元素或对象的属性

生命周期及其对应的方法

Ajax : 异步的js和xml

Axios

简化了Axios
npm install axios
在入口文件引入

import axios from "axios";  
//把axios全局导出  
app.config.globalProperties.$axios = axios;
  • 发送多个请求
    //定义两个异步函数
    function getUserAccount() {
    	return axios.get('/user/12345'); 
    } 
    function getUserPermissions() { 
    return axios.get('/user/12345/permissions'); 
    }
    //等待两个都执行完毕再返回结果
    Promise.all([getUserAccount(), getUserPermissions()]) .then(function ([acct, perm]) { // ... });
  • 使用时只需要在axios中传入config即可
  • 创建一个自定义的实例
    const instance = axios.create({
    	baseURL: 'https://some-domain.com/api/', 
    	timeout: 1000, 
    	headers: {'X-Custom-Header': 'foobar'} 
    });
  • config配置详解
  • 拦截器
    // 添加请求拦截器
    axios.interceptors.request.use(function (config) {
        // 在发送请求之前做些什么
        return config;
      }, function (error) {
        // 对请求错误做些什么
        return Promise.reject(error);
      });
    
    // 添加响应拦截器
    axios.interceptors.response.use(function (response) {
        // 2xx 范围内的状态码都会触发该函数。
        // 对响应数据做点什么
        return response;
      }, function (error) {
        // 超出 2xx 范围的状态码都会触发该函数。
        // 对响应错误做点什么
        return Promise.reject(error);
      });
  • 错误处理

Nginx

Nginx (engine x) 是一款轻量级的 Web 服务器 、反向代理服务器及电子邮件(IMAP/POP3)代理服务器。
什么是反向代理?

反向代理(Reverse Proxy)方式是指以代理服务器来接受 internet 上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给 internet 上请求连接的客户端,此时代理服务器对外就表现为一个反向代理服务器。

nginx -s stop       快速关闭Nginx,可能不保存相关信息,并迅速终止web服务。
nginx -s quit       平稳关闭Nginx,保存相关信息,有安排的结束web服务。
nginx -s reload     因改变了Nginx相关配置,需要重新加载配置而重载。
nginx -s reopen     重新打开日志文件。
nginx -c filename   为 Nginx 指定一个配置文件,来代替缺省的。
nginx -t            不运行,仅仅测试配置文件。nginx 将检查配置文件的语法的正确性,并尝试打开配置文件中所引用到的文件。
nginx -v            显示 nginx 的版本。
nginx -V            显示 nginx 的版本,编译器版本和配置参数。

使用

启动nginx服务,启动时会一闪而过是正常的

start nginx

查看任务进程是否存在,dos或打开任务管理器都行

tasklist /fi “imagename eq nginx.exe”

Vue

Element

maven

用于项目管理构建和依赖管理,将依赖写入到maven配置文件中即可自动下载到项目中

  1. https://m.imooc.com/wiki/mavenlesson-maveninstall
  2. 构建项目
    mvn archetype:generate -DgroupId=com.mic.tech -DartifactId=firstProject -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
    
  • -DgourpId: 组织名,一般为公司网址的反写;
  • -DartifactId: 项目名-模块名;
  • -DarchetypeArtifactId: 用来指定 ArchetypeId,这里用到的是maven-archetype-quickstart,即创建一个简单 Java 应用;
  • -DinteractiveMode: 是否使用交互模式。
  1. idea已经内置maven

添加外部依赖

将外部文件导入到项目中,配置pom.xml
<dependencies>
    <!-- 在这里添加你的依赖 -->
    <dependency>
        <groupId>ldapjdk</groupId>  <!-- 库名称,也可以自定义 -->
        <artifactId>ldapjdk</artifactId>    <!--库名称,也可以自定义-->
        <version>1.0</version> <!--版本号-->
        <scope>system</scope> <!--作用域-->
        <systemPath>${basedir}\src\lib\ldapjdk.jar</systemPath> <!--项目根目录下
        的lib文件夹下-->
    </dependency> 
</dependencies>

JSP

cookie存储在客户端,session 存储在服务器端

基本语法

<% 代码 %>
<%! int i = 0 ; %> 变量声明
<%= 表达式 %>  表达式声明
<%-- 注释 --%> 注释,注释内容 不会被发送到浏览器
<!-- html注释 --> 可以通过查看源代码来进行查看
if --else 语法
<% if (day == 1 || day == 7) { %>
      <p>今天是周末</p>
<% } else { %>
      <p>今天不是周末</p>
<% } %>
switch 
<% 
switch(day) {
case 0:
   out.println("星期天");
   break;
case 1:
   out.println("星期一");
   break;
case 2:
   out.println("星期二");
   break;
case 3:
   out.println("星期三");
   break;
case 4:
   out.println("星期四");
   break;
case 5:
   out.println("星期五");
   break;
default:
   out.println("星期六");
}
%>
for循环
<%for ( fontSize = 1; fontSize <= 3; fontSize++){ %>
   <font color="green" size="<%= fontSize %>">
    菜鸟教程
   </font><br />
<%}%>
while循环
<%while ( fontSize <= 3){ %>
   <font color="green" size="<%= fontSize %>">
    菜鸟教程
   </font><br />
<%fontSize++;%>
<%}%>

JSP行为

使用xml语法来控制servlet引擎,能够动态插入一个文件重用Javabean组件,切换页面

<jsp :action_name attribute attribute = "value"/>
action_name :
include 在当前页面包含静态
useBean 寻找和初始化一个java组件
setProperty 设置JavaBean组件的值
getProperty 将JavaBean 组件的值插入到output中
forward 从一个jsp文件向另一个文件传递一个包含用户请求的request 对象
plugin 再生成的html页面中包含Applet和JavaBean对象
element 动态创建一个xml元素
attribute 定义动态创建的xml元素的属性
body 动态创建xml元素主体
text 用于封装数据

JSP 指令

<%@ 指令 %>  
主要指令有: 
page  定义网页依赖属性 脚本语言,error页面,缓存需求等
	page相关的属性
	buffer 指定out对象使用缓冲区大小
	autoFlush 控制out 对象的缓存区
	conteneType 指定当前JSP 页面发生异常时需要转向的错误处理页面
	error Page 指定当JSP页面发生异常时需要转向的错误处理页面
	isErrorPage 指定当前页面是否可以作为另一个JSP页面的错误处理页面
	extends servlet 从哪个类继承
	import 导入要使用的java 类
	info 定义jsp页面的表述信息
	isThreadSafe 指定对JSP页面的访问是否为线程安全
	language 定义JSP页面所用的脚本语言,默认是Java
	session 指定JSP页面是否使用session
	isELIgnored 指定是否执行EL表达式
	isScriptingEnabled 确定脚本元素能否被使用
	
include 包含其他文件
	<%@ include file = "文件的相对url" %>
taglib  引入标签库的定义,可以自定义标签
	<% tagelib uri = "uri" prefix = "refix0Tag" %>
	uri 标签库的位置   prefix 指定标签库的前缀

JSP动作元素

JSP动作元素在请求阶段处理阶段起作用,且使用的时xml语法

语法:
<jsp: action_name attribute = "" />
include 在页面被请求的时候引入一个文件
	page = "url" flush = "true or false" 定义在包含资源前是否刷新缓存区
useBean 寻找或者实例化一个JavaBean
	<jsp:useBean id="name" class="package.class" />
	class指定Bean的完整包名。
	type 指定将引用该对象变量的类型。
	beanName 通过 java.beans.Beans 的 instantiate() 方法指定Bean的名字。
setProperty 设置JavaBean的属性
	将其放在useBean中只会在找到一个Bean示例才会进行执行
	放在外部则不论是否找到bean都会执行
	属性: name 表示要设置属性的是哪个bean
	property 无论要设置哪个属性都有一个特殊用法
	value 指定bean属性的值
	param 指定用哪个参数作为Bean属性的值
getProperty 输出某个JavaBean的属性
	提取对应的Bean属性的值,转化为字符串然后输出
	name 药检所的bean属性名称
	property 表示要提取属性的值
forward 把请求转到一个新的界面
plugin 根据浏览器类型为Java插件生成obeject 活embed 标记
element 定义动态xml元素
attribute 设置动态定义的xml属性
body 动态定义的xml内容
text 在jsp页面和文档中使用写入文本的模板

JSP隐式对象,可以直接使用而不显示声明

  1. request 对象 ^8a3722
  2. response对象
  3. out
  4. session 用来追踪在各个客户端请求间的会话
  5. application 代表这这个JSP页面
  6. config
  7. pageContext 代表整个jsp页面,存储了request和response的引用,application 对象,config , session . out可以访问这个对象来导出
  8. page == this
  9. exception

request(同servlet中的request)

1 Cookie[] getCookies()

返回客户端所有的Cookie的数组
2 Enumeration getAttributeNames()

返回request对象的所有属性名称的集合
3 Enumeration getHeaderNames()

返回所有HTTP头的名称集合
4 Enumeration getParameterNames()

返回请求中所有参数的集合
5 HttpSession getSession()

返回request对应的session对象,如果没有,则创建一个
6 HttpSession getSession(boolean create)

返回request对应的session对象,如果没有并且参数create为true,则返回一个新的session对象
7 Locale getLocale()

返回当前页的Locale对象,可以在response中设置
8 Object getAttribute(String name)

返回名称为name的属性值,如果不存在则返回null。
9 ServletInputStream getInputStream()

返回请求的输入流
10 String getAuthType()

返回认证方案的名称,用来保护servlet,比如 “BASIC” 或者 “SSL” 或 null 如果 JSP没设置保护措施
11 String getCharacterEncoding()

返回request的字符编码集名称
12 String getContentType()

返回request主体的MIME类型,若未知则返回null
13 String getContextPath()

返回request URI中指明的上下文路径
14 String getHeader(String name)

返回name指定的信息头
15 String getMethod()

返回此request中的HTTP方法,比如 GET,,POST,或PUT
16 String getParameter(String name)

返回此request中name指定的参数,若不存在则返回null
17 String getPathInfo()

返回任何额外的与此request URL相关的路径
18 String getProtocol()

返回此request所使用的协议名和版本
19 String getQueryString()

返回此 request URL包含的查询字符串
20 String getRemoteAddr()

返回客户端的IP地址
21 String getRemoteHost()

返回客户端的完整名称
22 String getRemoteUser()

返回客户端通过登录认证的用户,若用户未认证则返回null
23 String getRequestURI()

返回request的URI
24 String getRequestedSessionId()

返回request指定的session ID
25 String getServletPath()

返回所请求的servlet路径
26 String[] getParameterValues(String name)

返回指定名称的参数的所有值,若不存在则返回null
27 boolean isSecure()

返回request是否使用了加密通道,比如HTTPS
28 int getContentLength()

返回request主体所包含的字节数,若未知的返回-1
29 int getIntHeader(String name)

返回指定名称的request信息头的值
30 int getServerPort()

返回服务器端口号

Token是一种令牌,用来识别访问人员的