@TOC

一 一些概念

mysql的三层架构:
  第一层是管理客户端连接的。
    对于每个客户端连接,都会从管理的线程池中取出一个为其服务
  第二层是语句查询、语句解析、语句优化,一些select的缓存、还有内置函数、存储过程视图之类的管理。
    查询语句的优化则是再内部的解析器对语句重构以提升执行效率。
  第三层是存储引擎,主要负责数据的存取,建表时可以选择不同的引擎,不同的引擎各有优劣。
    比如MyISAM不支持事务却使用表锁,innoDB使用行锁却支持事务。

并发控制
  关于锁的上锁方式,分为两种:
    一种是在第一层实现的显示上锁,但是其不在sql的标准范围。
    另一种是在三层层引擎上实现的隐式上锁,详见本章事务部分。

  锁的粒度主要是表锁(MyISAM引擎)和行锁(innoDB引擎)两种。
    alter table之类的语句不论引擎,总是表锁。

事务
  事务的原子、一致、隔离、持久。

  关于隔离性的四个级别:
    未提交读:只要执行了一条语句,不需要commit,这行语句的改变就可以被其他地方看到。
    提交读(不可重复读):commit之前的所有修改对于其他地方都是看不到的
    可重复读(mysql默认):保证了同一个事务读取一份数据是一致的。
      事务A:select * from tableA
      事务B:insert into tableA… 然后commit
      事务A:select * from tableA
      这样的情况下事务A读到的数据是不一样的,该隔离级别解决了这样(幻读)的问题。
    串行:不如说是并发加上,每一行的的数据都会加锁执行.
    innodb支持这四个隔离级别。

  死锁的解决则是内部的死锁检测或者锁超时放弃,innoDB总是将持有锁最少的事务回滚,有的引擎则是放弃锁。

  如果没有显示声明是一个事务,那么每条语句都视为一个事务,查询即提交,这是innodb的默认行为。
    默认是SET AUTOCOMMIT=1,该值对于不支持事务的myisam或者memory是无影响的。
    当这个值是0是时,会直到commit或rollback才提交。
  alter table会强制commit当前正在执行的事务?还不清楚。
  如果一个事务混用事务表(innodb)和非事务表(myisam):
    成功执行不会有问题
    失败回滚,而非事务型表是无法回滚的,在非事务表上执行事务,也不会有警告。

  事务执行时,执行到哪锁上到哪,commit或者rollback时统一释放。

版本并发控制(另一种形式的锁)
  版本号:
    innodb中,该锁的实现通过每行记录的两个隐藏列实现,分别是创建时间和过期时间,但是存的数据并不是时间,而是一个叫版本号的东西,称作时间更容易理解。
    在数据库中,有一个叫系统版本号的东西,每开始一个事务,都会导致该系统版本号自增,隐藏列就存这个东西。

  一个事务开始时,首先记下系统版本号,作为自己的事务版本号
    select:
      ①只查找《创建时间版本号》小于等于《当前事务版本》的
      ②对于《删除时间版本号》,需要空,或者大于《当前事务版本》
    insert:新行将《创建时间版本》设为《当前事务版本》
    delete:不会真的删除,只是《删除时间版本》设为《当前事务版本》
    update:
      插入新行,设置为《创建时间版本》设为《当前事务版本》
      修改旧行,《删除时间版本》设为《当前事务版本》
      insert和delete的结合
  以上操作通过数据的冗余和版本号的判断,使用空间换时间的方式,实现了非阻塞操作

一些引擎的概述:
  尽量不混用多种引擎
  innoDB:每个database或者schema都会是一个子目录,该目录下存储着建表的文件代码,文件名为:表名.frm。
  使用版本并发控制来支持高并发,大量较短的事务执行、在线热备份、崩溃恢复
  MyISAM:该引擎将表分为数据文件(.MYD)和索引文件(.MYI),日志、插入快,表锁、延迟写入文件
  Archive:只支持select和insert。主要是对告诉的插入做了优化,日志
  CSV:使用csv文件作,可以外部或者数据库直接写入和读取数据。
  Memory:放在内存中,不进行IO,表锁,并发慢,hash表、查询快,不支持text类型,会被转为固定长度的char

转换表引擎:
  ①直接alter table,但是太慢
  ②导出表的文件,然后再导入新引擎的表,但是要主要导出或默认删原来的表
  ③建新表,insert into xx select …,加条件一部分一部分insert数据

                                      2022年6月2日19:13:39

三 服务器性能剖析()

  这章主要讲了一些分析服务器性能问题的东西,没好好看,以后还得来一遍,先写几个实用的东西

SHOW PROFILE
  主要功能是记录执行了那些sql语句,然后可以在记录中查看某条sql语句读写磁盘、检查操作等等每个细节花费了多少时间。
  set profiling = 1;      设置为1后,开始记录sql语句
  show profiles;        显示所有记录的sql语句,每条记录包含《id,整条语句的执行时间,语句本身》
  show profile for query 1;   显示id为1那条sql语句的详细数据,包括启动、打开表、上锁、权限检查、优化、创建临时表、执行、排序等等各个细致操作所花费的时间

  需要注意的是,其默认行为是按执行顺序的,比如启动、打开,可以通过:
    select state, sum(duration) as a from 库名.PROFILING where QUERY_ID=1 group by state order by a
    来实现按操作的花费时间来显示,state列就是具体某个操作如打开、启动,但是这一列可能会有相同的,比如检查权限可能是多张表,然后用group来分组该列

SHOW STATUS
  主要功能是返回一些计数器,有全局范围的,也有会话的。
  FLUSH STATUS; 会将会话级别的计数器清理
  执行某些sql语句
  show status where Variable_name LIKE 'Handler%' or Variable_name LIKE 'Created%'
    该行主要是看临时表临时文件还有一些句柄之类的东西,其各种变量意义随便搜了一个别人的链接,记下来备用吧

SHOW GLOBAL STATUS
  主要是查看连接线程数、正在查询的线程数等等之类的信息。
  提供的方案是每秒执行一次该命令来显示信息,不需要进入mysql,直接在命令行mysqladmin ext -i1,i后面就是执行频率,然后可以用某些命令解析
  以下命令,可以监控每秒的查询数、连接数、正在查询的线程数,以此推断出是哪里导致了服务器的停顿

