友儿屋 - MYSQL专题
type: channel
link: https://www.niuzheng.net/category/zt/
description: MYSQL专题
items: 数据库结构优化、高可用架构设计、数据库索引
items: 面试必备，MySQL索引优化实战总结，涵盖了几乎所
items: Mysql手册查询之常用函数大全
items: MySQL对于千万级的大表要怎么优化？
items: 设计数据库和表需要考虑哪些才不容易走弯路？
items: 什么是MySQL锁？有哪些锁类型？如何使用？
items: Mysql分库分表，你如何分，怎样分？
items: MySQL中你是如何REGEXP正则表达式
items: 写一手好SQL，你该从哪里入手？
items: 为什么你写的sql查询慢？为什么你建的索引常失效？

数据库结构优化、高可用架构设计、数据库索引
type: item
date: 2021-02-18T14:48:20+08:00
description: 一、数据库结构优化1.1 数据库结构优化目的1、减少数据冗余：（数据冗余是指在数据库中存在相同的数据，或者某些数据可以由其他数据计算得到），注意，尽量减少不代表完全避免数据冗余；2、尽量避免数据维护中出现更新，插入和删除异常：总结：要避免异常，需要对数据库结构进行范式化设计。3、节约数据存储空间。4、提高查询效率。1.2 数据库结构设计步骤1、需求分析：全面了解产品设计的存储需求、数据处理需求、数据安全性与完整性；2、逻辑设计（重要）：设计数据的逻辑存储结构。数据实体之间的逻辑关系，解决数据冗余和数据维护异常。数据范式可以帮助我们设计；3、物理设计：表结构设计，存储引擎与列的数据类型；4、维护优化：索引优化、存储结构优化。1.3 数据库范式设计与反范式化传送门：数据库逻辑设计之三大范式通俗理解，一看就懂，书上说的太晦涩1.4 物理设计 相关传送门：MySQL中字段类型与合理的选择字段类型；int(11)最大长度是多少？，varchar最大长度是多少二、高可用架构设计 2.1 读写分离MaxScale：实现MySQL读写分离与负载均衡的中间件利器三、数据库索引优化3.1 两种主要数据结构：B-tree和Hash3.1.1 B-tree结构B-tree索引的限制：3.1.2 Hash结构Hash索引的限制：Hash索引必须进行二次查找Hash索引无法用于排序Hash索引不支持部分索引查找也不支持范围查找Hash索引中Hash码的计算可能存在Hash冲突，不适合重复值很高的列，如性别，身份证比较合适。3.1.3 MySQL常见索引和各种索引区别 PRIMARY KEY（主键索引） ALTER TABLE `table_name` ADD PRIMARY KEY ( `column` ) UNIQUE(唯一索引) ALTER TABLE `table_name` ADD UNIQUE (`column`) INDEX(普通索引) ALTER TABLE `table_name` ADD INDEX index_name ( `column` ) FULLTEXT(全文索引) ALTER TABLE `table_name` ADD FULLTEXT ( `column` ) 组合索引 ALTER TABLE `table_name` ADD INDEX index_name ( `column1`, `column2`, `column3` ) 普通索引：最基本的索引，没有任何限制唯一索引：与"普通索引"类似，不同的就是：索引列的值必须唯一，但允许有空值。主键索引：它 是一种特殊的唯一索引，不允许有空值。全文索引：仅可用于 MyISAM 表，针对较大的数据，生成全文索引很耗时好空间。组合索引：为了更多的提高mysql效率可建立组合索引，遵循”最左前缀“原则。3.2 使用索引好处和索引缺陷3.2.1 为什么要使用索引1、索引大大减少了存储引擎需要扫描的数据量；2、索引可以帮助我们进行排序以避免使用临时表；3、索引可以把随机I/O变为顺序I/O。3.2.2 索引不是越多越好1、索引会增加写操作的成本；2、太多的索引会增加查询优化器的选择时间。索引就好比一本书的目录，它会让你更快的找到内容，显然目录（索引）并不是越多越好，假如这本书1000页，而有500页是目录，它当然效率低，目录是要占纸张的,而索引是要占磁盘空间的。3.3 索引优化策略3.3.1 索引列上不能使用表达式和函数3.3.2 前缀索引和索引列的选择性Innodb索引列最大宽度为667个字节(utf-8 差不多255个字符),MyIsam索引类宽度最大为1000个字节，于是出现前缀索引，索引的选择性。对于列的值较长，比如BLOB、TEXT、VARCHAR，就必须建立前缀索引，即将值的前一部分作为索引。这样既可以节约空间，又可以提高查询效率。但无法使用前缀索引做 ORDER BY 和 GROUP BY，也无法使用前缀索引做覆盖扫描。语法： ALTER TABLE table\_name ADD KEY(column\_name(prefix\_length))如何选择索引列的顺序：1、经常会被使用到的列优先（选择性差的列不适合，如性别，查询优化器可能会认为全表扫描性能更好）；2、选择性高的列优先；3、宽度小的列优先（一页中存储的索引越多，降低I/O，查找越快）；3.3.3 组合/联合索引策略如果索引了多列，要遵守最左前缀法则。指的是查询从索引的最左前列开始并且不跳过索引中的列。 深入理解请移步：最左前缀原理与相关优化3.3.4 覆盖索引策略跟组合索引有点类似，如果索引包含所有满足查询需要的数据的索引则成为覆盖索引(Covering Index)，也就是平时所说的不需要回表操作。即索…

