目录
一、MySQL基础知识
1.1 表结构(create table)
- 字段名:名称如果是关键字,需要用单引号(如'name' varchar(32) )
- 字段类型
- 非空:not null
- 自增长:auto increment
- 默认值:'name' varchar(32) defaul 'zhangsan'
- 主键:primary key('id')
- 外键:foreign key
- 引擎:
- engine =lnnoDB # 使用lnnoDB引擎
- MyISAM # 不支持外键
- 数据库编码:default charset=utf8 # 数据库默认编码为utf-8
- 数据类型
- 数值:bit、tinyint、int、bigint ....
- 字符串:char、varchar、text、mediumtext、longtext、enum
- 时间
- date YYYY-MM-DD(1000-01-01/9999-12-31)
- time HH:MM:SS('-838:59:59'/'838:59:59')
- datetime YYYY-MM-DD HH:MM:SS
- 约束
- 数据库导出
- mysql -u root -p
- use phpwind
- mysqldump -u root -p phpwind >hpwind0731
- 数据库导入
- 先建立一个库
- use phpwind
- source /root/phpwind0731
1.2 常用的SQL操作
- DDL # 定义或改变表(TABLE)的结构,数据类型
主要是用于定义或改变表的结构,数据类型,表之间的链接和约束等初始化工作上,大多在建立表时使用,常用的命令有create、alter、drop等
- DML # 对数据库里的数据进行操作的语言
SELECT、UPDATE、INSERT、DELETE
- DCL # 数据权限控制
设置或更改数据库用户或角色权限的语句常用的是grant
二、问题SQL的定位
2.1 全面日志查询
路径:/etc/my.conf
[mysqld]
general_log=0 //1开 0关
general_log_file=c:\usr\local\mysql5\data\
2.2 慢SQL日志的生成、分析
2.2.1 配置文件在my.cnf
mysqld配置信息:
- slow global show_query_log=1 # 开启日志(1 代表开启日志 0 代表关闭日志)
- slow-query-log-file=c:/... # 慢日志要写在哪个目录下的文件中
- long_query_time=1 # 大于1秒就算慢SQL
- slow_query_log=1 # 1:代表慢日志的已开启 0:代表关闭
- log_output='FILE' # 慢日志展示的形式
命令行查询慢日志设置:
use phpwind; show variables like '%query%'; # 查看配置相关的信息
show variables like 'long_query_time%'; # 查看慢SQL设置的时间
命令行修改慢日志设置:
set global slow_query_log =0;
set global long_query_time=1;
修改后,只在当前数据库生效。mysql重启后,会去读my.cnf配置文件的信息
慢日志分析工具:mysqldumpslow
s:是表示按照何种方式排序
c:访问次数
i:锁定时间
r:返回记录
t:查询时间
al:平均锁定时间
ar:平均返回记录数
at:平均查询时间
t:即为返回前面多少条数据
g:后边搭配一个正则匹配模式,大小写不敏感
mysqldumpslow -s c -t 20 xxx-slow.log # 访问次数最多的20个sql语句
mysqldumpslow -s r -t 20 xxx-slow.log # 返回记录集最多的20个sql
mysqldumpslow -s t -t 10 -g "left join" /data/mysql/mysql-slow.log # 得到按照时间排序的前10条里面含有做了连接的查询SQL
2.2.2 慢SQL分析
explain select count(*) from pw_bbs_threads; # 查询语句前面加一个 eplain 来解析SQL语句
结果各字段简单描述:
- id:sql执行顺序的表示从大到小执行,同数值从下到上执行
- select_type
- table:对应的是表名,有可能是中间表或别名表
- type:索引
- possible_keys
- key:使用到的索引
- key_len:索引的长度
- ref
- rows
- extra:扩展执行顺序
2.3 进程列表查询
show processlist; # 查询数据库的瞬时操作,查看当前连接运行的情况
结果各字段简单描述:
- id:一个标识,kill一个语句的时候很有用 (如 kill 11)
- user:显示当前用户,如果不是root,这个命令就只显示你权限范围内的sql语句
- host:显示这个语句是从哪个端口上发出的,可用来追踪出问题语句的用户
- db:显示这个进程目前连接的是哪个数据库
- command:显示当前连接的执行命令,一般就是休眠(sleep),查询(query),连接(connect)
- time:这个状态持续的时间,单位是秒
- state(重要):显示使用当前连接的sql语句的状态,很重要的列,后续会有所有的状态的描述,state只是语句执行中的某一个状态,一个SQL语句,已查询为例,可能需要经过copying to tmptable,sorting result, sending data等状态才可以完成
- 状态为:sleeping,代表资源未释放
- 状态为:copying to tmptable on disk 临时表从内存存储转为磁盘存储,会导致大量的IO压力,频繁出现需要优化
- 状态为:locked 代表查询锁住了或有更新操作锁定(更新操作需要使用正确的索引)
- 状态为:sorting for group 正在处理select查询的记录,如果sending data连接过多,通常是某查询的影响结果集过大,需要优化
- 状态为:reading from net 如大量出现,迅速检查数据库到前端的网络状态和流量
- info:显示这个SQL语句,因为长度有限,所以长的SQL语句就显示不全,但是一个判断问题语句的重要依据
每一条记录代表一个数据库连接数。
2.4 SQL运行的步骤
- 1. 检查SQL的语法、语义是否正确
- 2. 查询缓存中是否有执行计划(优化器中是否有历史记录)
- 3. 如果没有执行,生成新的执行计划
- 4. 运算,合并结果
软解析(执行步骤1,2,4),查询缓存(library cache)中有执行计划
硬解析(步骤1,2,3,4),查询缓存(library cache)中没有执行计划,需要耗用CPU资源
where 条件,mysql 是从最右侧进行条件匹配,可以对条件的顺序进行调整优化。
2.5 执行计划的查看
2.5.1 SQL的执行计划
2.5.2 explain 命令分解
2.5.2.1 ID
SQL执行的顺序的标识,从大到小的执行,同数值从下到上执行
- 如上图,id相同说明是同时执行,从下往上执行
- 如上图,如果id的顺序是2,1。先执行第2行的语句,在执行第一行的语句
2.5.2.2 select_type
SIMPLE:最简单的查询语句,没有子查询或者多表查询
- explain select * from pw_bbs_threads where tid=59628\G;
PRIMARY:为复杂查询而建立的首要表,通常在Derived 和 Union中看到
- explain select max(tid) from (select tid from pw_bbs_threads where fid = '3' ) c;
Union: UNION语句中的一个元素(UNION 作用是将多个select语句的结果整合到一个结果中返回)
- explain select a.name from company_a a union select b.name from company_b b;
UNION RESULT: UNION 的结果集
DEPENDENT SUBQUERY: 子查询的结果集
- explain select p.* from pw_bbs_threads p where p.tid not in (select tid from pw_bbs_posts c) ;
DERIVED:驱动表,或者中间表,只是一个临时存放数据的表
- explain select max(tid) from (select tid from pw_bbs_threads where fid = '3' ) c;
2.5.2.3 table
可能是表名、表的别名,或者一个为查询产生的临时表。
2.5.2.4 type(重点关注)
特殊场景:
- System:表仅有一行(系统表),这是const联接类型的一个特例
- ref_or_null:该联接类型如同ref,但是添加了MySQL可以专门搜索包含NULL值的行。在解决子查询中经常使用该联接类型的优化
- index_merge:该联接类型表示使用了索引合并优化方法。在这种情况下,key列包含了使用的索引的清单,key_len包含了使用的索引的最长的关键元素
- index_subquery:可以替换IN子查询,但只适合下列形式的子查询中的非唯一索引
- unique_subquery:是一个索引查找函数,可以完全替换子查询,效率更高
常见场景(以下字段,越往下,执行效率越低):
const:表最多有一个匹配行,它将在查询开始时被读取。因为仅有一行,在这行的列值可被优化器剩余部分认为是常数。const表很快,因为它们只读取一次!const用于用常数值比较PRIMARY KEY或UNIQUE索引的所有部分
- explain select * from pw_bbs_threads WHERE tid=59628;
- 执行效率最高,数据在列表中具有唯一性
eq_ref:对于每个来自于前面的表的行组合,从该表中读取一行。这可能是最好的联接类型,除了const类型。它用在一个索引的所有部分被联接使用并且索引是UNIQUE或PRIMARY KEY
eq_ref可以用于使用" = "操作符比较的带索引的列。比较值可以为常量或一个使用在该表前面所读取的表的列的表达式。
- 与ref比较,没有本质上的区别(数据量大会用eq_ref,数据量小会用ref)
- 需要在索引列中进行使用
ref:对于每个来自于前面的表的行组合,所有有匹配索引值的行将从这张表中读取。如果联接只使用键的最左边的前缀,或如果键不是UNIQUE或PRIMARY KEY(换句话说,如果联接不能基于关键字选择单个行的话),则使用ref。
如果使用的键仅仅匹配少量行,该联接类型是不错的。ref可以用于使用" = "或" <=> "操作符的带索引的列
- explain select * from pw_bbs_threads t ,pw_bbs_forum f where t.fid = f.fid;
range:只检索给定范围的行,使用一个索引来选择行。key列显示使用了哪个索引。key_len包含所使用索引的最长关键元素。在该类型中ref列为NULL。当使用" = "、" <> "、" > "、" >= "、" < "、" <= "、" IS NULL "、" <=> "、" BETWEEN "或者" IN "操作符,用常量比较关键字列时,可以使用range
- explain SELECT * FROM pw_bbs_threads WHERE tid IN ('59638','9642', '59641', '59640', '59639', '59637', '59636', '59635');
index:索引树被全扫描。这通常比ALL快,因为索引文件通常比数据文件小
ALL:全表扫描,很不好
- explain select t.* from pw_bbs_threads t ,pw_bbs_forum f where f.parentid = '2' ;
内连接(数据量大的情况下,避免内连接),尽量用left join、right join 进行优化和替换,提高效率。原则上避免,但实际工作中,主要还是关注数据量。
2.5.2.5 possible_keys(重点关注)
查询中用到的索引,如果一个列值多于3个,则说明引用的索引太多,或者存在一个无效的索引。
- 可能会用到的索引
2.5.2.6 Key(重点关注)
key列显示MySQL实际决定使用的键(索引)。如果没有选择索引,键是NULL
- 真正用到的索引
2.5.2.7 Key_len(重点关注)
计算组合索引,索引键的长度,用于确认索引的有效性,及多列索引中用到的索引的数量非常重要
2.5.2.8 ref
对索引进行解析
2.5.2.9 rows
显示MySQL认为它执行查询时必须检查的行数,采用嵌套算法,如第一列查出需要2行,第二列查出有10行,则数值为20,值越大,表示查询用到的数量越多
2.5.2.10 extra(扩展)
using where:
使用了where查询条件,但是没有使用到索引列,需要从表数据中去获取结果
using join buffer:
使用表链接时,没有使用索引,需要用缓冲区来保存结果(中间的值放在buffer里),一般情况下不需要处理(需要注意数量级,内存是有限的)
内存满掉的结果: buffer ---> freesort(由从内存中取值,变为取物理磁盘上的值)
using filesort:
从磁盘读取,使用文件IO来存储中间过程数据,建议做优化处理(加索引)
using temporary:
使用内部临时表来存储中间过程数据,通常在order by 或者 group by 条件中使用
DBA和架构师层面对数据库方面的优化的思考:
基于业务和表结构的优化,这个过程的优化效果是最大的,SQL优化效果和成本并没有想象中的 那么大。连表查询,最大3 ~ 5张表,超过我们就需要从业务的角度思考,有没有这个必要做这个查询,或者优化表结构或业务架构。
三、SQL并发问题
3.1 锁
BDB存储引擎采用的是页面锁,也支持表级锁。
myISAM存储引擎采用的是表级锁。从锁的角度来说,表级锁更适合于以查询为主,只有少量按索引条件更新数据的应用,如web应用
表级锁有两种模式:
- 表共享读锁(table read lock)
- 表独占写锁(table writeLock)
读锁:不会阻塞其他进程对同一表的读请求,但会阻塞对同一表的写请求(只有当读锁释放后,才会执行其他进程的写操作)
写锁:会阻塞其他进程对同一表的读和写操作,只有当写锁释放后,才会执行其他进程的读写操作
表级锁:开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低。
innoDB存储引擎支持行级锁,也支持表级锁,但默认情况下采用的是行级锁
行级锁更适合于有大量索引条件并发更新少量不同数据,同时又有并发查询的应用,如一些在线事务处理(OLTP)系统
行级锁:开销大,加锁慢;会出现死锁;锁定粒度最小,发生所冲突的概率最低,并发度也最高。
show status like 'table%'; # 分析系统上的表锁定争夺
- table_locks_waited
- table_locks_immediate
如果table_locks_waited的值比较高,则说明存在着较严重的表级锁争用的情况。
3.2 事务
3.3 并发引起的问题
3.4 其它锁问题
四、MYSQL基本分析命令
explain # 查看SQL的执行计划
show create table t1; # 查看表的建表语句
show table status; # 显示表的相关信息
show status # 整体状态的查看
show varlables # 查看当前的配置
show indexes (索引相关的内容)
在通过explain 命令查看索引是否失效(如语句中使用了“like”)
show index from tables; # 查看索引
结果列说明:
Non_unique:如果索引不能包括重复词,则为0,如果可以则为1
key_name:索引的名称
seq_in_index:索引中的列序列号,从1开始
column_name:列名称
collation:列以什么方式存储在索引中。在mysql中,有值‘A’(升序)或NULL(无分类)
cardinality:索引中唯一值的数目的估计值
sub_part:如果列只是被部分地编入索引,则为被编入索引的字符的数目。如果整列被编入索引,则为null
packed:指示关键字如何被压缩,如果没有被压缩,则为NULL
NULL:如果列含有NULL,则为YES,否则为NO
index_type:用过的索引方法(Btree\fulltext\hash\Rtree)
五、索引
5.1 索引的类型及作用
索引是一种特殊的文件(lnnoDB数据表上索引是表考核关键的一个组成部分),他包含对数据表所有的记录的引用指针。数据库索引好比是一本书前面的目录,能加快数据库的查询速度。
索引的作用:
- 保证数据完整性
- 优化数据访问性能
- 改进表连接操作
- 对结果进行排序
- 简化聚合数据操作
5.2 索引的存储类型
FULLTEXT:全文索引
他的出现是为了解决where name like '%Word%'这类针对文本的模糊查询,效率较低的问题。
BTREE:二叉树(mysql默认的储存类型)
存入一个树形的数据结构,每次查询都是从树的入口root开始,一次遍历node,获取leaf。
HASHhash:哈希(目前比较常用)
一种 key => value 形式的键值,对可以一次定位,不需要像树形索引那样逐层查找,因此具有极高的效率。目前更多的用来校验数据的唯一性,效率快。
使用哈希索引存储类型也存在一些问题:
- 仅能满足“=” “in” “<=>” 查询,不能满足范围的查询
- 无法被用来避免数据的排序操作
- 任何时候都不能避免表扫描
- 遇到大量hash值相等的情况后,性能并不一定会比B-Tree索引高
5.3 索引类型
5.3.1 单列索引
PRIMRY KEY(主键索引):建立一张表,建了主键,系统或主动加上索引,不需添加。一个表中只会有一个主键索引
- alter table 'table_name' add PRIMARY KEY('column');
UNIQUE(唯一索引):数列里面的值只有一个唯一值。唯一索引在表中可以有多个,值是唯一的,作用是保证数据的完整性
- alter table 'table_name' add unique ('column');
INDEX(普通索引):给普通的列添加索引
- alter table 'table_name' add index_name ('column');
5.3.2 多列索引(组合索引)
使用组合索引,第一个列必须出现查询组语句中,这个索引才会被用到,不然会被失效。
最左前缀原则:组合索引的第一个字段必须出现在查询组句中,这个索引才会被用到,创建多列索引时,要根据业务需求,where子句中使用最频繁的一列放在最左边。
show index from pw_bbs_threads # 查看索引
alter table pw_bbs_threads drop index XXX # 删除索引
alter table pw_bbs_threads add index index_fid(fid) # 增加索引
单列索引与多列索引的区别:
- 多列索引可以更有效的利用第一列索引的查询结果
- 索引建立得越多就越占磁盘空间,在更新数据的时候速度会更慢
5.4 索引的作用
利用索引闲置查询读取的行数,利用索引连接表
select * from pw_bbs_threads LEFT JOIN pw_bbs_forum USING (fid) where fid =8;
给fid建立索引后:
5.5 索引的缺点
影响inset语句的执行,每次插入新数据,都需要更新索引。
当表数据较大时,生成索引需要较长的时间,此时的数据是不可用的,再生产环境执行时,需要特别的注意。
索引需要空间,组合索引如果较大,会占用较多的物理空间 (从性能角度来看,也算不上缺点)
5.6 索引的优化(空间换时间)
索引优化原理:让SQL少做事,让SQL少找数据
- 优化where
- 优化 or
- 优化 is null
- 优化distinct
- 优化 left join
- 优化 order by
- 优化 limit
5.7 性能调优的原则
更多的是业务与资源之间找平衡,或者是资源与资源之间找平衡,又或是程序之间的平衡。
六、SQL调优
6.1 索引失效
- 使用 not like 会使索引失效
- 使用between 不会使索引失效(between比较的特殊)
- 使用 like“%xxx”会让索引失效
- 使用 like“xxx%”索引是不会失效
6.2 常见的表连接方式
内链接:inner join \,\where条件
左链接:left join
右链接:right join
处理表链接的方式,MySQL只有一种:Block Nestde loop,因为只有一种,索引显得特别重要,通过中间表和其他查询条件,进行一 一的匹配。
A、B表连表查询,如果不走索引,会进行4 * 8 = 32次匹配。拿到结果,放在tmp(中间表),在于C表进行一 一对应的匹配
mysql中,中间表的选择是非常重要,他决定了遍历数据的次数和效率。所以,我们尽量在驱动表(where条件最靠右的表)进行按列查询(建立索引)
mysql中的where执行顺序,先执行最右的条件,从右往左执行。大量数据的表放在最右边(第一张表),优先进行过滤。
6.3 发现mysql有问题的手段
- 1. 慢SQL日志,找到执行慢的SQL
- 2. explain 分析慢日志
- 3. show status确认数据库的相关状态
- 4. show processlist 定位有问题的SQL,并kill
6.4 常见优化实例
性能测试的优化的本质是:空间换时间,提高系统效率。
案例1:数据库表结构设计不合理
一张表只有两个字段,一个id,一个txt,但是txt 中存储的是一段业务日志,近10KB的数据。存储很快,但是对数据的读取的影响将会很大
一个表,有20个字段。性能很差,需要对表进行分表处理(对不常用的字段进行独立出来)
案例2:数据库IO错误
很多数据库放置在可用性很差的磁盘上,还有很多数据库的指定的磁盘不正确,这样只是配置了磁盘空间而没有考虑IO带宽。如果1条数据 10KB ,TPS 1000(预估IO带宽需要达到10M/s以上才可能达到1000TPS)。而然每个TPS,还需要操作3次库,以3M/s的速度消耗磁盘,此时需要优化磁盘文件类型。
案例3:多次不必要的连接数据库操作
有一个订单表的操作,程序先在表中增加一条订单记录,然后等待支付系统的返回,在更新数据状态(2次),一个简单的订单入库需要做3次的数据操作。
业务层面优化:增加订单记录时,将数据放入redis缓存,等更新数据状态时,在进行入库的数据操作(Redis设置入库时间,超过时间就不在进行入库操作。 redis可以做负载均衡,只是对账比较麻烦)。
案例4:某些SQL出现慢查询中,但是单独执行的时候确很快(单独执行很快,为什么出现在慢查询日志中?)
通过以下思路进行问题的排查:
1. 变量变更绑定失效(软解析、硬解析的问题)
2. 系统资源瓶颈。在执行的时候刚好CPU处于繁忙状态? IO有问题? 网络有问题?
3. 数据库状态
- 1. 查看表锁的数量 show status like 'bdb_max_lock';
- 2. 查看立即获得的表的锁的次数 show status like 'table_locks_immediate';
- 3. 查看不能立即获得的表的锁的次数,如果该值较高,并且有性能问题,你应首先优化查询,然后拆分表或使用复制 show status like 'table_locks_waited';
4. 缓存、开销上的差异。客户端缓存、连接开销、线程的争用
6.5 SQL调优
show 相关命令
explain
index
单SQL优化
事务相关 锁(表锁与行锁 、读锁/写锁、 共享锁/排它锁)
6.5.1 数据库配置
线程的递进关系
max_connections =1000 # MySQL的最大连接数
不能盲目提高设置,需要建立在机器能支撑的情况下。连接数越多,介于mysql会为每个连接提供连接缓冲区,就会开销越多的内存。可以根据当前状态的连接数量('conn%'通配符查看),以定夺该值得大小。
query_cache_size =8M # MySQL的查询缓存大小,对于同样的select语句,将直接从缓冲区中读取结果
query_cache_size 设置是否合理?
如果Qcache_lowmem_prunes 的值非常大,则表明出现缓冲不够的情况
如果Qcache_hits 的值也非常大,则表明缓冲使用非常频繁,此时需要增加缓冲大小
如果Qcache_hits 的值不大,则表明你的查询重复率很低,这种情况下使用查询缓冲反而会影响效率,那么可以考虑不用查询缓冲
select 语句中加入SQL_NO_CACHE可以明确表示不使用查询缓冲
6.5.2 表级优化(业务层优化)
6.5.3 大数据的优化(分库分表、读写分离、大数据库)
6.5.4 库表切分(针对千万级别的表,进行优化)
库表的水平切分(最常见)
特点:
- 水平切分与垂直切分相比,相对来说稍微复杂一些
- 需要将同一个表中的不同数据拆分到不同的数据库中
- 对于应用程序来说,拆分规则本身就较根据表名来拆分更为复杂,后期的数据维护也会更为复杂一些
一张表,拆分为多张表。根据user_id 或者 时间,进行水平拆分,需要改业务。以前1KW的数据放在同一张表,现在将1KW的数据放在多张表中,这样单表的数据就开始下降,数据查询性能提升。
库表的垂直切分
特点:
- 规则简单,实施也更为方便,尤其适合各业务之间耦合度非常低,相互影响很小,业务逻辑非常清晰的系统
- 这种系统中,可以很容易做到将不同业务模块所使用的表拆分到不同的数据库中
- 根据不同的表来进行拆分,对应用程序的影响也很小,拆分规则也会比较简单清晰
综合处理: 先垂直切分,再水平切分
从而将整个数据库切分成一个分布式矩阵
切分后的一些问题:拆分,不影响数据的一致性问题。拆分后,对于单表查询肯定是会提升性能。但对于多表查询,如使用group by \ left join 等查询,还是会影响性能,可以继续优化
- 通过框架进行解决与优化
- LVS负载均衡集群架构
- Alter 数据库框架
6.5.5 主从库复制原理
主库将所有的数据更改都记录到二进制日志,从库通过I/O线程把主库的二进制日志复制到备库的中继日志。从库通过SQL线程读取并应用到中继日志。
复制的延迟问题:
mysql只支持异步复制,由于只有一个SQL进程,主库的并行更新在从库上只能串行执行,这是造成延迟的根本原因。
6.5.6 数据的读写分离
使用replication 技术 进行主从数据同步、写只能在主库、从库用于分担读压力,读可横向扩展(读:毫秒级 阿里:可以做到微妙级)。减少IO的利用,读可以很方面的进行横向扩展。
七、存储过程
7.1 概述
存储过程简单来说,就是为以后的使用而保存的一条或多条MySQL语句的聚合。存储过程就是业务逻辑和流程的集合,可以在存储过程中创建表,更新数据,删除数据等。
普通业务逻辑尽量不要使用存储过程,定时性的ETL任务或报表统计函数可以根据团队资源采用存储过程处理(报表性数据库)
写一个存储过程来删除另一个存储过程是不被允许的
7.2 存储过程缺点
1. 不可移植性。每种数据库的内部编码都不太相同,当你的系统需要兼容多种数据库时最好不要用存储过程
2. 学习成本高。DBA擅长写存储过程,但不是每个程序员都能写好。后期坑很多,很大的维护成本
3. 业务逻辑存在多处处理的问题。采用存储过程后也就意味着你的系统有一些业务逻辑不是在应用程序中处理。这种架构会增加一些系统维护和调试成本(储存过程无法单步调试)
4. 存储过程和常用应用程序语言不一样。他支持的函数及语法有可能不能满足需求,有些逻辑就只能通过应用程序处理
5. 如果存储过程中有复杂运算的话,会增加一些数据库服务端的处理成本。对与集中式数据库可能会导致系统可拓展性问题
6. 为了提高性能,数据库会把存储过程代码编译成中间运行代码(类似于Java的class文件),更像静态语言。当存储过程引用对象(表、视图等)结构改变后,存储过程需要重新编译才能生效。一般都是在线变更构造的,所以在变更的瞬间要同时编译存储过程,这可能导致数据库瞬间压力上升引发故障。
7.3 存储过程建立及调用
create PROCEDURE test1() # 创建存储过程
BEGIN
select * from pw_acloud_apis;
END
call test1(); # 调用
drop procedure if exists test1; # 删除存储过程
show create procedure test1; # 显示某一个存储过程的详细信息
show procedure status; # 显示数据库中所有存储的存储过程基本信息
7.4 变量的使用及传递
7.4.1 输出存储过程查询到的值
存储过程只能接收一个值,如果需要达到多个值,可以通过游标来控制。
drop PROCEDURE if exists pl_test_out;
create PROCEDURE pl_test_out(out student_name VARCHAR(50)) # 入参的名字,不能跟列名一样,不然查询无结果
BEGIN
select s.name into student_name from student_info s limit 1; # 只能输出一个值
END;
call pl_test_out(@student_name); # 存储过程只能接收一个值
select @student_name; # 输出存储过程结果,存储过程没有办法返回结果集
drop PROCEDURE if exists pl_test_out; # 入参也可以是两个字段
create PROCEDURE pl_test_out(out student_name VARCHAR(50),out student_f float(50,0))
BEGIN
select s.name into student_name from student_info s limit 1;
select s.Fraction into student_f from student_info s limit 1;
END;
call pl_test_out(@student_name,@student_f);
select @student_name,@student_f;
drop PROCEDURE if exists pl_test_in; # 将limit 行数改成where条件,需要传一个in参数来实现
create PROCEDURE pl_test_in(out student_name VARCHAR(50),out student_f float(50,0),in student_id int)
BEGIN
select s.name into student_name from student_info s where s.id = student_id;
select s.fraction into student_f from student_info s where s.id = student_id;
END;
call pl_test_in(@student_name,@student_f,4);
select @student_name,@student_f;
7.4.2 变量的定义
drop procedure if exists pl_test_check;
create procedure pl_test_check(out checkresult varchar(50),in student_id int)
begin
declare v1 float(50,0); # 对变量进行赋值(另一种方式:set @a ='一个新变量')
set student_id =student_id +1; # 对变量进行运算
select s.fraction into v1 from student_info s where s.id = student_id;
# select student_id; # 查询变量的值,作用于调试
# select v1;
if v1 > 60 then
set checkresult = 'PASS';
else
set checkresult = 'FAIL';
end if;
end;
call pl_test_check(@checkresult,3);
select @checkresult; # into 输出值关键字
7.5 if 、case、loop、while的使用
# 使用语法
if .... end if;
case when ... when ... end case;
...
7.6 游标
游标实际上是一种从包括多条数据记录的结果集中每次提取一条记录的机制。游标充当与指针的作用,尽管游标能遍历结果中的所有行,但他一次只指向一行,游标的作用就是用于对查询数据库所返回的记录进行遍历,以便进行对应的操作。
步骤:
1. 声明一个游标:declare 游标名称cursor for table select语句 #这里的table可以是你查询出来的任意集合
2. 打开定义的游标: open 游标名称
3. 获得下一行数据:fetch 游标名称 into testrangeid, versionid;
4. 需要执行 的语句(增删改查):这里视具体情况而定
5. 释放游标:close 游标名称;
7.7 存储过程批量造数据
####随机生成4条相同参数的数据######
# 造数前,需要准备一些插入的随机数对应使用到的函数
select rand_string(); # 随机生成字符串(函数为自定义)
select CONCAT('zhangsan',rand_string()); # 随机姓名
select CEILING(RAND()*2); # 随机性别
select SYSDATE(); # 当前系统时间
select DATE_SUB(SYSDATE(),INTERVAL CEILING(RAND())*30 YEAR); # 与当前系统时间相差三十年以内
# 存储过程
drop procedure if exists inser_test;
create procedure inser_test(in number int)
begin
DECLARE i int DEFAULT 0;
WHILE i < number DO
INSERT INTO procedure_test ( `test_name`, `test_sex`, `test_start_time`, `test_birthday`, `test_study_no`, `test_study_LV` )
VALUES
( 'zhangsan_aaa', '1', '2021-9-8 16:45:12', '1991-9-8 00:00:00', '10000001', 'C0' );
set i = i + 1;
END WHILE;
end;
call inser_test(4);
select * from procedure_test;
#####随机生成100W不同参数的数据【将插入值,用函数进行参数化】 ######
# 造数前,需要准备一些插入的随机数对应使用到的函数
select rand_string(); # 随机生成字符串(函数为自定义)
select CONCAT('zhangsan',rand_string()); # 随机姓名
select CEILING(RAND()*2); # 随机性别
select SYSDATE(); # 当前系统时间
select DATE_SUB(SYSDATE(),INTERVAL CEILING(RAND())*30 YEAR); # 与当前系统时间相差三十年以内
# 存储过程
drop procedure if exists inser_test;
create procedure inser_test(in number int)
begin
DECLARE i int DEFAULT 0;
DECLARE max int;
select max(p.test_study_no) into max from procedure_test p;
select max; # 查询数据中最大编号,在插入数据时进行+1赋值
WHILE i < number DO
INSERT INTO procedure_test ( `test_name`, `test_sex`, `test_start_time`, `test_birthday`, `test_study_no`, `test_study_LV` )
VALUES
(CONCAT('zhangsan',rand_string()),CEILING(RAND()*2),SYSDATE(),DATE_SUB(SYSDATE(),INTERVAL,CEILING(RAND())*30 YEAR),max+1,CONCAT('pp',CEILING(RAND()*2)-1);
set i = i + 1;
END WHILE;
end;
call inser_test(4);
select * from procedure_test;
八、生产全链路压测
业务日志回放。在Nginx层加一层拦截,service所有的请求,产生的业务日志存放在一台服务器中将业务日志在测试环境进行回放,施加压力,到达性能测试的目的。
影子系统。通过type来控制,比如 type =1 走正常的生产环境及数据,type =2 是模拟出来的数据,走虚拟出来的压测服务器(虚拟出来的Nginx及服务器,配置和生产一模一样)
业务平移。