mysqladmin extended-status -ri3mysqladmin ext -i1 | awk '/Queries/{q=$4-qp;qp=$4}/Threads_connected/{tc=$4}/Threads_runing/{printf "%5d %5d %5d\n",q, tc, $4}'mysqladmin ext -i1 | awk '/Queries/{q=$4-qp;qp=$4}/Threads_connected/{tc=$4}/Threads_runing/{printf "%5d %5d %5d\n",q, tc, $4}'

四 数据类型的选择

通用原则
  尽量选择更小的类型

  对于IP地址,使用INET_ATON()INET_NTOA()

  避免NULL,使用NOT NULL。
    NULL变为NOT NULL并不会有显著性能提升,但是在列上建索引的话,就会有。
    NULL值会导致索引统计变得复杂、更多的存储空间以及特殊处理,当可为NULL的列设为索引是时,更需要额外的字节,在MyISAM中还可能导致《原来固定长度的索引》变为《可变长度的索引》。

  避免太多的列,执行语句时,《服务器层》通过行缓存从《引擎层》拷贝数据,然后在服务器层将这些经过编码的数据转换成对应类型,而需要转换的列越多,相应的代价烨越大。

  查询语句关联的数量,mysql将关联操作设为最多61张表,然而建议是单个查询不要超过12个表关联。

  范式还是反范式:
    范式即多表,没有冗余的数据,但取数据时需要多表联查
    反范式即不分表,一张表里有冗余数据,但是不需要联查
    又或者混用,多表存储某列相同的数据,但更新时需要更新多个表
    需要频繁统计之类的,设置专门的计数表、列等,并在插入删除时维护,以避免count函数带来的开销

  一些加快ALTER TABLE操作的方式,但是不被mysql支持:
    大部分ALTER TABLE会导致表的重建,即建新表、复制数据、删酒标,但烨不是所有。
    例如MODIFY 列名会导致表的重建,但是ALTER TABLE下的ALTER COLUMN不会。
    1.替换修改表的.frm文件(InnoDB)
      建一张结构相同的新表
      执行:FLUSH TABLES WITH READ LOCK,关闭所有正在使用的表,并禁止打开新表。
      新表的.frm文件和旧表的.frm文件互相改名替换
      UNLOCK TABLES解锁
    2,MyISAM载入数据
      先关闭索引,然后导入索引,再重新建立索引

标识列的选择
  最好使用整型,避免字符串。
  避免使用随机数做标识列,如MD5、SHA1等,随机的数值导致不同的行分布在很大的空间内,导致select、insert等变慢等。

整型
  TINYINT、SMALLINT、MEDIUMINT、INT、BIGINT是,分别使用8、16、24、32、64字节存储。
  可选的UNSIGNEDSHUXING
  整数计算都会转成BIGINT操作,32位环境也是,一些聚合函数则转成实数计算。
  使用如INT(11)并不会真的导致只有11位空间,还是按int算,但是在显示的时候只显示11位的值。

实数
  FLOATDOUBLE是标准的浮点近似运算。
  DECIMAL则是不会丢失的高精度计算,如存储钱,由mysql服务器实现,相比于标准浮点更慢。
    DECLMAL(18, 9)表示总共18个数字,小数部分有9位精度。
    MYSQL将该类型打包大一个二进制字符串,每4个字节存9个数字,也就是说DECLMAL(18, 9)占4+4+1字节,1字节是小数点。
    百度查的是18+2个字节,可能是因为书中说明MYSQL版本5.0以上才将其打包位二进制字符串的原因。

  最好的方式是使用BIGINT来存DECIMAL的数据,使用的时候根据小数位数来算出实际数据,既避免标准浮点的精度丢失,又避免DECIMAL的运算慢。

字符串
  首先,字符串VARCHARCHAR在不同的引擎中表现是不一样的。

  VARCHAR:变长字符串。
    只使用需要的空间,例外是建表时使用ROW_FORMAT=FIXED,则全是定长。
    另外需要额外的空间存字符串的长度,长度字段需要的空间根据实际存储的空间变化。如10字节需要1字节的长度,150字节则需要2字节的长度空间
    缺点:当update语句导致字符串需要的空间的增长时,额外的空间处理,在MyISAM引擎将其拆成多段处理,InnoDB则是分裂页,使该行可以放入,InnoDB还可以将其转为BLOB二进制处理。
    使用场景:该列更新少、使用了复杂字符集(URF-8)、定义的最大长度比平均长度大很多。
  CHAR:定长字符串。
  BINARYVARBINARY:和前面两个的区别是这两个存二进制,而不是字符,比较时更快、大小写敏感。

  在MYSQL5.0以前,字符串末尾的空格总是会被删除,而之后,CHAR的末尾空格会被抹掉,而VARCHAR不会被抹去,memory表对于VARCHAR总是以最大空间分配。

BLOB和TEXT
  占用空间大的数据专用。当空间过大,InnoDB使用指针指向外部的存储区域。
  BLOB存储二进制数据,TEXT存超长文本数据。
  对于这两个类型的排序,只比较前MAX_SORT_LENGTH个字节,而不是全部,或者用ORDER BY SUBSTRING(column,length)
  MEMORY引擎不支持BLOB和TEXT类型,如果临时表有这样的列,则会导致使用MyISAM存储临时数据。或者使用ORDER BY SUBSTRING就可以使用内存表了,但是要确保子串足够短。

枚举
  列名 ENUMM(‘a’, ‘b’, ‘c’)
  枚举字段存储的是整数而不是字符串,排序也是按整数而不是字符串
  缺点是增加枚举的新字段需要使用ALTER TABLE,对大表很麻烦。
  后略,更多时候会使用INT代替枚举吧。

时间
  DATETIME:范围是1001年到9999年,精度秒,数据封装到YYYYMMDDHHMMSS的整数格式。但显示时是标准显示。
  TIMESTAMP:UNIX时间戳,需要注意时差。

位数据
  BIT
    可以用BIT(N)来明确位数的长度,最多64
    MyISAM引擎将不同行的bit打包在一起节省空间。
    InnoDB则使用int存每个bit。
    BIT在MYSQL被视为《01的二进制字符串》,而不是《数字》,select时,将该《二进制》转为《字符串》显示,也就是说如果存了一个“00111001”,其对应的ASCII码是9,直接select该列显示9,转成数字显示5。
  SET
    同样是位运算,提供一组函数,但改变代价是ALTER TABLE。

                                      2022年6月13日18:52:54

五 索引

5.1索引的类型
  主要是B树索引和哈希索引两种
  重要的事:《一个索引包含多个列》和《多个索引都只包含一个列》的优化是完全不一样的,前者可以在说明B树索引的部分看到