面试必备，MySQL索引优化实战总结，涵盖了几乎所
type: item
date: 2021-02-18T14:48:16+08:00
description: 索引的种类众所周知，索引类似于字典的目录，可以提高查询的效率。索引从物理上可以分为：聚集索引，非聚集索引从逻辑上可以分为：普通索引，唯一索引，主键索引，联合索引，全文索引索引优化策略 不要在索引列上进行运算或使用函数在列上进行运算或使用函数会使索引失效，从而进行全表扫描。如下面例子在publish\_time，id列上分别加上索引，publish\_time为datetime类型，id为int类型 -- 全表扫描select * from article where year(publish_time) < 2019 -- 走索引select * from article where publish_time < '2019-01-01' -- 全表扫描select * from article where id + 1 = 5 -- 走索引select * from article where id = 4小心隐式类型转换 假设id为varchar类型 -- 全表扫描select * from article where id = 100 -- 走索引select * from article where id = '100'为什么呢？select * from article where id = 100 -- 等价于select * from article where CAST(id AS signed int) = 100上一条规则说过，不要在索引列上使用函数，隐式类型转换在索引字段上做了函数操作，因此会全表扫描那么如果id是int，执行下面这个语句是否会导致全表扫描呢？select * from article where id = '100'答案是会用到索引 前导模糊查询不会使用索引 -- 全表扫描select * from article where author like '%李'%李,%李%都会导致全表扫描，非前导模糊查询可以使用索引 -- 走索引select * from article where author like '李%' 联合索引最左前缀原则mysql会一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配，比如a = 1 and b = 2 and c > 3 and d = 4 如果建立(a,b,c,d)顺序的索引，d是用不到索引的，如果建立(a,b,d,c)的索引则都可以用到，a,b,d的顺序可以任意调整1.将区分度最高的字段放在最左边当不需要考虑排序和分组时，将区分度最高的列放在前面通常是很好的。这时候索引的作用只是用于优化WHERE条件的查找如果在a b列上建立联合索引，该如何建立，才能使查询效率最高 select count(distinct a) / count(*), count(distinct b) / count(*), count(*) from table执行如下语句，假设3个输出依次为0.001,0.373,16049，可以看到b列的选择性最高，因此将其作为联合索引的第一列，即建立(b, a)的联合索引2.查询时=可以乱序如果建立了联合索引（a, b）。例如下面的2个写法是等价的，因为MySQL会将查询的顺序优化成和联合索引的顺序一致select * from table where a = '1' and b = '1' select * from table where b = '1' and a = '1'3.优化查询，避免出现filesortselect * from table where a = ? and b = ? order by c最左前缀原则不仅用在查询中，还能用在排序中。MySQL中，有两种方式生成有序结果集：通过有序索引顺序扫描直接返回有序数据Filesort排序，对返回的数据进行排序因为索引的结构是B+树，索引中的数据是按照一定顺序进行排列的，所以在排序查询中如果能利用索引，就能避免额外的排序操作。EXPLAIN分析查询时，Extra显示为Using index。所有不是通过索引直接返回排序结果的操作都是Filesort排序，也就是说进行了额外的排序操作。EXPLAIN分析查询时，Extra显示为Using filesort，当出现Using filesort时对性能损耗较大，所以要尽量避免Using filesort对于如下sqlselect * from table where a = ? and b = ? order by c可以建立联合索引（a, b, c） 如果索引中有范围查找，那么索引有序性无法利用，如select * from table where a > 10 order by b索引(a，b)无法…

Mysql手册查询之常用函数大全
type: item
date: 2021-02-18T14:48:00+08:00
description: 一、数学函数ABS(x) 返回x的绝对值BIN(x) 返回x的二进制（OCT返回八进制，HEX返回十六进制）CEILING(x) 返回大于x的最小整数值EXP(x) 返回值e（自然对数的底）的x次方FLOOR(x) 返回小于x的最大整数值GREATEST(x1,x2,...,xn)返回集合中最大的值LEAST(x1,x2,...,xn) 返回集合中最小的值LN(x) 返回x的自然对数LOG(x,y)返回x的以y为底的对数MOD(x,y) 返回x/y的模（余数）PI()返回pi的值（圆周率）RAND()返回０到１内的随机值,可以通过提供一个参数(种子)使RAND()随机数生成器生成一个指定的值。ROUND(x,y)返回参数x的四舍五入的有y位小数的值SIGN(x) 返回代表数字x的符号的值SQRT(x) 返回一个数的平方根TRUNCATE(x,y) 返回数字x截短为y位小数的结果二、聚合函数(常用于GROUP BY从句的SELECT查询中)AVG(col)返回指定列的平均值COUNT(col)返回指定列中非NULL值的个数MIN(col)返回指定列的最小值MAX(col)返回指定列的最大值SUM(col)返回指定列的所有值之和GROUP_CONCAT(col) 返回由属于一组的列值连接组合而成的结果三、字符串函数ASCII(char)返回字符的ASCII码值BIT_LENGTH(str)返回字符串的比特长度CONCAT(s1,s2...,sn)将s1,s2...,sn连接成字符串CONCAT_WS(sep,s1,s2...,sn)将s1,s2...,sn连接成字符串，并用sep字符间隔INSERT(str,x,y,instr) 将字符串str从第x位置开始，y个字符长的子串替换为字符串instr，返回结果FIND_IN_SET(str,list)分析逗号分隔的list列表，如果发现str，返回str在list中的位置LCASE(str)或LOWER(str) 返回将字符串str中所有字符改变为小写后的结果LEFT(str,x)返回字符串str中最左边的x个字符LENGTH(s)返回字符串str中的字符数LTRIM(str) 从字符串str中切掉开头的空格POSITION(substr,str) 返回子串substr在字符串str中第一次出现的位置QUOTE(str) 用反斜杠转义str中的单引号REPEAT(str,srchstr,rplcstr)返回字符串str重复x次的结果REVERSE(str) 返回颠倒字符串str的结果RIGHT(str,x) 返回字符串str中最右边的x个字符RTRIM(str) 返回字符串str尾部的空格STRCMP(s1,s2)比较字符串s1和s2TRIM(str)去除字符串首部和尾部的所有空格UCASE(str)或UPPER(str) 返回将字符串str中所有字符转变为大写后的结果四、日期和时间函数CURDATE()或CURRENT_DATE() 返回当前的日期CURTIME()或CURRENT_TIME() 返回当前的时间DATE_ADD(date,INTERVAL int keyword)返回日期date加上间隔时间int的结果(int必须按照关键字进行格式化),如：SELECTDATE_ADD(CURRENT_DATE,INTERVAL 6 MONTH);DATE_FORMAT(date,fmt) 依照指定的fmt格式格式化日期date值DATE_SUB(date,INTERVAL int keyword)返回日期date加上间隔时间int的结果(int必须按照关键字进行格式化),如：SELECTDATE_SUB(CURRENT_DATE,INTERVAL 6 MONTH);DAYOFWEEK(date) 返回date所代表的一星期中的第几天(1~7)DAYOFMONTH(date) 返回date是一个月的第几天(1~31)DAYOFYEAR(date) 返回date是一年的第几天(1~366)DAYNAME(date) 返回date的星期名，如：SELECT DAYNAME(CURRENT_DATE);FROM_UNIXTIME(ts,fmt) 根据指定的fmt格式，格式化UNIX时间戳tsHOUR(time) 返回time的小时值(0~23)MINUTE(time) 返回time的分钟值(0~59)MONTH(date) 返回date的月份值(1~12)MONTHNAME(date) 返回date的月份名，如：SELECT MONTHNAME(CURRENT_DATE);NOW() 返回当前的日期和时间QUARTER(date) 返回date在一年中的季度(1~4)，如SELECT Q…

