mysql可重复读解决了什么
作者:宁愿juejin.im/post/5c9040e95188252d92095a9e引言之前在深入了解数据库理论的时候,了解到事务的不同隔离级别可能存在的问题。为了更好的理解所以在MySQL数据库...
2024.11.12information_schema.tables 元数据? ----> “基表”(无法直接查询和修改的)----> DDL 进行元数据修改----> show ,desc(show),information_schema(全局类的统计和查询)
use information_schemadesc tables;
TABLE_SCHEMA 表所在的库 TABLE_NAME 表名ENGINE 表的存储引擎 TABLE_ROWS 表的行数AVG_ROW_LENGTH 平均行长度INDEX_LENGTH 索引的长度
-- information_schema
--- 查询整个数据库中所有的库对应的表名例如:world city world country oldboy oldguo
select table_schema,table_name FROM information_schema.tables;
information_schema INNODB_SYS_FOREIGNinformation_schema INNODB_SYS_TABLESTATSliexin_crmv2 lie_approve_resultliexin_crmv2 lie_change_recordliexin_crmv2 lie_companyliexin_crmv2 lie_company_terminal_infoliexin_crmv2 lie_invoice_approveliexin_crmv2 lie_taskliexin_crmv2 lie_userliexin_crmv2 lie_user_feedback
--- 查询world和school库下的所有表名
select table_schema,table_name FROM information_schema.tables WHERE table_schema=‘world‘unionALL select table_schema,table_name FROM information_schema.tables WHERE table_schema=‘school‘;
--- 查询整个数据库中所有的库对应的表名,每个库显示成一行
select table_schema,GROUP_CONCAT(table_name) FROM information_schema.tablesGROUP BY table_schema;
--- 统计一下每个库下的表的个数
select table_schema,COUNT(table_name) FROM information_schema.tablesGROUP BY table_schema;
--- 统计一下每个库的真实数据量
每张表数据量=AVG_ROW_LENGTH*TABLE_ROWS+INDEX_LENGTHSELECTSUM(AVG_ROW_LENGTH*TABLE_ROWS+INDEX_LENGTH)/1024/1024 AS total_mbFROM information_schema.TABLES
--- information_schema.tables+concat(),拼接命令
--- 使用方法举例mysql> select concat(USER,"@","‘",HOST,"‘") FROM mysql.user;--- 生产需求1mysqldump -uroot -p123 world city >/tmp/world_city.sql--- 模仿以上命令,对整个数据库下的1000张表进行单独备份,--- 排除sys,performance,information_schema
mysqldump -uroot -p123 world city >/tmp/world_city.sql
select concat("mysqldump -uroot -p123 ",table_schema," ",table_name," >/tmp/",table_schema,"_",table_name,".sql") FROM information_schema.tables WHERE table_schema NOT IN(‘sys‘,‘performance‘,‘information_schema‘)INTO outfile ‘/tmp/bak.sh‘;
vim /etc/my.cnf secure-file-priv=/tmp/etc/init.d/mysqld restart
--- 例子:模仿以下语句,批量实现world下所有表的操作语句生成
alter table world.city discard tablespace;select concat("alter table ",table_schema,".",table_name,"discard tablespace;") from information_schema.tables where table_schema=‘world‘into outfile ‘/tmp/discard.sql‘;
5. showshow databases; 查看所有数据库名show tables; 查看当前库下的表名show tables from world; 查看world数据库下的表名show create database 查看建库语句show create table 查看建表语句show grants for root@‘localhost‘ 查看用户权限信息show charset 查看所有的字符集show collation 查看校对规则show full processlist 查看数据库连接情况show status 查看数据库的整体状态show status like ‘%lock%‘ 模糊查看数据库的整体状态show variables 查看数据库所有变量情况show variables like ‘%innodb%‘ 查看数据库所有变量情况show engines 查看所有支持存储引擎show engine innodb status 查看所有innodb存储引擎状态情况show binary logs 查看二进制日志情况 show binlog events in 查看二进制日志事件 show relaylog events in 查看relay日志事件show slave status 查看从库状态show master status 查看数据库binlog位置信息show index from 查看表的索引情况
6. 索引创建100万数据
create table t100w (id int,num int,k1 char(2),k2 char(4),dt timestamp);delimiter //createprocedure rand_data(in num int)begindeclare str char(62) default ‘abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789‘;declare str2 char(2);declare str4 char(4);declare i int default 0;while i < >= desc city;+-------------+----------+------+-----+---------+----------------+| Field| Type| Null | Key | Default | Extra|+-------------+----------+------+-----+---------+----------------+| ID| int(11)| NO| PRI | NULL| auto_increment || Name| char(35) | NO||||| CountryCode | char(3)| NO| MUL |||| District| char(20) | NO||||| Population| int(11)| NO|| 0||+-------------+----------+------+-----+---------+----------------+5 rows in set (0.00 sec)Field :列名字key:有没有索引,索引类型PRI: 主键索引UNI: 唯一索引MUL: 辅助索引(单列,联和,前缀)7.1 单列普通辅助索引7.1.1 创建索引db01 [world]>alter table city add index idx_name(name);表索引名(列名)db01 [world]>create index idx_name1 on city(name);db01 [world]>show index from city;![image](https://upload-images.jianshu.io/upload_images/16956686-8c8421524dca6291.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)注意:以上操作不代表生产操作,我们不建议在一个列上建多个索引同一个表中,索引名不能同名。### 7.1.2 删除索引:db01 [world]>alter table city drop index idx_name1;表名 索引名7.2 覆盖索引(联合索引)Master [world]>alter table city add index idx_co_po(countrycode,population);7.3 前缀索引db01 [world]>alter table city add index idx_di(district(5));注意:数字列不能用作前缀索引。7.4 唯一索引db01 [world]>alter table city add unique index idx_uni1(name);ERROR 1062 (23000): Duplicate entry ‘San Jose‘ for key ‘idx_uni1‘计city表中,以省的名字为分组,统计组的个数
select district,count(id) from city group by district;需求: 找到world下,city表中 name列有重复值的行,最后删掉重复的行db01 [world]>select name,count(id) as cid from city group by namehaving cid>1 order by cid desc;db01 [world]>select * from city where name=‘suzhou‘;8. 执行计划获取及分析8.0 介绍(1) 获取到的是优化器选择完成的,他认为代价最小的执行计划. 作用: 语句执行前,先看执行计划信息,可以有效的防止性能较差的语句带来的性能问题. 如果业务中出现了慢语句,我们也需要借助此命令进行语句的评估,分析优化方案。 (2) select 获取数据的方法 1. 全表扫描(应当尽量避免,因为性能低) 2. 索引扫描 3. 获取不到数据
8.2 执行计划分析8.2.0 重点关注的信息table: city---->查询操作的表**possible_keys: CountryCode,idx_co_po---->可能会走的索引**key: CountryCode---->真正走的索引***type: ref---->索引类型*****Extra: Using index condition---->额外信息*****8.2.1 type详解从左到右性能依次变好.ALL:全表扫描,不走索引例子:1. 查询条件列,没有索引select * FROM t_100w WHERE k2=‘780P‘;2. 查询条件出现以下语句(辅助索引列)USE world DESC city;DESC select * FROM city WHERE countrycode ‘CHN‘;DESC select * FROM city WHERE countrycode NOT IN (‘CHN‘,‘USA‘);DESC select * FROM city WHERE countrycode LIKE ‘%CH%‘;注意:对于聚集索引列,使用以上语句,依然会走索引DESC select * FROM city WHERE id 10;INDEX:全索引扫描1. 查询需要获取整个索引树种的值时:DESCSELECT countrycodeFROM city;2. 联合索引中,任何一个非最左列作为查询条件时:idx_a_b_c(a,b,c)---> aababcSELECT * FROM t1 WHERE b select * FROM t1 WHERE cRANGE :索引范围扫描 辅助索引> < >= --number-of-queries=2000 -uroot -p123 -verbosemysqlslap: [Warning] Using a password on the command line interface can be insecure.BenchmarkRunning for engine rboseAverage number of seconds to run all queries: 701.743 secondsMinimum number of seconds to run all queries: 701.743 secondsMaximum number of seconds to run all queries: 701.743 secondsNumber of clients running queries: 100Average number of queries per client: 20优化后:[root@db01 ~]# mysqlslap --defaults-file=/etc/my.cnf --concurrency=100 --iterations=1 --create-schema=‘oldboy‘ --query="select * from oldboy.t_100w where k2=‘780P‘" engine=innodb --number-of-queries=2000 -uroot -p123 -verbosemysqlslap: [Warning] Using a password on the command line interface can be insecure.BenchmarkRunning for engine rboseAverage number of seconds to run all queries: 0.190 secondsMinimum number of seconds to run all queries: 0.190 secondsMaximum number of seconds to run all queries: 0.190 secondsNumber of clients running queries: 100Average number of queries per client: 20联合索引:1. select * FROM t1WHERE a=b=我们建立联合索引时:ALTER TABLE t1 ADD INDEX idx_a_b(a,b);ALTER TABLE t1 ADD INDEX idx_b_a(b,a);以上的查询不考虑索引的顺序,优化器会自动调整where的条件顺序注意: 索引,我们在这种情况下建索引时,需要考虑哪个列的唯一值更多,哪个放在索引左边.2.如果出现where 条件中出现不等值查询条件DESCSELECT * FROM t_100w WHERE num slowlog"热"数据9.1.2 选择唯一性索引唯一性索引的值是唯一的,可以更快速的通过该索引来确定某条记录。例如,学生表中学号是具有唯一性的字段。为该字段建立唯一性索引可以很快的确定某个学生的信息。如果使用姓名的话,可能存在同名现象,从而降低查询速度。优化方案:(1) 如果非得使用重复值较多的列作为查询条件(例如:男女),可以将表逻辑拆分(2) 可以将此列和其他的查询类,做联和索引select count(*) from world.city;select count(distinct countrycode) from world.city;select count(distinct countrycode,population ) from world.city;9.1.3(必须的) 为经常需要where 、ORDER BY、GROUP BY,join on等操作的字段,排序操作会浪费很多时间。whereA B C----》 ABCin where Agroup by Border by CA,B,C如果为其建立索引,优化查询注:如果经常作为条件的列,重复值特别多,可以建立联合索引。9.1.4 尽量使用前缀来索引如果索引字段的值很长,最好使用值的前缀来索引。
9.1.5 限制索引的数目索引的数目不是越多越好。 可能会产生的问题: (1) 每个索引都需要占用磁盘空间,索引越多,需要的磁盘空间就越大。 (2) 修改表时,对索引的重构和更新很麻烦。越多的索引,会使更新表变得很浪费时间。 (3) 优化器的负担会很重,有可能会影响到优化器的选择. percona-toolkit中有个工具,专门分析索引是否有用
9.1.6 删除不再使用或者很少使用的索引(percona toolkit)pt-duplicate-key-checker 表中的数据被大量更新,或者数据的使用方式被改变后,原有的一些索引可能不再需要。数据库管理 员应当定期找出这些索引,将它们删除,从而减少索引对更新操作的影响。
9.1.7 大表加索引,要在业务不繁忙期间操作9.1.8 尽量少在经常更新值的列上建索引9.1.9 建索引原则(1) 必须要有主键,如果没有可以做为主键条件的列,创建无关列(2) 经常做为where条件列order bygroup byjoin on, distinct 的条件(业务:产品功能+用户行为)(3) 最好使用唯一值多的列作为索引,如果索引列重复值较多,可以考虑使用联合索引(4) 列值长度较长的索引列,我们建议使用前缀索引.(5) 降低索引条目,一方面不要创建没用索引,不常使用的索引清理,percona toolkit(xxxxx)(6) 索引维护要避开业务繁忙期9.2.1 没有查询条件,或者查询条件没有建立索引select * from tab;全表扫描。select* from tab where 1=1;在业务数据库中,特别是数据量比较大的表。是没有全表扫描这种需求。1、对用户查看是非常痛苦的。2、对服务器来讲毁灭性的。(1)select * from tab;SQL改写成以下语句:select* fromtaborder bypricelimit 10 ;需要在price列上建立索引(2)select* fromtab where name=‘zhangsan‘name列没有索引改:1、换成有索引的列作为查询条件2、将name列建立索引9.2.2 查询结果集是原表中的大部分数据,应该是25%以上。查询的结果集,超过了总数行数25%,优化器觉得就没有必要走索引了。假如:tab表 id,nameid:1-100w,id列有(辅助)索引select * from tabwhere id>500000;如果业务允许,可以使用limit控制。怎么改写 ?结合业务判断,有没有更好的方式。如果没有更好的改写方案尽量不要在mysql存放这个数据了。放到redis里面。9.2.3 索引本身失效,统计数据不真实索引有自我维护的能力。对于表内容变化比较频繁的情况下,有可能会出现索引失效。一般是删除重建现象:有一条select语句平常查询时很快,突然有一天很慢,会是什么原因select?--->索引失效,,统计数据不真实DML ?--->锁冲突9.2.4 查询条件使用函数在索引列上,或者对索引列进行运算,运算包括(+,-,*,/,! 等)例子:错误的例子:select * from test where id-1=9;正确的例子:select * from test where id=10;算术运算函数运算子查询9.2.5 隐式转换导致索引失效.这一点应当引起重视.也是开发中经常会犯的错误.这样会导致索引失效. 错误的例子:mysql> alter table tab add index inx_tel(telnum);Query OK, 0 rows affected (0.03 sec)Records: 0Duplicates: 0Warnings: 0mysql>mysql> desc tab;+--------+-------------+------+-----+---------+-------+| Field| Type| Null | Key | Default | Extra |+--------+-------------+------+-----+---------+-------+| id| int(11)| YES|| NULL||| name| varchar(20) | YES|| NULL||| telnum | varchar(20) | YES| MUL | NULL||+--------+-------------+------+-----+---------+-------+3 rows in set (0.01 sec)mysql> select * from tab where telnum=‘1333333‘;+------+------+---------+| id| name | telnum|+------+------+---------+|1 | a| 1333333 |+------+------+---------+1 row in set (0.00 sec)mysql> select * from tab where telnum=1333333;+------+------+---------+| id| name | telnum|+------+------+---------+|1 | a| 1333333 |+------+------+---------+1 row in set (0.00 sec)mysql> explainselect * from tab where telnum=‘1333333‘;+----+-------------+-------+------+---------------+---------+---------+-------+------+-----------------------+| id | select_type | table | type | possible_keys | key| key_len | ref| rows | Extra|+----+-------------+-------+------+---------------+---------+---------+-------+------+-----------------------+|1 | SIMPLE| tab| ref| inx_tel| inx_tel | 63| const |1 | Using index condition |+----+-------------+-------+------+---------------+---------+---------+-------+------+-----------------------+1 row in set (0.00 sec)mysql> explainselect * from tab where telnum=1333333;+----+-------------+-------+------+---------------+------+---------+------+------+-------------+| id | select_type | table | type | possible_keys | key| key_len | ref| rows | Extra|+----+-------------+-------+------+---------------+------+---------+------+------+-------------+|1 | SIMPLE| tab| ALL| inx_tel| NULL | NULL| NULL |2 | Using where |+----+-------------+-------+------+---------------+------+---------+------+------+-------------+1 row in set (0.00 sec)mysql> explainselect * from tab where telnum=1555555;+----+-------------+-------+------+---------------+------+---------+------+------+-------------+| id | select_type | table | type | possible_keys | key| key_len | ref| rows | Extra|+----+-------------+-------+------+---------------+------+---------+------+------+-------------+|1 | SIMPLE| tab| ALL| inx_tel| NULL | NULL| NULL |2 | Using where |+----+-------------+-------+------+---------------+------+---------+------+------+-------------+1 row in set (0.00 sec)mysql> explainselect * from tab where telnum=‘1555555‘;+----+-------------+-------+------+---------------+---------+---------+-------+------+-----------------------+| id | select_type | table | type | possible_keys | key| key_len | ref| rows | Extra|+----+-------------+-------+------+---------------+---------+---------+-------+------+-----------------------+|1 | SIMPLE| tab| ref| inx_tel| inx_tel | 63| const |1 | Using index condition |+----+-------------+-------+------+---------------+---------+---------+-------+------+-----------------------+1 row in set (0.00 sec)mysql>9.2.6 ,not in 不走索引(辅助索引)EXPLAINSELECT * FROM teltab WHERE telnum ‘110‘;EXPLAINSELECT * FROM teltab WHERE telnumNOT IN (‘110‘,‘119‘);mysql> select * from tab where telnum ‘1555555‘;+------+------+---------+| id| name | telnum|+------+------+---------+|1 | a| 1333333 |+------+------+---------+1 row in set (0.00 sec)mysql> explain select * from tab where telnum ‘1555555‘;单独的>, 9 and id < 12 order by id for update访问顺序 :1. id=9,gap [5-10]2. 向右遍历 next-lock 10-15,发现10>9,停止扫描,但由于不是等值12,所有next-lock保持不变更gap最终锁范围: [5-10],[10-15] ====》 [5-15]第二种select id from t where id > 9 and id < 12 order by id desc for update由于order by desc 的存在,查询优化器为了避免再排一次序,会将查找顺序优化为先找id 9,继续向左,扫到0-5,发现5 [0-15]
隔离级别影响到数据的读取,默认的级别是 RR模式. transaction_isolation 隔离级别(参数) 负责的是,MVCC,读一致性问题 RU : 读未提交,可脏读,一般部议叙出现 RC : 读已提交,可能出现幻读,可以防止脏读. RR : 可重复读,功能是防止"幻读"现象 ,利用的是undo的快照技术+GAP(间隙锁)+NextLock(下键锁) SR : 可串行化,可以防止死锁,但是并发事务性能较差 补充: 在RC级别下,可以减轻GAP+NextLock锁的问题,但是会出现幻读现象,一般在为了读一致性会在正常select后添加for update语句.但是,请记住执行完一定要commit否则容易出现所等待比较严重. 例如: [world]>select * from city where id=999 for update; [world]>commit;
RR级别:解决了 不可重复读问题+幻读的现象不可重复读问题是由 undo的快照技术来解决。幻读现象是由:MVCC+GAP+next-lock
架构改造项目项目背景: 2台 IBM X3650 32G ,原来主从关系,2年多没有主从了,"小问题"不断(锁,宕机后的安全) MySQL 5.1.77 默认存储引擎 MyISAM 数据量: 60G左右 ,每周全备,没有开二进制日志 架构方案: 1. 升级数据库版本到5.7.20 2. 更新所有业务表的存储引擎为InnoDB 3. 重新设计备份策略为热备份,每天全备,并备份日志 4. 重新构建主从 结果: 1.性能 2.安全方面 3.快速故障处理
InnoDB存储引擎核心特性-参数补充--存储引擎相关
查看show engines;show variables like ‘default_storage_engine‘;select @@default_storage_engine;如何指定和修改存储引擎(1) 通过参数设置默认引擎(2) 建表的时候进行设置(3) alter table t1 engine=innodb;如何指定和修改存储引擎(1) 通过参数设置默认引擎(2) 建表的时候进行设置(3) alter table t1 engine=innodb;共享表空间innodb_data_file_path一般是在初始化数据之前就设置好例子:innodb_data_file_path=ibdata1:512M:ibdata2:512M:autoextend独立表空间show variables like ‘innodb_file_per_table‘;缓冲区池查询select @@innodb_buffer_pool_size; show engine innodb status\G innodb_buffer_pool_size 一般建议最多是物理内存的 75-80%
innodb_flush_log_at_trx_commit (双一标准之一)作用主要控制了innodb将log buffer中的数据写入日志文件并flush磁盘的时间点,取值分别为0、1、2三个。控制了redo buffer 刷写策略,是一个安全参数,是在5.6版本以上默认的参数查询select @@innodb_flush_log_at_trx_commit; #innodb_flush_log_at_trx_commit=1 参数说明:
1,每次事物的提交都会引起日志文件写入、flush磁盘的操作,确保了事务的ACID;flush 到操作系统的文件系统缓存 fsync到物理磁盘
0,表示当事务提交时,不做日志写入操作,而是每秒钟将log buffer中的数据写入文件系统缓存并且秒fsync磁盘一次;
2,每次事务提交引起写入文件系统缓存,但每秒钟完成一次fsync磁盘操作。
控制了redo buffer 刷写策略,是一个安全参数,是在5.6版本以上默认的参数参数功能1:每次事务提交,都会立即刷下redo到磁盘(redo buffer --每次事务-->os buffer(操作系统缓存) --每次事务--写入磁盘)0:表示当事务提交时,不立即做日志写入操作(redo buffer --每秒-->os buffer (操作系统缓存)--每秒--磁盘)2:每次事务提交引起写入文件系统缓存(redo buffer --每事务-->os buffer(操作系统缓存) --每秒--磁盘)
Innodb_flush_method=(O_DIRECT, fdatasync)作用:
控制了 redo buffer 和 data bufffer 刷写磁盘方式
控制的是,log buffer 和data buffer,刷写磁盘的时候是否经过文件系统缓存show variables like ‘%innodb_flush%‘;O_DIRECT :数据缓冲区写磁盘,不走OS buffer(操作系统缓存)fsync :日志和数据缓冲区写磁盘,都走OS buffer(操作系统缓存)O_DSYNC :日志缓冲区写磁盘,不走 OS buffer(操作系统缓存)
最大安全模式:innodb_flush_log_at_trx_commit=1innodb_flush_method=O_DIRECT
最大性能模式:innodb_flush_log_at_trx_commit=0innodb_flush_method=fsync
## 4.3 关于redo设置innodb_log_buffer_size= 128M 业务系统CPU压力有关innodb_log_file_size=256 一般是1-2倍innodb_log_files_in_group = 3 3-4组
redo日志有关的参数innodb_log_buffer_size=16777216 内存大小innodb_log_file_size=50331648文件大小innodb_log_files_in_group = 3扩展(自己扩展,建议是官方文档。)RR模式(对索引进行删除时):GAP:间隙锁next-lock:下一键锁定例子:id(有索引)1 2 3 4 5 6 GAP:在对3这个值做变更时,会产生两种锁,一种是本行的行级锁,另一种会在2和4索引键上进行枷锁next-lock:对第六行变更时,一种是本行的行级锁,在索引末尾键进行加锁,6以后的值在这时是不能被插入的。总之:GAP、next lock都是为了保证RR模式下,不会出现幻读,降低隔离级别或取消索引,这两种锁都不会产生。IX IS X S是什么?相关文献:
Mysql 索引精讲
MySQL聚集索引和非聚集索引
B-/B+ 树看 MySQL索引实现,深入思考两个面试题背后的设计思路
为什么Mysql用B+树做索引,不用B-树或平衡二叉树?
MySQL死锁系列-常见加锁场景分析
mysql 锁机制详解加锁处理分析
mysql之innodb引擎的共享表空间和独立表空间
为什么MongoDB使用B-Tree,Mysql使用B+Tree
MySQL数据库 锁机制简介
Mysql并发时经典常见的死锁原因及解决方法
MySQL学习之——锁(行锁、表锁、页锁、乐观锁、悲观锁等)
深入理解乐观锁与悲观锁
深入浅出mysql事务处理和锁机制
本文来自博客园,作者:孙龙-程序员,转载请注明原文链接:https://www.cnblogs.com/sunlong88/p/16632891.html
作者:宁愿juejin.im/post/5c9040e95188252d92095a9e引言之前在深入了解数据库理论的时候,了解到事务的不同隔离级别可能存在的问题。为了更好的理解所以在MySQL数据库...
2024.11.12MySQL是一个关系型数据库管理系统,由瑞典MySQL AB 公司开发,目前属于 Oracle 旗下产品。MySQL 是最流行的关系型数据库管理系统之一,在 WEB 应用方面,MySQL是最好的 RD...
2024.11.15MySQL和Oracle都是流行的关系数据库管理系统(RDBMS),在世界各地广泛使用;大多数数据库以类似的方式工作,但MySQL和Oracle的这里和那里总是存在一些差异的。本篇文章就给大家比较Or...
2024.11.15概述很多面试官经常会问mysql跟oracle的区别,整理一下分享给大家。一、并发性并发性是oltp数据库最重要的特性,但并发涉及到资源的获取、共享与锁定。mysql:mysql以表级锁为主,对资源锁...
2024.11.15mysql热门面试题整理了一下mysql的面试,问得最多的有3个:mysql引擎:innodb和isam的区别mysql索引:索引下推、索引覆盖、回表概念、like索引失效mysql锁和事务我们先总结...
2024.11.12