B树索引
这里主要写写B树索引的限制吧。
适用于全键值、键值范围、左前缀查找。
假如说有一个表建立索引: key(列1, 列2, 列3)
  如果使用直接使用列2、列3作为where条件,如 select * from XXX where 列2=xxx
  这样的情况下,索引优化的查询速度是用不上的,因为一个索引包含多个列,在B树索引的实现是对三个字段一起排序的,优先左边
  也就是说,限定了列1的条件以后,才能在B树中缩小到某个上下限的范围,跳过列1只限定列2,那么就是整个树范围内挨个遍历每个小区间,然后再去找列2的条件,那么根据列1建立的索引(排序)也就用不上了。
  类似的,如果 select * from XXX where 列1=xxx 列3=xxx,那么只有列1的排序能用上
哈希索引
目前只有memory引擎支持hash索引,同时也是唯一支持非唯一哈希索引的,见后。
使用hash索引的列作为where条件时:
  1.首先根据列值计算hash值
  2.根据hash值取找到指向某行的指针
  3.然后判断where那个具体条件,因为是非唯一哈希,所以必须判断,否则就可能出现hash冲突而导致拿到完全不一样的值。
哈希索引是按hash值而不是列名排序的,所以当order by碰上哈希索引,对排序速度是没有优化的。
也不支持部分索引, key(列1, 列2):这样建立的索引是,是由列1、列2两个作为参数计算hash值,如果where里只有一个条件,那么无法计算出hash值,这个索引带来的优化也就用不上了
只支持==、!=这些比较操作,不能> <之类的,因为hash值是随机的,范围也就没办法确定。
innoDB上自建hash索引
加一列值叫MYHASH,将另一列的值作为参数 CRC32(列1)生成一个32位的hash值,代价有两个,一是插入和更新是维护这个值,二是 selete * from XXX where 列1=xxx and MYHASH=CRC32(列1)来防止hash冲突

5.2索引的原理及数据的分布

一些概念
一张表的多颗B树:每当有一个索引被建立,不论这个索引是《单列》还是《多列》索引,就会有一棵B树被建立。
聚簇(主键)索引:聚簇索引就是主键,当一个表有主键列,那么他就是聚簇的,而聚簇二字的含义意味着《数据放在这个索引对应的B树里叶子节点上》,非聚簇即数据不在这个索引对应的B树里。
二级索引:只要一个表里有聚簇索引存在,那么其他所有的索引就全部都是二级索引。
使用聚簇索引的优劣:提高了IO密集型应用的性能、按主键插入速度快、更新代价高(需要移动更新的行)、数据插入删除时导致的也分裂问题(进而导致存储不连续),并发插入的话,竞争插入是热点。
使用聚簇索引的建议:InnoDB使用时,如果没有数据需要聚簇,那么建一个与数据无关的自增列,以此来保证数据按顺序插入,因为是顺序插入,插入的地方总是在B树的右下角,那么就会写一个新的节点,而不是导致原来的节点分裂合并。但是如果这个列不是自增而是随机,那么缓存命中、节点分裂合并等等一系列问题就会到来。

    数据增删后导致的各种空间碎片问题,可以使用OPTIMIZE TABLE或者导入再导出来解决,又或者不支持OPTIMIZE,可以用ALTER TABLE 表名 ENGINE=原来的引擎名即可。

    瞎写点东西,实习的时候,看到数据库里全是MyISAM引擎,每张表里都有一个与数据无关自增的索引,首先MyISAM不支持聚簇,因为数据总是按序写在磁盘,其次也仅仅是将其设为普通索引,为什么要这样做?后来写东西的时候倒也有用到,很多时候程序里处理过的写回数据库时,直接用这个ID作为条件,其他倒是没怎么用到。

数据的分布
MyISAM(聚簇索引):不支持聚簇,因为总是按插入的顺序依次存储在磁盘上,即便有主键,主键索引对应的B树叶节点上也是存其在磁盘上的物理地址。
InnoDB(聚簇索引):支持聚簇,某行数据存储再主键索引对应的那颗B树里。
MyISAM(二级索引):叶子节点上直接存其在磁盘上的物理地址
InnoDB(二级索引):与MyISAM,因为其数据在主键索引的那个B树里,所以叶子节点存的是《主键索引+索引列的值》,存索引列的值是为了在where语句使用这个列时用来判断,存主键则是为了在where条件找到后,根据主键值再去主键索引的B树立找对应的数据行。
文字描述好费力,来一张书上的图:

5.3建立索引的策略

选择性
某列选择性的计算公式: count(distinct 列名1) / count(*)
假如某列可选择性为0.5,那么意味着该列的每个数据都存在一个重复值,选择性越接近1,那么代表该列的值唯一性越高,那么使用该列索引作为查询条件时,就可以一次性剔除掉更多的《非目标值》,即筛选效率更高。
独立的列
当有索引 key(money)时,如果使用 select * from XXX where money*2=10,是无法使用索引带来的性能提升的,是一个全表扫描,所以应该尽可能避免表达式,而使用独立的列名作为where条件
字符串列作为索引,选择整个字串还是字串的前缀做优化,前缀的长度又如何确定
使用整个字符串作为索引会导致索引变得又大又慢
前缀的长度依赖于列的选择性,如果选择性接近1,那么就退化到了对整个字符串的索引,如果选择性过低即接近0,或者只选取字符串的第一个字符作为索引,那么又会使大量的无关数据未被剔除。
书中给出的选择性标准是: 0.03
接下来是前缀的长度如何确定,书中的例子是:count(distinct left(列名1, n)) / count(*),不断手动改变n的值,来确定一个结果是 0.03的长度

    还需要注意数据的特殊性,比如说选择了前5个字符,但是表中又有大量的数据前5个字符一样,那么索引的效率也会降低,对于特殊数据,当然也得有特殊的处理。