MySQL对于千万级的大表要怎么优化？
type: item
date: 2021-02-18T14:48:00+08:00
description: 首先采用Mysql存储千亿级的数据，确实是一项非常大的挑战。Mysql单表确实可以存储10亿级的数据，只是这个时候性能非常差，项目中大量的实验证明，Mysql单表容量在500万左右，性能处于最佳状态。针对大表的优化，主要是通过数据库分库分表来解决，目前比较普遍的方案有三个：分区，分库分表，NoSql/NewSql。实际项目中，这三种方案是结合的，目前绝大部分系统的核心数据都是以RDBMS存储为主，NoSql/NewSql存储为辅。分区首先来了解一下分区方案。分区表是由多个相关的底层表实现的。这些底层表也是由句柄对象表示，所以我们也可以直接访问各个分区，存储引擎管理分区的各个底层表和管理普通表一样（所有的底层表都必须使用相同的存储引擎），分区表的索引只是在各个底层表上各自加上一个相同的索引。这个方案对用户屏蔽了sharding的细节，即使查询条件没有sharding column，它也能正常工作（只是这时候性能一般）。不过它的缺点很明显：很多的资源都受到单机的限制，例如连接数，网络吞吐等。如何进行分区，在实际应用中是一个非常关键的要素之一。下面开始举例：以客户信息为例，客户数据量5000万加，项目背景要求保存客户的银行卡绑定关系，客户的证件绑定关系，以及客户绑定的业务信息。此业务背景下，该如何设计数据库呢。项目一期的时候，我们建立了一张客户业务绑定关系表，里面冗余了每一位客户绑定的业务信息。基本结构大致如下：查询时，对银行卡做索引，业务编号做索引，证件号做索引。随着需求大增多，这张表的索引会达到10个以上。而且客户解约再签约，里面会保存两条数据，只是绑定的状态不同。假设我们有5千万的客户，5个业务类型，每位客户平均2张卡，那么这张表的数据量将会达到惊人的5亿，事实上我们系统用户量还没有过百万时就已经不行了。这样的设计绝对是不行的，无论是插入，还是查询，都会让系统崩溃。mysql数据库中的数据是以文件的形势存在磁盘上的，默认放在/mysql/data下面（可以通过my.cnf中的datadir来查看）， 一张表主要对应着三个文件，一个是frm存放表结构的，一个是myd存放表数据的，一个是myi存表索引的。这三个文件都非常的庞大，尤其是.myd文件，快5个G了。下面进行第一次分区优化，Mysql支持的分区方式有四种：在我们的项目中，range分区和list分区没有使用场景，如果基于绑定编号做range或者list分区，绑定编号没有实际的业务含义，无法通过它进行查询，因此，我们就剩下 HASH 分区和 KEY 分区了，HASH分区仅支持int类型列的分区，且是其中的一列。 KEY 分区倒是可以支持多列，但也要求其中的一列必须是int类型；看我们的库表结构，发现没有哪一列是int类型的，如何做分区呢？增加一列，绑定时间列，将此列设置为int类型，然后按照绑定时间进行分区，将每一天绑定的用户分到同一个区里面去。这次优化之后，我们的插入快了许多，但是查询依然很慢，为什么？因为在做查询的时候，我们也只是根据银行卡或者证件号进行查询，并没有根据时间查询，相当于每次查询，mysql都会将所有的分区表查询一遍。进行第二次方案优化，既然 HASH 分区和 KEY分区要求其中的一列必须是int类型的，那么创造出一个int类型的列出来分区是否可以？分析发现，银行卡的那串数字有秘密。银行卡一般是16位到19位不等的数字串，我们取其中的某一位拿出来作为表分区是否可行呢，通过分析发现，在这串数字中，其中确实有一位是0到9随机生成的，我们基于银行卡号+随机位进行KEY分区，每次查询的时候，通过计算截取出这位随机位数字，再加上卡号，联合查询，达到了分区查询的目的，需要说明的是，分区后，建立的索引，也必须是分区列，否则Mysql还是会在所有的分区表中查询数据。通过银行卡号查询绑定关系的问题解决了，那么证件号呢，如何通过证件号来查询绑定关系。前面已经讲过，做索引一定是要在分区健上进行，否则会引起全表扫描。我们再创建了一张新表，保存客户的证件号绑定关系，每位客户的证件号都是唯一的，新的证件号绑定关系表里，证件号作为了主键，那么如何来计算这个分区健呢，客户的证件信息比较庞杂，有身份证号，港澳台通行证，机动车驾驶证等等，如何在无序的证件号里找到分区健。为了解决这个问题，我们将证件号绑定关系表一分为二，其中的一张表专用于保存身份证类型的证件号，另一张表则保存其他证件类型的证件号，在身份证类型的证件绑定关系表中，我们将身份证号中的月数拆分出来作为了分区健，将同一个月出生的客户证件号保存在同一个区，这样分成了12个区，其他证件类型的证件号，数据量不超过10万，就没有必要进行分区了。这样每次查询时，首先通过证件类型确定要去查询哪张表，再计算分区健进行查询。作了分区设计之后，保存20…

设计数据库和表需要考虑哪些才不容易走弯路？
type: item
date: 2021-02-18T14:48:00+08:00
description: 在数据库设计和表创建时，我们首要考虑的就是性能咯，不然的话，在后期数据更新到千亿级别时，再来优化，那成本就加大了。或者说你给后期的开发人员挖了一个大坑吧！ 考虑到性能，那你在设计的时候会考虑到哪些问题了，下面将来详细的介绍。 设计表时要注意： 1 表字段避免null值出现，null值很难查询优化且占用额外的索引空间，推荐默认数字0代替null。 2 尽量使用INT而非BIGINT，如果非负则加上UNSIGNED(这样数值容量会扩大一倍)，当然能使用TINYINT、SMALLINT、MEDIUM\_INT更好。 3 使用枚举或整数代替字符串类型 4 尽量使用TIMESTAMP而非DATETIME 5 单表不要有太多字段，建议在20以内 6 用整型来存IP 索引 1 索引并不是越多越好，要根据查询有针对性的创建，考虑在WHERE和ORDER BY命令上涉及的列建立索引，可根据EXPLAIN来查看是否用了索引还是全表扫描 2 应尽量避免在WHERE子句中对字段进行NULL值判断，否则将导致引擎放弃使用索引而进行全表扫描 3 值分布很稀少的字段不适合建索引，例如"性别"这种只有两三个值的字段 4 字符字段只建前缀索引 5 字符字段最好不要做主键 6 不用外键，由程序保证约束 7 尽量不用UNIQUE，由程序保证约束 8 使用多列索引时主意顺序和查询条件保持一致，同时删除不必要的单列索引 简言之就是使用合适的数据类型，选择合适的索引 选择合适的数据类型 (1)使用可存下数据的最小的数据类型，整型 < date,time < char,varchar < blob (2)使用简单的数据类型，整型比字符处理开销更小，因为字符串的比较更复杂。如，int类型存储时间类型，bigint类型转ip函数 (3)使用合理的字段属性长度，固定长度的表会更快。使用enum、char而不是varchar (4)尽可能使用not null定义字段 (5)尽量少用text，非用不可最好分表 选择合适的索引列 (1)查询频繁的列，在where，group by，order by，on从句中出现的列 (2)where条件中<，<=，=，>，>=，between，in，以及like 字符串+通配符(%)出现的列 (3)长度小的列，索引字段越小越好，因为数据库的存储单位是页，一页中能存下的数据越多越好 (4)离散度大(不同的值多)的列，放在联合索引前面。查看离散度，通过统计不同的列值来实现，count越大，离散程度越高： sql的编写需要注意优化 1 使用limit对查询结果的记录进行限定 2 避免select *，将需要查找的字段列出来 3 使用连接(join)来代替子查询 4 拆分大的delete或insert语句 5 可通过开启慢查询日志来找出较慢的SQL 6 不做列运算：SELECT id WHERE age + 1 = 10，任何对列的操作都将导致表扫描，它包括数据库教程函数、计算表达式等等，查询时要尽可能将操作移至等号右边 7 sql语句尽可能简单：一条sql只能在一个cpu运算;大语句拆小语句，减少锁时间;一条大sql可以堵死整个库 8 OR改写成IN：OR的效率是n级别，IN的效率是log(n)级别，in的个数建议控制在200以内 9 不用函数和触发器，在应用程序实现 10 避免%xxx式查询 11 少用JOIN 12 使用同类型进行比较，比如用'123'和'123'比，123和123比 13 尽量避免在WHERE子句中使用!=或<>操作符，否则将引擎放弃使用索引而进行全表扫描 14 对于连续数值，使用BETWEEN不用IN：SELECT id FROM t WHERE num BETWEEN 1 AND 5 15 列表数据不要拿全表，要使用LIMIT来分页，每页数量也不要太大 引擎 目前广泛使用的是MyISAM和InnoDB两种引擎： MyISAM MyISAM引擎是MySQL 5.1及之前版本的默认引擎，它的特点是： 1 不支持行锁，读取时对需要读到的所有表加锁，写入时则对表加排它锁 2 不支持事务 3 不支持外键 4 不支持崩溃后的安全恢复 5 在表有读取查询的同时，支持往表中插入新纪录 6 支持BLOB和TEXT的前500个字符索引，支持全文索引 7 支持延迟更新索引，极大提升写入性能 8 对于不会进行修改的表，支持压缩表，极大减少磁盘空间占用 InnoDB InnoDB在MySQL 5.5后成为默认索引，它的特点是： 1.支持行锁，采用MVCC来支持高并发 2.支持事务 3.支持外键 4.支持崩溃后的安全恢复 5.不支持全文索引 总体来讲，MyISAM适合SELECT密集型的表，而InnoDB适合INSERT和UPDATE密集型的表 MyISA…