多个单列索引
在老的mysql版本中(5.0之前),where碰上多个单列索引,mysql只能使用其中一个去查找,而在新版本中,则会将这些条件分裂为多个子查询,然后将他们的结果交、并、差等。不过这种优化成本,完全可以在建立索引时考虑将其合并为《单个多列索引》来提升。
单个多列索引的顺序
key(列1, 列2)
到底哪列在前哪列在后,一般情况下是将 选择性高的列放在前面,这样在B树查找时可以优先筛掉更多的无关值。
如果不涉及排序或分组,那这自然是最好的选择。
但是如果要排序或分组,那么就要在选择性和排序分组之间权衡,或者干脆再为列单独建一个索引用于排序分组。
覆盖索引
假如有索引 key(列1)
语句 select 列1 from XXX,那么不需要再回表查,因为列1索引的B树,已经存储了列1的值,也就不需要再根据主键ID到聚簇的B树里去查了。
但是如果有 select * from XXX where 列3=x and 列4=x,可以改为 select * from XXX JOIN(select 列1 from XXX where 列3=x and 列4=x )AS YYY ON(XXX.列1=YYY.列1)
思路是子查询满足了覆盖索引的条件得到了一些优化。
但是慎用、慎用、慎用。因为可能出现原来的语句返回一个较小的数据集(已经筛掉了足够多的数据),反而会因为多出的子查询而效率下降。
索引排序
如果索引的顺序和order by中出现的列顺序一样,那么可以直接按照查询结果排序,而不需要再由mysql对结果排序,如果出现多表联查,order by中出现的字段需要都是第一张表的。
前面有提到 key(列1, 列2, 列3)这样的索引,如果where条件没有列1,直接 where 列2=xxx,索引是无法生效的,如果在 order by 列2又会如何,当然也是无法生效的。
但是有一个例外: where 列1=xxx order by 列2,是可以生效的,因为B树中先按列1排序,当指定了列1的值,那么范围也确定了,这个范围内按列2排序,所以索引是可以生效的。

    索引列的条件尽可能放左边,范围查询放右边。

索引和行锁
InnoDB只有在访问到具体某行,或者说主键那个B树里的某行数据时才会上锁,而对其他索引的访问是不会对行上锁的。
  1.语句首先由索引过滤一遍,查找需要哪些主键值
  2.然后根据主键值去访问行数据,这时候对行上锁
  3.然后才是where条件的匹配,过滤出真正需要的数据,而这个过程会有无关的数据行被锁,他们在整个事务结束后释放(InnoDB每条语句都被视作一个事务)
所以一个原则是,使用索引尽可能过滤掉多的数据,即便是在5.1版本之后,无关的数据在被检查过后就会被立即释放锁。

                                      2022年6月13日21:02:37

六 查询优化

一些概念

慢查询:即查询很慢的语句,超过long_query_time被视为是慢查询,默认值10秒。

查询的优化的一些概念

首先是不需要的列和重复的查询,select *是应该被避免的,而对于复用性强的数据,是否应该缓存在应用层,而不是依赖于mysql的缓存(查询需要经过网络传输和缓存失效的可能)。
查询的优化离不开索引,上一章和这里相似,再来一遍。
  1.引擎层,使用索引先一步过滤到大量的数据,就可以避免无关数据被扫描和上锁
  2.服务层,使用覆盖索引,直接从对应索引的B树找到数据返回,而不需要再去主键查一次。
  3.服务层,没有索引,大多数时候是全表扫描或者大量无关数据被返回出来,再通过where筛选。

对于group by语句,应当考虑是否有一张独立的统计表。

对于数据量大的语句,考虑是否将任务拆分,每隔一段时间来操作一部分数据,如limit 10000,避免长期对锁的持有

对于多表查询,是否应该将其分裂为多个子查询,会带来:
  1.缓存命中率高,最直接的就是,一个多表查询的结果缓存,一旦其中一个表有改变,可能整个缓存都不能用了,而单表查询的结果缓存就不会了。
  2.减少锁的竞争。
  3.如果在应用层对这些单表缓存,使用map或者hash_map对其进行hash关联会不会更有效率?

执行一条查询经历了什么

1.客户端把这条查询通过一个半双工的协议发给服务器,然后客户端剩下的就只有等待了。
  需要注意的是,当某个语言里调用mysql的接口去请求查询,只要接口返回,数据就已经保存在本地缓存了,而不是取数据的时候现查。

2.服务器检查缓存,如果缓存有,就直接返回而不执行任何操作。

3.对sql解析、预处理,再由优化器生成执行计划。
  解析器是检查语法规则、关键字、引号等是否匹配。
  预处理则是检查表、列是否存在,是由有权限等。
  查询优化:表关联的顺序、外连接转为内连接、条件等价变换、索引覆盖等。
  最终生成执行计划,是一串API、命令。

4.调用存储引擎的API执行这一串命令完成查询。

5.查询结果发给客户端。
  只要有一条结果被查询出来,就开始向客户端发,而不是等全部结果一起发。

具体优化举措

where in子查询,使用INNER JOIN或EXISTS子查询代替
  没有具体的方式,应该具体测试INNER JOIN快 还是 EXISTS子查询快。
  如果有等值传递,对于in列表的所有数据都会复制到其他条件一份,而当in列表非常大时…

UNION
  对于两个查询的合并,如果要limit,应该每个select的查询都有一个limit,否则就会返回全部的结果集,然后再取前多少条。
  同样的,所有的where条件在每个select中也应该有一份,便于优化器优化。
  尽可能使用UNION ALL,否则就会加上DISTINCT选项,导致对表做唯一性检查。

MIN、MAX优化
  select min(id) from 表1 where name=‘xxx’
  改为
  select id from 表1 USE INDEX(PRIMARY) WHERE name=‘xxx’ limit 1;
  如果name列没有索引,就会导致全表扫描,而主键字段ID是排序的,那么只要找到第一个条件满足的,那么它就是最小的

count优化
  对于MyISAM,只有在没有where时才会很快,否则就是全表或者使用了索引
  select count(*) from 表名 where id > 5
  改成
  select (select count(*) from 表名) - count(*) from 表名 where id <=5
  只扫描前5行,而不用扫描5之后的行

limit优化
  对于limit 1000,20这样的查询,会查询前1020条,然后显示那20条
  如果真的需要真的需要分页,那么最简单的应该是记录下主键ID,然后where + limit