什么是MySQL锁？有哪些锁类型？如何使用？
type: item
date: 2021-02-18T14:48:00+08:00
description: 数据库锁知识不少人在开发的时候，应该很少会注意到这些锁的问题，也很少会给程序加锁(除了库存这些对数量准确性要求极高的情况下)，即使我们不会这些锁知识，我们的程序在一般情况下还是可以跑得好好的。因为这些锁数据库隐式帮我们加了，只会在某些特定的场景下才需要手动加锁。对于UPDATE、DELETE、INSERT语句，InnoDB会自动给涉及数据集加排他锁（X) MyISAM在执行查询语句SELECT前，会自动给涉及的所有表加读锁，在执行增、删、改操作前，会自动给涉及的表加写锁，这个过程并不需要用户干预表锁首先，从锁的粒度，我们可以分成两大类： 表锁 :开销小，加锁快；不会出现死锁；锁定力度大，发生锁冲突概率高，并发度最低 行锁:开销大，加锁慢；会出现死锁；锁定粒度小，发生锁冲突的概率低，并发度高 不同的存储引擎支持的锁粒度是不一样的==：InnoDB行锁和表锁都支持、MyISAM只支持表锁！InnoDB只有通过索引条件检索数据才使用行级锁==，否则，InnoDB使用表锁也就是说，InnoDB的行锁是基于索引的！表锁下又分为两种模式： 表读锁（Table Read Lock）&& 表写锁（Table Write Lock） 从下图可以清晰看到，在表读锁和表写锁的环境下：读读不阻塞，读写阻塞，写写阻塞！ 读读不阻塞：当前用户在读数据，其他的用户也在读数据，不会加锁 读写阻塞：当前用户在读数据，其他的用户不能修改当前用户读的数据，会加锁！ 写写阻塞：当前用户在修改数据，其他的用户不能修改当前用户正在修改的数据，会加锁！从上面已经看到了：读锁和写锁是互斥的，读写操作是串行。如果某个进程想要获取读锁，同时另外一个进程想要获取写锁。在mysql中，写锁是优先于读锁的！写锁和读锁优先级的问题是可以通过参数调节的：max_write_lock_count和low-priority-updates注：行锁InnoDB和MyISAM有两个本质的区别：InnoDB支持行锁、InnoDB支持事务InnoDB实现了以下两种类型的行锁：共享锁（S锁、读锁）：允许一个事务去读一行，阻止其他事务获得相同数据集的排他锁。即多个客户可以同时读取同一个资源，但不允许其他客户修改。排他锁（X锁、写锁)：允许获得排他锁的事务更新数据，阻止其他事务取得相同数据集的读锁和写锁。写锁是排他的，写锁会阻塞其他的写锁和读锁。另外，为了允许行锁和表锁共存，实现多粒度锁机制，InnoDB还有两种内部使用的意向锁（Intention Locks），这两种意向锁都是表锁：意向共享锁（IS）：事务打算给数据行加行共享锁，事务在给一个数据行加共享锁前必须先取得该表的IS锁。意向排他锁（IX）：事务打算给数据行加行排他锁，事务在给一个数据行加排他锁前必须先取得该表的IX锁。意向锁也是数据库隐式帮我们做了，不需要程序员关心！MVCCMVCC(Multi-Version ConcurrencyControl)多版本并发控制，可以简单地认为：MVCC就是行级锁的一个变种(升级版)。在表锁中我们读写是阻塞的，基于提升并发性能的考虑，MVCC一般读写是不阻塞的(很多情况下避免了加锁的操作)。 可以简单的理解为：对数据库的任何修改的提交都不会直接覆盖之前的数据，而是产生一个新的版本与老版本共存，使得读取时可以完全不加锁。事务的隔离级别事务的隔离级别就是通过锁的机制来实现，锁的应用最终导致不同事务的隔离级别，只不过隐藏了加锁细节，事务的隔离级别有4种：Read uncommitted：会出现脏读，不可重复读，幻读Read committed：会出现不可重复读，幻读Repeatable read：会出现幻读(Mysql默认的隔离级别，但是Repeatable read配合gap锁不会出现幻读！)Serializable：串行，避免以上的情况Read uncommitted： 出现的现象--->脏读：一个事务读取到另外一个事务未提交的数据，例子：A向B转账，A执行了转账语句，但A还没有提交事务，B读取数据，发现自己账户钱变多了！B跟A说，我已经收到钱了。A回滚事务【rollback】，等B再查看账户的钱时，发现钱并没有多...Read committed： 出现的现象--->不可重复读：一个事务读取到另外一个事务已经提交的数据，也就是说一个事务可以看到其他事务所做的修改，例如：A查询数据库得到数据，B去修改数据库的数据，导致A多次查询数据库的结果都不一样【危害：A每次查询的结果都是受B的影响的，那么A查询出来的信息就没有意思了】Repeatable read： 避免不可重复读是事务级别的快照！每次读取的都是当前事务的版本，即使被修改了，也只会读取当前事务版本的数据至于虚读(幻读)：是指在一个事务内读取到了别的事务插…

Mysql分库分表，你如何分，怎样分？
type: item
date: 2021-02-18T14:47:54+08:00
description: 为什么要分表和分区？ 日常开发中我们经常会遇到大表的情况，所谓的大表是指存储了百万级乃至千万级条记录的表。这样的表过于庞大，导致数据库在查询和插入的时候耗时太长，性能低下，如果涉及联合查询的情况，性能会更加糟糕。分表和表分区的目的就是减少数据库的负担，提高数据库的效率，通常点来讲就是提高表的增删改查效率。什么是分表？ 分表是将一个大表按照一定的规则分解成多张具有独立存储空间的实体表，我们可以称为子表，每个表都对应三个文件，MYD数据文件，.MYI索引文件，.frm表结构文件。这些子表可以分布在同一块磁盘上，也可以在不同的机器上。app读写的时候根据事先定义好的规则得到对应的子表名，然后去操作它。什么是分区？ 分区和分表相似，都是按照规则分解表。不同在于分表将大表分解为若干个独立的实体表，而分区是将数据分段划分在多个位置存放，可以是同一块磁盘也可以在不同的机器。分区后，表面上还是一张表，但数据散列到多个位置了。app读写的时候操作的还是大表名字，db自动去组织分区的数据。mysql分表和分区有什么联系呢？ 1.都能提高mysql的性高，在高并发状态下都有一个良好的表现。 2.分表和分区不矛盾，可以相互配合的，对于那些大访问量，并且表数据比较多的表，我们可以采取分表和分区结合的方式（如果merge这种分表方式，不能和分区配合的话，可以用其他的分表试），访问量不大，但是表数据很多的表，我们可以采取分区的方式等。 3.分表技术是比较麻烦的，需要手动去创建子表，app服务端读写时候需要计算子表名。采用merge好一些，但也要创建子表和配置子表间的union关系。 4.表分区相对于分表，操作方便，不需要创建子表。 我们知道对于大型的互联网应用，数据库单表的数据量可能达到千万甚至上亿级别，同时面临这高并发的压力。Master-Slave结构只能对数据库的读能力进行扩展，写操作还是集中在Master中，Master并不能无限制的挂接Slave库，如果需要对数据库的吞吐能力进行进一步的扩展，可以考虑采用分库分表的策略。 1.分表 在分表之前，首先要选中合适的分表策略（以哪个字典为分表字段，需要将数据分为多少张表），使数据能够均衡的分布在多张表中，并且不影响正常的查询。在企业级应用中，往往使用org\_id(组织主键)做为分表字段，在互联网应用中往往是userid。在确定分表策略后，当数据进行存储及查询时，需要确定到哪张表里去查找数据， 数据存放的数据表 = 分表字段的内容 % 分表数量 2.分库 分表能够解决单表数据量过大带来的查询效率下降的问题，但是不能给数据库的并发访问带来质的提升，面对高并发的写访问，当Master无法承担高并发的写入请求时，不管如何扩展Slave服务器，都没有意义了。我们通过对数据库进行拆分，来提高数据库的写入能力，即所谓的分库。分库采用对关键字取模的方式，对数据库进行路由。 数据存放的数据库=分库字段的内容%数据库的数量 3.即分表又分库 数据库分表可以解决单表海量数据的查询性能问题，分库可以解决单台数据库的并发访问压力问题 当数据库同时面临海量数据存储和高并发访问的时候，需要同时采取分表和分库策略。一般分表分库策略如下： 中间变量 = 关键字%（数据库数量*单库数据表数量） 库 = 取整（中间变量/单库数据表数量） 表 = （中间变量%单库数据表数量） **先谈谈分表的几种方式： 1、mysql集群** 事实它并不是分表，但起到了和分表相同的作用。集群可分担数据库的操作次数，将任务分担到多台数据库上。集群可以读写分离，减少读写压力。从而提升数据库性能。 2、自定义规则分表 大表可以按照业务的规则来分解为多个子表。通常为以下几种类型，也可自己定义规则。 1 Range（范围）–这种模式允许将数据划分不同范围。例如可以将一个表通过年份划分成若干个分区。 2 Hash（哈希）–这中模式允许通过对表的一个或多个列的Hash Key进行计算，最后通过这个Hash码不同数值对应的数据区域进行分区。例如可以建立一个对表主键进行分区的表。 3 Key（键值）-上面Hash模式的一种延伸，这里的Hash Key是MySQL系统产生的。 4 List（预定义列表）–这种模式允许系统通过预定义的列表的值来对数据进行分割。 5 composite（复合模式） –以上模式的组合使用 以聊天信息表为例： 我事先建100个这样的表，message\_00,message\_01,message\_02……….message\_98,message\_99.然后根据用户的ID来判断这个用户的聊天信息放到哪张表里面，你可以用hash的方式来获得，可以用求余的方式来获得，方法很多，各人想各人的吧。下面用hash的方法来获得表名：<?php functi…