一些指定查询方式的命令
  HIGH_PRIORITYLOW__PRIORITY
    只对表锁起作用,在行锁表中使用会导致表锁而降低性能。
    HIGH总是将任务放到最前面执行,LOW只要前面有任务,就不执行
    可用于insert或select。
  DELAYED
    对于insert和replace有效,数据放到缓存中
    客户端可以直接看到,数据库则在空闲时写入。
  STRAIGHT_JOIN
    放在select开头:所有的表都按出现顺序关联
    放在select两个关联表的中间:两个表按顺序关联。
  SQL_SMALL_RESULTSQL_BIG_RESULT
    只对select有效,对于查询结果,分别告mysql结果集很大或者很小,临时表分别放在磁盘或内存中
  SQL_BUFFER_RESULT
    查询结果放在临时表中,然后尽早释放表锁。
  SQL_CACHESQL_NO_CACHE
    是否将结果放在缓存中。
  SQL_CALC_FOUND_ROWS
    一个select加上limit后,只显示前多少条,该关键字返回在limit之前总共查了多少行。
  FOR UPDATELOCK IN SHARE MODE
    只对行锁有效,看不懂干嘛的。
  USE INDEXIGNORE INDEXFORCE INDEX
    使用某个索引或者不使用索引。

关于用户自定义变量
  set @var1 := 1;
  生命周期在一个连接内有效
  可以用作各种条件但不是不能做表名、列名之类的
  :=优先级很低,复杂表达式应使用括号。
  使用未定义变量不会有错误。
  在select中,可以不断修改自己来为下一行输出提供新值
  可以where xxx=xxx AND var1:=999

                                      2022年6月21日13:25:49

七 高级特性

创建表分区表的例子
create table sales(
order_date DATETIME NOT NULL
)ENGINE=InnoDB PARTITION BY RANGE(YEAR(order_date))
(PARTITION 默认分区 VALUES LESS THAN (0),       // 条件null的插入会被丢到这里PARTITION 分区1的名字 VALUES LESS THAN (2001),PARTITION 分区2的名字 VALUES LESS THAN (2002),PARTITION 分区3的名字 VALUES LESS THAN (2003)
)
分区表
对表分区,有点类似为一个表建立索引一样
主要作用是提供一个粗粒度的筛选,同索引一样目的是为了筛选数据
在用户的角度,分区表依旧是一个表,只不过是由多个物理子表组成,分区表知道这些子表的句柄
在引擎的角度,每个物理子表都被视作一个普通的表,引擎只提供对表访问的接口,不需要知道其是否属于某个分区表
数据量大时,除非是覆盖索引,否则B树索引失效,因为如果不是覆盖,那么回表查询导致大量随机IO,慢也就是理所应当了。
限制:
  1.每个表最多1024个分区
  2.分区表达式必须徐返回一个整数 YEAR(order_date)
  3.增删改查都会导致锁住所有分区,然后解锁不需要的,锁表只是执行操作前做的,如果引擎是行锁,处理过程中使用的就是行锁。
  4.不能用外键
  5.插入时《分区表达式》为NULL会导致数据被放入第一个分区,所以可以专门建一个默认分区
  6.分区列尽可能是索引列,否则会导致无法使用分区过滤,除非where条件里有分区列。或者说where条件应该总是带上分区列
两种使用场景:
  1.数据量大,不建立索引,只根据分区条件定位数据,因为数据量大,内存放不下,所以缓存也会失效
  2.数据量小,建立索引,经常访问的数据放在某个分区中。
视图
首先mysql不会存建试图的代码,show create view是各种转义符号,要自己存好。
对于试图的处理有两种算法:
  1.合并算法(优先):比如视图内部有where条件,而select * from 视图1 where xxx=xxx,这样就会导致条件合并到一个查询
  2.临时表算法:group by、distinct、聚合函数等,原表记录和视图记录不能一一映射就会导致临时表算法,使用临时表算法的视图是无法通过update语句更新实表的、
  3.查看使用什么算法: EXPLAIN SELECT * FROM 视图名PRIMARY是合并算法,DERIVED是临时表算法。
存储过程、函数、触发器、事件
感觉也没啥好写的
触发器需要注意:MyISAM表,如果出发后代码执行失败,那么前一张表是无法回滚,但是innodb是事务,所以可以回滚。
事件:定时任务,在独立的线程中调度,不接受参数,无返回值,如果执行一个事件需要2小时,而调度间隔是1小时,需要自己加过来防止。(前两行异常时释放锁)
  declare continue hanlder for SQLEXCEPTION
    BEGIN END;
  IF GET_LOCK(‘表名’, 0) THEN
    DO CALL optimize_tables(‘表名’);
  END IF;
  DO RELEASE_LOCK(‘表名’)
游标
只读、单向,大多数时候只在存储过程使用(客户端也不行),数据存在临时表。
声明一个游标会绑定一条sql语句。
打开一个游标会导致语句执行。
如果游标结果集很大,所有的查询结果都会在临时表中保存,但是如果只使用很少一部分数据就会很亏。
绑定变量
名字很怪,有点像函数签名之类的东西。
比如 insert into tb1(co1, co2) values('a', 'b')
被转化成
insert into tb1(co1, co2) values(?, ?)
然后对于后面生成的这个东西,服务器会保存下来,并返回一个与之相对的句柄,下一次客户端如果有相同的调用只需要发送句柄和参数就可以执行了
好处是:解析器、优化器等的工作只会执行依次,网络传输效率更高等。
书中还有写如何手动使用这个东西,然后拼语句,不过这里懒得写了
用户自定义函数
这里的用户自定义函数不是mysql的函数,mysql的函数遵循mysql的语法
而这里的用户自定义函数是指编译C/C++代码放在mysql服务器上运行,而用户需要保证代码线程安全,而且如果程序异常,还可能导致mysql崩溃。
字符集
字符集在mysql中的使用主要有两个地方
1.创建库、表、列的时候
  库、表都有自己的默认值,是逐层继承的。
  创建库时,根据character_set_server设置库的默认字符集
  创建表、列时,按上层的默认继承。
2.客户端和服务器通信的时候
  通信时,有三个字符集变量,分别是character_set_client、character_set_cinnection、character_set_result
  客户端发送sql语句查询时,从 character_set_client转为 character_set_cinnection
  执行时,使用 character_set_cinnection
  结果集返回给客户端时,从 character_set_cinnection转为 character_set_client
如果比较字符串的字符集不同,转成同一字符集比较。
还有排序要求的字符集要和服务器字符集相同才能使用索引排序
还要注意多字节字符, LENGTH()返回字节数, CHAR_LENGTH()返回字符数。
全文索引
关键字匹配,关键字搜索之类的东西,没看懂。
分布式事务(XA事务)
内部分布式事务:跨引擎的事务,XA协议作为一个协调者。
外部分布式事务:看不懂。
查询缓存
概念:
  select查询的缓存,如果有相同的查询,就返回缓存。
  缓存会追踪相关的表,只要任何一行数据变动都会导致缓存失效,即使改变的地方不影响查询结果,只是为了实现简单。