MySQL中你是如何REGEXP正则表达式
type: item
date: 2021-02-18T14:47:49+08:00
description: 正则表达式描述了一组字符串。最简单的正则表达式是不含任何特殊字符的正则表达式。例如，正则表达式hello匹配hello。 非平凡的正则表达式采用了特殊的特定结构，从而使得它们能够与1个以上的字符串匹配。例如，正则表达式hello|word匹配字符串hello或字符串word。 作为一个更为复杂的示例，正则表达式B[an]*s匹配下述字符串中的任何一个：Bananas，Baaaaas，Bs，以及以B开始、以s结束、并在其中包含任意数目a或n字符的任何其他字符串。 以下是可用于随REGEXP操作符的表的模式。 应用示例，查找用户表中Email格式错误的用户记录：SELECT * FROM users WHERE email NOT REGEXP '^[A-Z0-9._%-]+@[A-Z0-9.-]+.[A-Z]{2,4}$' MySQL数据库中正则表达式的语法，主要包括各种符号的含义。 ##### （^）字符 匹配字符串的开始位置，如“^a”表示以字母a开头的字符串。mysql> select 'xxxyyy' regexp '^xx'; +-----------------------+ | 'xxxyyy' regexp '^xx' | +-----------------------+ | 1 | +-----------------------+ 1 row in set (0.00 sec) 查询xxxyyy字符串中是否以xx开头，结果值为1，表示值为true，满足条件。##### （$）字符匹配字符串的结束位置，如“X^”表示以字母X结尾的字符串。 ##### （.）字符这个字符就是英文下的点，它匹配任何一个字符，包括回车、换行等。 ##### （*）字符星号匹配0个或多个字符，在它之前必须有内容。如：mysql> select 'xxxyyy' regexp 'x*'; 这个SQL语句，正则匹配为true。 ##### （+）字符加号匹配1个或多个字符，在它之前也必须有内容。加号跟星号的用法类似，只是星号允许出现0次，加号则必须至少出现一次。 ##### （?）字符问号匹配0次或1次。 实例: 现在根据上面的表，可以装置各种不同类型的SQL查询以满足要求。在这里列出一些理解。考虑我们有一个表为person\_tbl和有一个字段名为名称： 查询找到所有的名字以'st'开头mysql> SELECT name FROM person_tbl WHERE name REGEXP '^st'; 查询找到所有的名字以'ok'结尾mysql> SELECT name FROM person_tbl WHERE name REGEXP 'ok$'; 查询找到所有的名字包函'mar'的字符串mysql> SELECT name FROM person_tbl WHERE name REGEXP 'mar'; 查询找到所有名称以元音开始和'ok'结束 的mysql> SELECT name FROM person_tbl WHERE name REGEXP '^[aeiou]|ok$'; 一个正则表达式中的可以使用以下保留字 ##### ^所匹配的字符串以后面的字符串开头mysql> select "fonfo" REGEXP "^fo$"; -> 0（表示不匹配） mysql> select "fofo" REGEXP "^fo"; -> 1（表示匹配） $所匹配的字符串以前面的字符串结尾mysql> select "fono" REGEXP "^fono$"; -> 1（表示匹配） mysql> select "fono" REGEXP "^fo$"; -> 0（表示不匹配） . 匹配任何字符（包括新行）mysql> select "fofo" REGEXP "^f.*"; -> 1（表示匹配） mysql> select "fonfo" REGEXP "^f.*"; -> 1（表示匹配） a*匹配任意多个a（包括空串)mysql> select "Ban" REGEXP "^Ba*n"; -> 1（表示匹配） mysql> select "Baaan" REGEXP "^Ba*n"; -> 1（表示匹配） mysql> select "Bn" REGEXP "^Ba*n"; -> 1（表示匹配） a+匹配任意多个a（不包括空串)mysql> select "Ban" REGEXP "^Ba+n"; -> 1（表示匹配） mysql> select "Bn" REGEXP "^Ba+n"; -> 0（表示不匹配） a?匹配一个或零个amysql> select "Bn" REGEXP "^Ba?n"; -> 1（表示匹配） mys…

写一手好SQL，你该从哪里入手？
type: item
date: 2021-02-18T14:47:36+08:00
description: 有时候我们会遇到：在查询sql的时候，假如有100w条数据，会出现慢sql告警，这个时候你就应该到处sql日志来查找原因了。这里很有可能的主要原因就是没有命中索引和没有分页处理（原因有很多种，主要分析你的日志）。那接下来我们就得去优化sql了。 **如何优化呢？下面我们来谈谈有关的问题。 一、从sql优化入手** 谈到sql性能优化，那我们就离不开谈到大数据量和并发数，MySQL没有限制单表的最大记录数，它只是取决于操作系统对文件大小的限制。看表： 从表中我们可以看出，如果单表行数超过500万行或者单表容量超过2GB，才推荐分库分表。性能由综合因素决定，抛开业务复杂度，影响程度依次是硬件配置、MySQL配置、数据表设计、索引优化。500万这个值仅供参考，并非铁律。 假如你在操作过超过4亿行数据的单表，你可以用分页查询，分页查询最新的20条记录耗时0.6秒，SQL语句大致是select field_1,field_2 from table where id < #{prePageMinId} order by id desc limit 20，prePageMinId是上一页数据记录的最小ID。 这个查询出来的查询速度还凑合，不过随着数据不断增长，有朝一日必定不堪重负。所以分库分表是个周期长而风险高的大活儿，应该尽可能在当前结构上优化，比如升级硬件、迁移历史数据等等，实在没辙了再分。最大并发数并发数是指同一时刻数据库能处理多少个请求，由max\_connections和max\_user\_connections决定。max\_connections是指MySQL实例的最大连接数，上限值是16384，max\_user\_connections是指每个数据库用户的最大连接数。 MySQL会为每个连接提供缓冲区，意味着消耗更多的内存。如果连接数设置太高硬件吃不消，太低又不能充分利用硬件。一般要求两者比值超过10%，计算方法如下：max_used_connections / max_connections * 100% = 3/100 *100% ≈ 3% 查看最大连接数与响应最大连接数如下：show variables like '%max_connections%'; show variables like '%max_user_connections%'; 在配置文件my.cnf中你可以修改最大连接数[mysqld] max_connections = 100 max_used_connections = 20 查询耗时0.5秒建议将单次查询耗时控制在0.5秒以内，0.5秒是个经验值，源于用户体验的3秒原则。如果用户的操作3秒内没有响应，将会厌烦甚至退出。响应时间=客户端UI渲染耗时+网络请求耗时+应用程序处理耗时+查询数据库耗时，0.5秒就是留给数据库1/6的处理时间。 二、数据库设计也是影响性能的关键 数据类型的选择原则：更简单或者占用空间更小。你可以这样设计： 1 如果长度能够满足，整型尽量使用tinyint、smallint、medium\_int而非int。 2 如果字符串长度确定，采用char类型。 3 如果varchar能够满足，不采用text类型。 4 精度要求较高的使用decimal类型，也可以使用BIGINT，比如精确两位小数就乘以100后保存。 5 尽量采用timestamp而非datetime。 相比datetime，timestamp占用更少的空间，以UTC的格式储存自动转换时区。 避免空值 MySQL中字段为NULL时依然占用空间，会使索引、索引统计更加复杂。从NULL值更新到非NULL无法做到原地更新，容易发生索引分裂影响性能。尽可能将NULL值用有意义的值代替，也能避免SQL语句里面包含is not null的判断。 text类型优化 由于text字段储存大量数据，表容量会很早涨上去，影响其他字段的查询性能。建议抽取出来放在子表里，用业务主键关联。 三、索引优化，这个经常谈到 索引的分类有哪些？ 1 普通索引：最基本的索引 2 组合索引：多个字段上建立的索引，能够加速复合查询条件的检索。 3 唯一索引：与普通索引类似，但索引列的值必须唯一，允许有空值 4 组合唯一索引：列值的组合必须唯一 5 主键索引：特殊的唯一索引，用于唯一标识数据表中的某一条记录，不允许有空值，一般用primary key约束。 6 全文索引：用于海量文本的查询，MySQL5.6之后的InnoDB和MyISAM均支持全文索引。由于查询精度以及扩展性不佳，更多的企业选择Elasticsearch。 索引优化 1 分页查询很重要，如果查询数据量超过30%，MYSQL不会使用索引。 2 单表索引数不超过5个、单个索引字段数不超过5…