内存分布:
  mysql使用内存池来查询缓存。启动时申请一块大空间,初始化40KB左右的空间作为管理空间使用。
  缓存结果放入时:一块一块的申请,直到放完,最后一块用不完的空间会被归还从而导致内存碎片,可以用 FLUSH QUERY CACHE整理碎片。
判断是否命中:
  首先,判断是不是SEL(select)开头,然后才查缓存。
  缓存放在一个哈希表里,计算hash的参数有很多,查询本身、数据库、协议版本等,只要有一个改变,甚至是一个空格,都会导致未命中。
  查询中包含函数、存储过程、用户变量、临时表、系统表等函数的都不会被缓存,如带有now()函数,因为他们是不确定的值。
查询缓存的配置参数:
  query_cache_type:OFF、ON、DEMAND,三个选项,是否开启缓存,DEMAND只有在查询语句中写明 SQL_CACHE才会缓存。
  query_cache_size:缓存使用的总空间,单位字节,1024整数倍。
  query_cache_min_res_unit:内存池中,块的最小单位
  query_cache_limit:能缓存的最大长度,超出就不会缓存,需要注意,查询开始时不知道会不会超出限制,而是发现超出限制时,丢掉,可以加 SQL_NO_CACHE来表示不缓存
  query_cache_wlock_invalidate:如果读缓存的时候,相关表被锁住,说明其他地方正在修改数据,该值OFF(默认)表示继续读,ON则表示等待锁,读新的数据。
缓存命中率低的原因、表现:
  查看 Qcache_free_memory,如果剩余的空间多,说明是内存碎片
  剩余空间少,说明新的缓存被放入,而旧的缓存被移除,同时导致 Qcache_lowmem_prunes增长快
使用场景、限制:
  一次插入多条只会缓存失效一次,而多次插入一条导致多次失效。
  缓存空间过大,或者缓存内容过大,会导致操作期间服务器僵死。
  写密集场景直接禁用会更好

                                      2022年6月21日16:39:04

八 服务器配置文件

一个阉割版但是《够用》的配置文件(略有修改),后面篇幅基本都基于此[mysqld]
# GENERALdatadir                    = /var/lib/mysqlsocket                 = /var/lib/mysql/mysql.sockpid_file                = /var/lib/mysql/mysql.piduser                 = mysql            # 哪个系统用户启动mysqld,需要存在且有权限port                    = 3306default_storage_engine   = InnoDB
# INNODBinnodb_buffer_poll_size = 自定义的值                    # innodb最依赖的选项,见后:InnoDB的配置innodb_max_dirty_pages_pct = 0                        # 脏页百分比,超过该值时,把缓存刷到批判innodb_log_file_size = 自定义的值                    # 默认是5MB,大多数时候128M足足足够了innodb_file_per_table     = 1                            # 将数据存到某个具体的表空间文件,而不是由mysql管理,鸡肋innodb_flush_method       = O_DIRECTinnodb_flush_log_at_trx_commit = 0、1、2              # 详见后:InnoDB的事务日志
# MyISAMkey_buffer_size         = 自定义的值myisam_block_size       = 最好填一个和操作系统页大小相同的值
# LOGGINGlog_errror             = /var/lib/mysql/mysql-error.logslow_query_log         = /var/lib/mysql/mysql-slow.log
# OTHERtmp_table_size           = 32M                      # 临时表(memory引擎)的最大大小,超过会导致磁盘表max_heap_table_size     = 32M                      # 临时表(memory引擎)的最大大小,超过会导致磁盘表query_cache_type        = 0query_cache_size        = 0max_connections         = 自定义的值                    # 最大的连接用户数量thread_cache         = 自定义的值                    # 连接线程池的连接数量(当前有的)thread_cache_size       = 自定义的值                    # 连接线程池的连接数量(可容纳的)table_cache             = 自定义的值open_files_limit        = 65536[client]user                    = mysqlport                    = 3306
一些其他的配置选项expire_logs_days                # 保留多少天的二进制日志max_allowed_packet             # 控制数据包的最大大小,包含发生和接收max_connect_errors               # 可以设的大,防止网络波动,客户端频繁发起连接而被拉入黑名单(每隔一段时间移除)skip_name_resolve                # 跳过dns查找sql_mode                       # 很多选项read_only                     # 禁止《无特权用户变更》,即备库,只接受主库传来的变更skip_slave_strat              # 禁用自启动salve_net_timeout                # 主备库断连后,重连的间隔,默认1小时,可以设的短innodb                           # 若设为FORCE,只有在支持innodb时才启动innodb_autoinc_lock_mode       # 0:对于自增列的锁定,insert结束才释放# 1:默认,拿到新的自增值后,自增列锁就释放# 2:不上锁,得到的值可能不连续innode_buffer_pool_instances    # 把缓存池切成多块,锁竞争没那么厉害innodb_io_capacity                # 每秒多少次IO到磁盘?看不太懂innodb_read_id_threads          # 默认4,后台有几个读线程innodb_write_id_threads            # 默认4,后台有几个写线程innodb_strict_mode             # 让警告变为报错innodb_old_blocks_time         # 全表扫描多时,设置大点,否则设小点
一些胡言乱语
秉承能跑就不动的经验,默认配置经历了大量的检验,除非明确清除修改会带来性能收益才修改。
也不要相信各种网上的优化配置脚本,没有完美适用于所有场景的配置,甚至因为工作负载不同,上午会带来性能提升的配置,下午就可能导致性能降低。
配置值又该设置多大合适,比如各种缓存的大小:
  配置小了导致各种缓存未命中
  配置大了,如果缓存几G甚至几十G,那么缓存中是大量未写入磁盘的数据,关闭的时候会慢,或者直接关掉,但是下一次启动时,还是需要去处理这些未同步到磁盘的数据,
配置文件从哪读
不同的系统位置可能不一样,稳妥的方法是
执行 which mysqld,输出 /usr/sbin/mysqld(可能会不同)
执行 /usr/sbin/mysqld --verbose --help | grep -A 1 'Default options',输出配置文件的位置
配置也可以从命令行添加,但是可能会被忘记,最好还是写到文件。
配置文件的语法、动态修改、生命周期等
配置项有全局的、会话的、对象的。
  show session variables;       显示会话变量
  show global variables;       显示全局变量
  set session varname = value;    设置会话变量
  set global sort_buffer_size = 40000; 设置全局变量
需要注意,即便是对全局配置的修改,下一次启动时还是按配置文件来。
分多少内存给mysql作为缓存
需要考虑的问题有很多,同一服务器上是否还有其他程序需要运行、为操作系统保留的运行内存、mysql本身需要使用多少内存等,把这些纳入考虑范围后得出一个大致《可分配内存总量》,即便是这个值,也不该全塞给mysql,总得预留一些应对突发情况,然后将《可分配内存总量》细分给本章开头的那些具体配置,比如 innodb_buffer_poll_sizekey_buffer_size 之类的。
MyISAM使用《操作系统的缓存》来存储《表数据》,而不像InnoDB那样,操作系统分内存给mysql,mysql再把内存分给InnoDB来存《表数据》
所以,到底分多少内存给mysql,真的需要根据场景区分。
InnoDB的配置
如果表是InnoDB,那么 innodb_buffer_poll_size很重要,因为这个空间存放着行缓存、索引、缓冲区(延迟写入)、锁等等很多结构。
何时同步到磁盘:有一个线程专门来同步到磁盘数据,当脏页数量超过 innodb_max_dirty_pages_pct时同步。
MyISAM的配置
MyISAM数据和索引不在一块,所以只有键缓存大小 key_buffer_size
select sum(INDEX_LENGTH) from INFORMATION_SCHEMA.TABLES WHERE ENGINE='MYISAM'
上述语句可以查出当前mysql里,所有使用myisam引擎的表所占索引的总空间
另外,因为某些时候会用MyISAM作临时表,所以应该总是给 key_buffer_size一个值。
由于MyISAM依赖于操作系统的缓存,所以还有一个 myisam_block_size,当修改的数据到了操作系统的缓存上,操作系统的缓存又要和磁盘同步, myisam_block_size操作系统的页大小相同,会带来效率的提升。
InnoDB的事务日志
对于InnoDB的每条语句的执行都视为一个事务,InnoDB选择《不在》事务commit时把修改的数据写到磁盘
所有的操作,都由事务日志记录,当日志缓存刷到磁盘时,修改的数据才写到磁盘。
好处是单条的commit写入磁盘带来随机IO,而事务日志集中写时,会尽可能将这些随机IO有序写入磁盘,大小依赖于 innodb_log_file_size
如果日志文件小,那么频繁的写入磁盘会拖慢,如果日志大,崩溃恢复的时候可能需要做更多工作。
如果在运行时修改日志文件大小,要完全关闭mysql,备份旧日志,重启成功之后才能删掉旧日志。
innodb_flush_log_at_trx_commit
  0,即默认,每隔一秒通过日志把数据同步到磁盘。
  1,写日志文件的时候,同时把数据写到磁盘
  2,只保存日志本身到磁盘,但是不同步那些数据到磁盘,mysqld突然死掉,可以根据日志恢复,只是数据还没写进去而已,但是断电还是会丢失一部分。
前半部分由mysql缓存刷到磁盘的《请求》过程,接下来是刷到磁盘的方式
innodb_flush_method
  fdatasync:使用fsync函数同步数据到磁盘,但是如果指定了 innodb_flush_method,会导致IO操作无法合并,多次调用fsync,详见后InnoDB表空间。另外,还会导致双重缓存,即mysql内部缓存一遍日志,数据经过fsync,又会在操作系统上缓存一遍。
  O_DIRECT、ALL_O_DIRECT:不经过操作系统,直接写到磁盘或从磁盘读。
  O_DSYNC:导致阻塞,数据完全写入磁盘后才返回。
InnoDB表空间
其出现的原因是文件系统不够大,所以将表或者说整个表的数据,存放到某个文件上,可以在不同的磁盘,每张表一个文件,而现在文件系统足够大了,管理表空间只会带来更多麻烦。
MyISAM同步到磁盘
MyISAM分为两部分,由mysql缓存的索引,由操作系统缓存的数据,书中只描述了如何同步索引,方式和InnoDB大同小异,而数据如何同步,并没有看到,也许…修改操作系统的缓存,然后由操作系统完成同步,同时还描述了一些MyISAM备份、修复之类的东西。

                                      2022年6月24日16:49:49

九 硬件和mysql

看不懂,以后再看吧。

十 备库

主备库概念
两种复制方式:行复制和语句复制
  行复制:即只根据当前的、改变后的数据的值进行复制。
  语句复制;记录sql语句,然后通过sql语句去修改备库的数据。
不管是行复制,还是语句复制,都是将对应的信息,转化为《 二进制日志》 记录。
1.主库上,只要有语句执行或者数据改变之类,相关信息记录到《 二进制日志》。
2.备库上,一条IO线程,长连接到主库,有《 二进制日志》就拉到备库,日志拉到备库后上被称为《 中继日志》,没有就睡眠。
3.备库上,另一条sql线程,不断处理拉来的《 中继日志》,处理过的《 中继日志》又被写入到备库的《 二进制日志》,完成数据备份。
配置主备库的方法(两个新服务器开始配置,另见后)
1.主库和备库都执行:
   grant replication slave, replication client on *.* to shh_test@'192.168.0.%' idenified by '12345678';
  该语句会导致建立shh_test用户,并设置密码,主备库都加replication client,便于主库监控。
  两个账号一样,也方便主备库交换角色。
2.主备库配置文件
  除了server_id都不是必须的,server_id默认1,不设置的话可能冲突
  log_bin就是那个《二进制日志》文件的名字,不填的话就是机器名字之类的生成,不过书中的例子需要这个参数, 另外,mysql还会具体生成的日志文件名加一些后缀,如mysql-bin.00001,才是真正的文件名,可以通过show master status;来查看到底是什么文件名
  主库配置文件
     log_bin= /var/lib/mysql/mysql-bin
     server_id= 10    ID的主要目的是避免循环复制。
     sync_binlog=1    该行导致事务commit之前先写二进制日志防止丢失,但是只对库上的《 二进制日志》有效,对于备库上拉来的《 中继日志》无效。
  备库配置文件
     log_bin= mysql-bin
     server_id= 11
     relay_log= /var/lib/mysql/mysql-relay-bin  《二进制日志》从主库拉到备库存储的目录、文件名
     log_slave_updates= 1    有点看不懂,但是最好设置,备份操作本身的命令也记录到日志之类的?
     read_only= 1
     skip_slave_start    备库崩溃重启后,有该选项,将阻止继续同步主库的数据,以防止损坏后没有机会恢复
     sync_master_info=1    最后三个sync,《 中继日志》是不安全的,崩溃后,中继日志丢失,这些操作用来同步《 中继日志》到磁盘防丢失。
     sync_relay_log=1
     sync_relay_log_info=1