为什么你写的sql查询慢？为什么你建的索引常失效？
type: item
date: 2021-02-18T14:47:00+08:00
description: 为什么你写的sql查询慢？为什么你建的索引常失效？ 通过本篇内容，你将学会MySQL性能下降的原因，索引的简介，索引创建的原则，explain命令的使用，以及explain输出字段的意义。助你了解索引，分析索引，使用索引，从而写出更高性能的sql语句。 案例分析 我们先简单了解一下非关系型数据库和关系型数据库的区别。 MongoDB是NoSQL中的一种。NoSQL的全称是Not only SQL，非关系型数据库。它的特点是性能高，扩张性强，模式灵活，在高并发场景表现得尤为突出。但目前它还只是关系型数据库的补充，它在数据的一致性，数据的安全性，查询的复杂性问题上和关系型数据库还存在一定差距。 MySQL是关系性数据库中的一种，查询功能强，数据一致性高，数据安全性高，支持二级索引。但性能方面稍逊与MongoDB，特别是百万级别以上的数据，很容易出现查询慢的现象。这时候需要分析查询慢的原因，一般情况下是程序员sql写的烂，或者是没有键索引，或者是索引失效等原因导致的。 公司ERP系统数据库主要是MongoDB（最接近关系型数据的NoSQL），其次是Redis，MySQL只占很少的部分。现在又重新使用MySQL，归功于阿里巴巴的奇门系统和聚石塔系统。考虑到订单数量已经是百万级以上，对MySQL的性能分析也就显得格外重要。 我们先通过两个简单的例子来入门。后面会详细介绍各个参数的作用和意义。 场景一：订单导入，通过交易号避免重复导单 业务逻辑：订单导入时，为了避免重复导单，一般会通过交易号去数据库中查询，判断该订单是否已经存在。 最基础的sql语句 查询的本身没有任何问题，在线下的测试环境也没有任何问题。可是，功能一旦上线，查询慢的问题就迎面而来。几百上千万的订单，用全表扫描？啊？哼! 怎么知道该sql是全表扫描呢？通过explain命令可以清楚MySQL是如何处理sql语句的。打印的内容分别表示： id : 查询序列号为1。 select\_type : 查询类型是简单查询，简单的select语句没有union和子查询。 table : 表是 itdragon\_order\_list。 partitions : 没有分区。 type : 连接类型，all表示采用全表扫描的方式。 possible\_keys : 可能用到索引为null。 key : 实际用到索引是null。 key\_len : 索引长度当然也是null。 ref : 没有哪个列或者参数和key一起被使用。 Extra : 使用了where查询。 因为数据库中只有三条数据，所以rows和filtered的信息作用不大。这里需要重点了解的是type为ALL，全表扫描的性能是最差的，假设数据库中有几百万条数据，在没有索引的帮助下会异常卡顿。 初步优化：为transaction\_id创建索引 这里创建的索引是唯一索引，而非普通索引。 唯一索引打印的type值是const。表示通过索引一次就可以找到。即找到值就结束扫描返回查询结果。 普通索引打印的type值是ref。表示非唯一性索引扫描。找到值还要继续扫描，直到将索引文件扫描完为止。(这里没有贴出代码) 显而易见，const的性能要远高于ref。并且根据业务逻辑来判断，创建唯一索引是合情合理的。 再次优化：覆盖索引 这里将select * from 改为了 select transaction\_id from 后 Extra 显示 Using index，表示该查询使用了覆盖索引，这是一个非常好的消息，说明该sql语句的性能很好。若提示的是Using filesort(使用内部排序)和Using temporary(使用临时表)则表明该sql需要立即优化了。 根据业务逻辑来的，查询结构返回transaction\_id 是可以满足业务逻辑要求的。 场景二，订单管理页面，通过订单级别和订单录入时间排序 业务逻辑：优先处理订单级别高，录入时间长的订单。 既然是排序，首先想到的应该是order by， 还有一个可怕的 Using filesort 等着你。 最基础的sql语句 首先，采用全表扫描就不合理，还使用了文件排序Using filesort，更加拖慢了性能。 MySQL在4.1版本之前文件排序是采用双路排序的算法，由于两次扫描磁盘，I/O耗时太长。后优化成单路排序算法。其本质就是用空间换时间，但如果数据量太大，buffer的空间不足，会导致多次I/O的情况。其效果反而更差。与其找运维同事修改MySQL配置，还不如自己乖乖地建索引。 初步优化：为order\_level,input\_date 创建复合索引 创建复合索引后你会惊奇的发现，和没创建索引一样？？？都是全表扫描，都用到了文件排序。是索引失效？还是索引创建失败…