3.启动复制
   change master to
     MASTER_HOST='主机名字或ip',
     MASETER_SUER='shh_test',
     MASTER_PASSWORD='12345678',
     MASTER_LOG_FILE='mysql-bin.00001',
     MASTER_LOG_POS=0;
   start slave;
使用 show slave status;可以查看备库工作的详细信息。
配置主备库的方法(备份一个已有的服务器)
关闭数据库,直接复制数据。
MyISAM表可以使用mysqlhotcopy和rsync来复制数据。细节在15章,以后回来补。
InnoDB使用mysqldump。
快照或者备份:需要知道对应的二进制坐标,使用前面的 change master to ,细节也在15章。
热备份工具:Xtrabackup,也在15章。
10.4之后的随便写点,大部分有点架空,看不懂,以后再看。
一个主库可以有多备库,反过来备库不能有多个主库,但是《备库可以改变指向来备份不同的主库》。
一个主库拥有过多备库时,也会导致性能问题,大概数量是10?因为每个备库都会开一个线程拉日志。可以来一个分发备库,从主库拉,其他备库从分发备库拉日志。
使用非事务表如MyISAM引擎,关闭mysql前,一定要 stop slave,否则kill掉正在update的线程,导致主备不一致。
避免混用事务表和非事务表,避免主备使用不同的引擎。
不同主备之间一定要有唯一服务器ID,未定义或重复都可能出现问题。
使用实体表代替临时表,主库使用临时表时崩溃,备库复制时可能也会依赖他们。
避免在主库上创建备库上没有的 库或表

                                      2022年7月11日12:01:31

高性能mysql 随笔相关推荐

  1. 高性能Mysql主从架构的复制原理及配置详解

    1 复制概述 Mysql内建的复制功能是构建大型,高性能应用程序的基础.将Mysql的数据分布到多个系统上去,这种分布的机制,是通过将Mysql的某一台主机的数据复制到其它主机(slaves)上,并重 ...

  2. mysql 树形结构_再读MySQL索引-《高性能MySQL》索引手记

    最近工作中经常和MySQL打交道,当数据量小的时候,不同查询方式以及是否使用索引并无大碍,当数据量随着业务的成长急剧加速时,索引的重要性不言而喻. 本篇文章以<高性能MySQL>中的索引章 ...

  3. 高性能mysql主存架构

    高性能mysql主存架构 MySQL Replication(Master与Slave基本原理及配置) 主从mysql工作原理: 1:过程: (1)Mysql的复制(replication)是一个异步 ...

  4. 《高性能MySQL》の复制

    2019独角兽企业重金招聘Python工程师标准>>> 0x00前言 本书讲述到定稿前的MySQL5.5版,所以下面内容的适用范围止步于MySQL5.5.本文仅仅强调书中讲述的重中之 ...

  5. 读薄《高性能MySql》(四)查询性能优化

    读薄<高性能MySql>(一)MySql基本知识 读薄<高性能MySql>(二)Scheme与数据优化 读薄<高性能MySql>(三)索引优化 读薄<高性能M ...

  6. 高性能MySQL数据库(含二级考试)-张晨光-专题视频课程

    高性能MySQL数据库(含二级考试)-565人已学习 课程介绍         打造高性能MySQL数据库,完善的课程体系,基础+实操让你学透Mysql,高效解决企业数据库性能问题掌握MySql核心技 ...

  7. 转-《高性能mysql》并不是一本好书——SQL笔记

    转自: https://book.douban.com/review/8122660/ 版权归作者所有,任何形式转载请联系作者. 作者:姚泽源(来自豆瓣) 来源:https://book.douban ...

  8. 高性能MySQL之Count统计查询

    近一段时间,有同事问我 "MySQL执行count很慢,有没有什么优化的空间".当时在忙,就回复了一句"innodb里面count统计都是实时统计,慢一些是正常的&quo ...

  9. 高性能MySQL(4)——查询性能优化

    査询优化.索引优化.库表结构优化需要齐头并进,一个不落. 一.为什么查询速度为变慢 在尝试编写快速的查询之前,需要清楚一点,真正重要是响应时间.如果把查询看作是一个任务,那么他由一系列子任务组成,每个 ...

最新文章

  1. Django 定义模型2.1
  2. JAVA常用基础知识点[继承,抽象,接口,静态,枚举,反射,泛型,多线程...]
  3. 有什么值得推荐的Java Web练手项目?
  4. FPGA基础之逻辑单元(LE or LC)的基本结构
  5. 为什么要在基类使用私有数据_为什么要使用函数
  6. 深入单例模式 java,深入单例模式四
  7. 计算机网络运输层的概述,计算机网络_运输层
  8. 此“小霸王”非彼小霸王?官方声明:小霸王并未破产!
  9. VS2010怎样显示行号
  10. 【css练习】斑马线表格,美人尖,断线下划线
  11. 【王道计组笔记】总线(2):性能指标分析
  12. python力导向图论文_力导向图(关系图) echarts的运用
  13. 惠普打印机如何设置扫描到计算机,惠普打印机怎样扫描文件到电脑
  14. 建行u盾弹不出来_建设银行网银盾检测不到怎么解决
  15. 来自Google的围棋AlphaGo
  16. 如何用TextView显示Html格式的数据
  17. 无线网络性能测试 软件,WiFi性能测试
  18. 五则运算c语言程序,C语言算术运算示例程序
  19. C语言控制桌面背景图
  20. 24点游戏(自动生成随机数)

热门文章

  1. Cool Edit之生成.pk文件问题
  2. 用Tina-TI软件仿真并分析RC积分电路和微分电路
  3. 利用简单的爬虫获取CV顶会论文
  4. ChatGPT:开启智能对话的未来
  5. JQuery属性选择器(属性值是变量、多属性选择)
  6. 修改服务器上tomcat的默认端口号
  7. 感受山猫之力 Ubuntu 10.04 LTS试用手记
  8. TranUnet 复现[Errno 2] No such file or directory: ‘./model/TU_Synapse224/TU_pretrain_ViT-B_16_skip3_ep
  9. Mybatis教程之Mybatis配置篇
  10. 微星z370安装linux系统,微星Z370+8700K+1080ti安装10.13.6成功,安装思路及EFI分享