MYSQL

  • 练习题
    • 一个叫 team 的表,里面只有一个字段name, 一共有4 条纪录,分别是a,b,c,d, 对应四个球对,现在四个球对进行比赛,用一条sql 语句显示所有可能的比赛组合
      • select a.name, b.name from team a, team b where a.name < b.name
    • 联表更新
      • UPDATE bdp_user_message
      • INNER JOIN bdp_user ON bdp_user_message.userId = bdp_user.tbluserid
      • SET bdp_user_message.messageId = bdp_user.tbluserid
      • where bdp_user_message.userId=196602;
    • 按name分组取最大的N个val
      • select a.* from tb a where N > (select count(*) from tb where name = a.name and val > a.val ) order by a.name,a.val
    • 按name分组取最小的两个(N个)val
      • select a.* from tb a where N > (selectcount(*) from tb wherename = a.name and val < a.val ) orderby a.name,a.val
    • 显示每门课程都大于80分的学生姓名
      • select name from A group by name having min(score)>80
    • 总分排名前三的学生姓名,总分,平均分
      • SELECT studentId,SUM(userScore) score,AVG(userScore) avgscore FROM answer_exam GROUP BY studentId ORDER BY score DESC limit 3;
    • 删除自动编号不同,其余全部相同的冗余信息
      • 错误版本
        • delete tablename where 自动编号 not in(select min(自动编号) from tablename group by 学号, 姓名, 课程编号, 课程名称, 分数)
        • 这样写会报错:You can’t specify target table ‘tempA’ for update in FROM clause
      • 正确版本
delete from comment where id not in (
	SELECT * FROM(
		SELECT min(id) from comment group by questionId,sourceType,teacherId
	) a 
)
  • 常用函数
    • 多表关联on条件与where条件的区别
      • on条件是在生成临时表时使用的条件,它不管on中的条件是否为真,都会返回左边表中的记录
      • where条件是在临时表生成好后,再对临时表进行过滤的条件。这时已经没有left join的含义(必须返回左边表的记录)了,条件不为真的就全部过滤掉
TAB1
ID SIZE
1  10
2  20
3  30

TAB2
SIZE NAME
10   AAA
20   BBB
20   CCC

SELECT * FROM TAB1 LEFT JOIN TAB2 ON TAB1.SIZE = TAB2.SIZE WHERE TAB2.NAME='AAA';

ID TAB1.SIZE TAB2.SIZE NAME
1  10         10       AAA


SELECT * FROM TAB1 LEFT JOIN TAB2 ON TAB1.SIZE = TAB2.SIZE AND TAB2.NAME='AAA';

ID TAB1.SIZE TAB2.SIZE NAME
1  10         10       AAA
2  20         NULL     NULL
2  20         NULL     NULL
3  30         NULL     NULL
    • 清空数据并且自增id重新排序
      • truncate tablename
    • 随机抽取
      • SELECT * FROM users AS t1 JOIN (SELECT ROUND(RAND() * ((SELECT MAX(userId) FROM `users`)-(SELECT MIN(userId) FROM users))+(SELECT MIN(userId) FROM users)) AS userId) AS t2 WHERE t1.userId >= t2.userId ORDER BY t1.userId LIMIT 1
      • 执 行该sql语句,用时0.031s,效率非常好。当把”LIMIT 1“改为了”LIMIT 100“ 随机取一百条记录,用时0.048s。可是就在此时问题出现了,发现结果好像不是随机的。为了验证结果,又执行了N次,的确不是随机的。问题出现 在”ORDER BY t1.userId“这里,按userId排序了。随机取一条记录还是不错的选择,多条就不行了啊。
      • SELECT * FROM users WHERE userId >= ((SELECT MAX(userId) FROM users)-(SELECT MIN(userId) FROM users)) * RAND() + (SELECT MIN(userId) FROM users) LIMIT 1
      • 执行该sql语句,用时0.039s,效率也是非常好。接着把”LIMIT 1“改为了”LIMIT 10000“,用时0.063s。经过多次验证,得出的结果都是随机的。
    • 连接字符串
      • SELECT CONCAT(‘1′,’2′,’3’);
    • 日期格式化
      • select DATE_FORMAT(SYSDATE(),’%Y-%m-%d’) date
    • case when
      • case when score<’60’ THEN ‘不及格’
      • when score<’70’ then ‘合格’
      • when score<’80’ then ‘中’
      • when score<’90’ then ‘良’
      • else ‘优秀’ end ming
      • from SalesByQuarter;
    • if函数(当Condition为TRUE时,返回A;当Condition为FALSE时,返回B。)
      • select IF(DATE_FORMAT(SYSDATE(),’%Y-%m-%d’)>’2016-05-13′,’a’,’b’);
    • 日期和时间间隔运算
      • SELECT DATE_SUB(SYSDATE(),INTERVAL 2 DAY);
    • 两个时间段的间隔天数
      • select DATEDIFF(SYSDATE(),’2017-10-09′);
    • 判断null函数
      • SELECT IFNULL(expr1,expr2);(如果expr1是null,返回expr2,反之 ,返回expr1)
    • 生成UUID
      • SELECT REPLACE(UUID(),’-‘,”) AS id;
    • 分页功能
      • select * from account limit 0,4
    • replace into和insert into on duplicate key update的比较
      • 相同点:replace into和on duplcate key update都是只有在primary key或者unique key冲突的时候才会执行。
      • 不同点:如果数据存在,replace into则会将原有数据删除,再进行插入操作,这样就会有一种情况,如果某些字段有默认值,但是replace into语句的字段不完整,则会设置成默认值。而on duplicate key update则是执行update后面的语句
      • 效率:数据少的时候,两者相差不大;但是数据多的时候,replace的速度比update慢,因为数据多的时候,先删除后插入会重新维护索引,而update不会有这方面的情况
    • 创建表
      • create table t1(
      • id int UNSIGNED #确保这个数字是非负数 not NULL auto_increment PRIMARY key,
      • name VARCHAR(30));
    • 创建视图
      • create view v_t1 as select * from t1 where name=’张三’;
      • select * from v_t1
    • 备份表
      • 目标表Table2不存在;因为在插入时会自动创建表Table2,并将Table1中指定字段数据复制到Table2中。
        • CREATE TABLE Table2(select * from Table1);
      • 目标表Table2不存在;因为在插入时会自动创建表Table2,不会将Table1中指定字段数据复制到Table2中,但是会将Table1的数据结构复制到Table2中(比如字段自增,设置主键等等)CREATE TABLE Table2 like Table1;#要求:(1)目标表Table2必须存在;(2)因为在插入时会自动创建表Table2,并将Table1中指定字段数据复制到Table2中。Insert into Table2(field1,field2,…) select value1,value2,… from Table1Insert into t3 select * from t1
    • UNION/ALL、EXCEPT/ALL和INTERSECT/ALL
      • UNION/ALL:组合多个结果表,并消去表中重复行,和ALL一起使用时,不消除重复行。
      • EXCEPT/ALL:在table1中但不在table2中的行并消除重复行,和ALL一起使用时,不消除重复行。
      • INTERSECT/ALL:包括table1和table2中都有的行并消除重复行,和ALL一起使用时,不消除重复行。
  • 查询中用到的关键词,并且他们的执行顺序依次为
    • from>join>on>where>group by>having>select>distinct>order by>limit
    • where的执行顺序
      • 举例子:一百万条 第一个条件(80W符合条件–意味着可以过滤掉20万条数据) 第二个条件(20W符合条件–意味着可以过滤掉80万条数据)
      • where 执行顺序从右往左(从后往前)(Oralce 的 where执行顺序)
        • where 第一个条件 and 第二个条件
      • where 执行顺序从左往右(从前往后)(mysql的 where执行顺序)
        • where 第二个条件 and 第一个条件
  • sql事务处理
    • SET autocommit = 0;#这句话代表关闭自动提交功能
    • DELETE from t1 where name=’张三’;
    • SAVEPOINT p1;#还原点P1
    • DELETE from t1 where name=’李四’;
    • SAVEPOINT p2;#还原点P2
    • ROLLBACK to p1;
    • ROLLBACK;
    • COMMIT;
    • PS:如果这里是【ENGINE=MyISAM】,那么事务机制没有作用;只有【ENGINE=InnoDB 】,事务机制才能生效;
  • 存储过程
    • 创建存储过程
    • CREATE PROCEDURE p_t1()
    • BEGIN
      • set @i=103;
      • WHILE @i<110 DO
        • INSERT into t1 (name) values (CONCAT(‘user’,@i));
        • set @i=@i+1;
      • end WHILE;
    • END
    • 触发存储过程
    • call p_t1
    • 显示存储过程的信息
    • show create PROCEDURE p_t1
  • 事务的四大特性
    • 事务的原子性(Atomicity):指一个事务要么全部执行,要么不执行.也就是说一个事务不可能只执行了一半就停止了.比如你从取款机取钱,这个事务可以分成两个步骤:1划卡,2出钱.不可能划了卡,而钱却没出来.这两步必须同时完成.要么就不完成
    • 事务的一致性(Consistency):指事务的运行并不改变数据库中数据的一致性.例如,完整性约束了a+b=10,一个事务改变了a,那么b也应该随之改变
    • 独立性(Isolation):事务的独立性也有称作隔离性,是指两个以上的事务不会出现交错执行的状态.因为这样可能会导致数据不一致
    • 持久性(Durability):事务的持久性是指事务执行成功以后,该事务所对数据库所作的更改便是持久的保存在数据库之中,不会无缘无故的回滚
  • 事务隔离级别
    • 脏读:事务T1更新了一行记录,还未提交所做的修改,这个T2读取了更新后的数据,然后T1执行回滚操作,取消刚才的修改,所以T2所读取的行就无效,也就是脏数据。
    • 不可重复读:不可重复读是指在对于数据库中的某个数据,一个事务范围内多次查询却返回了不同的数据值,这是由于在查询间隔,被另一个事务修改并提交了。
    • 幻读:事务T1读取一条指定where条件的语句,返回结果集。此时事务T2插入一行新记录,恰好满足T1的where条件。然后T1使用相同的条件再次查询,结果集中可以看到T2插入的记录,这条新纪录就是幻想。
    • READ UNCOMMITTED(读未提交)
      • 支持幻读,不可重复读,脏读
    • READ COMMITTED(读已提交)
      • 支持幻读,不可重复读
    • REPEATABLE READ (重复读)
      • 支持幻读
    • SERIALIZABLE (序列化)
      • 全部不支持
    • MySQL默认REPEATABLE READ (重复读)
    • SqlServer,Oracle默认READ COMMITTED(读已提交)
  • delete和insert高并发情况下导致的deadlock
    • record lock:记录锁,也就是仅仅锁着单独的一行
    • gap lock:区间锁,仅仅锁住一个区间(注意这里的区间都是开区间,也就 是不包括边界值,至于为什么这么定义?innodb官方定义的)
    • next-key lock:record lock+gap lock,所以next-key lock也就半开半闭区间,且是下界开,上界闭。(为什么这么定义?innodb官方定义的)
    • 假如一个索引的行有10,11,13,20那么可能的next-key lock的包括:
      • (无穷小, 10]
      • (10,11]
      • (11,13]
      • (13,20]
      • (20, 无穷大)
      • 结合业务逻辑, 执行新增操作时也会执行一样的逻辑, 先进行delete.
        • 例如,现在表student中有四条数据:

《MYSQL》

  • 执行两个事务:
  • session 1:
begin;
delete from student where no = 21;
  • session 2:
begin;
delete from student where no = 22;
  • 继续执行:
  • session 1:
INSERT INTO student (no,name) VALUES(21, "Zhoubing");
  • session 2:
INSERT INTO student (no,name) VALUES(22, "Zhoubing");
  • 区间锁只是用来防止其他事务在区间中插入数据,当两个事务拿到相同区间锁后,就会阻止对方往区间内做insert操作。所以第一个事务insert会阻塞,第二个事务会提示死锁。换成read-commited级别后就没死锁了! 因为没有了间隙所,读提交不需要幻读控制,也就不需要间隙锁了.
  • 数据库引擎
    • InnoDB和MyISAM
      • InnoDB存储引擎既支持行级锁( row-level locking),也支持表级锁,但默认情况下是采用行级锁。
      • MyISAM表级锁不支持事务,不支持外键,delete 先drop表,然后重新建表;
      • MyIASM引擎是MySQL默认的引擎,但是它没有提供对数据库事务的支持,也不支持行级锁和外键,因此当INSERT(插入)或UPDATE(更新)数据时即写操作需要锁定整个表,效率便会低一些。不过和Innodb不同,MyIASM中存储了表的行数,于是SELECT COUNT(*) FROM TABLE时只需要直接读取已经保存好的值而不需要进行全表扫描。如果表的读操作远远多于写操作且不需要数据库事务的支持,那么MyIASM也是很好的选择。
      • 大尺寸的数据集趋向于选择InnoDB引擎,因为它支持事务处理和故障恢复。数据库的大小决定了故障恢复的时间长短,InnoDB可以利用事务日志进行数据恢复,这会比较快。主键查询在InnoDB引擎下也会相当快,不过需要注意的是如果主键太长也会导致性能问题,关于这个问题我会在下文中讲到。大批的INSERT语句(在每个INSERT语句中写入多行,批量插入)在MyISAM下会快一些,但是UPDATE语句在InnoDB下则会更快一些,尤其是在并发量大的时候。
      • 两种引擎所使用的索引
        • MyIASM和Innodb都使用了树这种数据结构做为索引。
        • MyISAM引擎的索引结构为B+Tree,其中B+Tree的数据域存储的内容为实际数据的地址,也就是说它的索引和实际的数据是分开的,只不过是用索引指向了实际的数据,这种索引就是所谓的非聚集索引。
        • Innodb引擎的索引结构:与MyISAM引擎的索引结构同样也是B+Tree,但是Innodb的索引文件本身就是数据文件,即B+Tree的数据域存储的就是实际的数据,这种索引就是聚集索引。这个索引的key就是数据表的主键,因此InnoDB表数据文件本身就是主索引。并且和MyISAM不同,InnoDB的辅助索引数据域存储的也是相应记录主键的值而不是地址,所以当以辅助索引查找时,会先根据辅助索引找到主键,再根据主键索引找到实际的数据。所以Innodb不建议使用过长的主键,否则会使辅助索引变得过大。建议使用自增的字段作为主键,这样B+Tree的每一个结点都会被顺序的填满,而不会频繁的分裂调整,会有效的提升插入数据的效率。
  • 数据库三范式
    • 1NF:表的列具有原子性,不可在分解,总结列不可分
    • 2NF:是在1NF基础上的,满足2NF必须满足1NF。要求数据库表中的每个实例或行必须可以被惟一地区分。为实现区分通常需要我们设计一个主键来实现
    • 3NF:即满足第二范式前提,如果某一属性依赖于其他非主键属性,而其他非主键属性又依赖于主键,那么这个属性就是间接依赖于主键,这被称作传递依赖于主属性。例如,存在一个部门信息表,其中每个部门有部门编号(dept_id)、部门名称、部门简介等信息。那么在员工信息表中列出部门编号后就不能再将部门名称、部门简介等与部门有关的信息再加入员工信息表中。
  • 事务传播性(事务传递性)
    • ROPAGATION_REQUIRED:如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择。
    • PROPAGATION_SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行。
    • PROPAGATION_MANDATORY:使用当前的事务,如果当前没有事务,就抛出异常。
    • PROPAGATION_REQUIRES_NEW:新建事务,如果当前存在事务,把当前事务挂起。
    • PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
    • PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
    • PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类 似的操作。
  • 分区:分区就是把一个数据表的文件和索引分散存储在不同的物理文件中。分区只不过把存放数据的文件分成了许多小块,例如mysql中的一张表对应三个文件.MYD,MYI,frm。根据一定的规则把数据文件(MYD)和索引文件(MYI)进行了分割,分区后的表呢,还是一张表。分区可以把表分到不同的硬盘上,但不能分配到不同服务器上。
    • 优点:
      • 数据不存在多个副本,不必进行数据复制,性能更高
    • 缺点:
      • 分区策略必须经过充分考虑,避免多个分区之间的数据存在关联关系,每个分区都是单点,如果某个分区宕机,就会影响到系统的使用。
    • mysql支持的分区类型包括Range、List、Hash、Key,其中Range比较常用:
    • RANGE分区:基于属于一个给定连续区间的列值,把多行分配给分区。
      • 建立一个user 表 以id进行分区 id 小于10的在user_1分区id小于20的在user_2分区
create table user(
    id int not null auto_increment,
    username varchar(10),
    primary key(id)
)engine = innodb charset=utf8
partition by range (id)(
    partition user_1 values less than (10),
    partition user_2 values less than (20)
);
    • 建立后添加分区:maxvalue 表示最大值 这样大于等于20的id 都出存储在user_3分区
alter table user add partition(
partition user_3 values less than maxvalue
);
    • 删除分区:
alter table user drop partition user_3;
    • mysql通过分区把数据保存到不同的文件里,同时索引也是分区的。相对于未分区的表来说,分区后单独的数据库文件索引文件的大小都明显降低,效率则明显的提升了
    • 分区的限制:
      • 主键或者唯一索引必须包含分区字段,如primary key (id,username),不过innoDB的大组建性能不好。
      • 很多时候,使用分区就不要在使用主键了,否则可能影响性能。
      • 只能通过int类型的字段或者返回int类型的表达式来分区,通常使用year或者to_days等函数(mysql 5.6 对限制开始放开了)。
      • 每个表最多1024个分区,而且多分区会大量消耗内存。
      • 分区的表不支持外键,相关的逻辑约束需要使用程序来实现。
      • 分区后,可能会造成索引失效,需要验证分区可行性。
      • 所有分区必须使用想用的存储引擎
CREATE TABLE users (         
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,         
usersname VARCHAR(30) NOT NULL DEFAULT '',         
email VARCHAR(30) NOT NULL DEFAULT ''  
)  
PARTITION BY RANGE (id) (         
PARTITION p0 VALUES LESS THAN (3000000),               
PARTITION p1 VALUES LESS THAN (6000000),            
PARTITION p2 VALUES LESS THAN (9000000),              
PARTITION p3 VALUES LESS THAN MAXVALUE     
);
    • 在这里,将用户表分成4个分区,以每300万条记录为界限,每个分区都有自己独立的数据、索引文件的存放目录。还可以将这些分区所在的物理磁盘分开完全独立,可以提高磁盘IO吞吐量。
CREATE TABLE users (         
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,         
usersname VARCHAR(30) NOT NULL DEFAULT '',         
email VARCHAR(30) NOT NULL DEFAULT ''  )  
PARTITION BY RANGE (id) (         
PARTITION p0 VALUES LESS THAN (3000000)         
DATA DIRECTORY = '/data0/data'         
INDEX DIRECTORY = '/data0/index',           
PARTITION p1 VALUES LESS THAN (6000000)         
DATA DIRECTORY = '/data1/data'         
INDEX DIRECTORY = '/data1/index',           
PARTITION p2 VALUES LESS THAN (9000000)         
DATA DIRECTORY = '/data2/data'         
INDEX DIRECTORY = '/data2/index',           
PARTITION p3 VALUES LESS THAN MAXVALUE            
DATA DIRECTORY = '/data3/data'          
INDEX DIRECTORY = '/data3/index'  
);
    • LIST分区:类似于按RANGE分区,区别在于LIST分区是基于列值匹配一个离散值集合中的某个值来进行选择。
      • List(预定义列表) – 这种模式允许系统通过DBA定义的列表的值所对应的行数据进行分割。例如:DBA根据用户的类型进行分区。
CREATE TABLE user (       
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,       
name VARCHAR(30) NOT NULL DEFAULT '' ,     
user_type   int not null)  
PARTITION BY LIST (user_type ) (       
PARTITION p0 VALUES IN (0,4,8,12) ,     
PARTITION p1 VALUES IN (1,5,9,13) ,       
PARTITION p2 VALUES IN (2,6,10,14),       
PARTITION p3 VALUES IN (3,7,11,15)   );
    • HASH分区:基于用户定义的表达式的返回值来进行选择的分区,该表达式使用将要插入到表中的这些行的列值进行计算。这个函数可以包含MySQL 中有效的、产生非负整数值的任何表达式。
      • Hash(哈希) – 这中模式允许DBA通过对表的一个或多个列的Hash Key进行计算,最后通过这个Hash码不同数值对应的数据区域进行分区,。例如DBA可以建立一个对表主键进行分区的表。
CREATE TABLE user (       
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,       
username VARCHAR(30) NOT NULL DEFAULT '',       
email VARCHAR(30) NOT NULL DEFAULT ''  )  
PARTITION BY HASH (id) PARTITIONS 4 (       
PARTITION p0 ,       PARTITION p1,       
PARTITION p2,     PARTITION p3  );
    • KEY分区:类似于按HASH分区,区别在于KEY分区只支持计算一列或多列,且MySQL服务器提供其自身的哈希函数。必须有一列或多列包含整数值。
      • Key(键值) – 上面Hash模式的一种延伸,这里的Hash Key是MySQL系统产生的。
CREATE TABLE user (       
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,       
name VARCHAR(30) NOT NULL DEFAULT '',       
email VARCHAR(30) NOT NULL DEFAULT '' 
 )  PARTITION BY KEY (id) PARTITIONS 4 (       
PARTITION p0,       PARTITION p1,       
PARTITION p2,       PARTITION p3);
  • 分表和分区类似,区别是,分区是把一个逻辑表文件分成几个物理文件后进行存储,而分表则是把原先的一个表分成几个表。进行分表查询时可以通过union或者视图。分表又分垂直分割和水平分割,其中水平分分割最为常用。水平分割通常是指切分到另外一个数据库或表中。例如对于一个会员表,按对3的模进行分割:table = id%3如果id%3 = 0 则将用户数据放入到user_0表中,如id%3=1就放入user_1表中,依次类推。在这里有个问题,这个uid应该是所有会员按序增长的,可他是怎么得到的呢?使用auto_increment是不行的,这样就用到序列了。对于一些流量统计系统,其数据量比较大,并且对过往数据的关注度不高,这时按年、月、日进行分表,将每日统计信息放到一个以日期命名的表中;或者按照增量进行分表,如每个表100万数据,超过100万就放入第二个表。还可以按Hash进行分表,但是按日期和取模余数分表最为常见,也容易扩展。分表后可能会遇到新的问题,那就是查询,分页和统计。通用的方法是在程序中进行处理,辅助视图。根据会员姓名搜索用户信息。在这种需求下,需要搜索所有的表,并对结果进行汇总。虽然这样做产生了多次的查询,但并不代表效率低。好的sql语句执行10次也比差的sql语句执行一次快。
  • 分库中间件Mycat(个人理解分片==分库)
    • mycat的server.xml
      • <firewall>可以配置白名单哪些IP允许访问哪些IP不允许访问,其中包括<whitehost>,<blacklist>
<system>
     <property name="sequnceHandlerType">1</property>0:本地文件 1:数据库方式 2:时间戳方式  (使用mycat生成主键)          
</system>

<user name="taibai" defaultAccount="true">
     <property name="password">123456</property>                                                          
     <property name="schemas">VIPorder,VIPuser,VIPproduct</property>
     //添加DML权限
     <privileges check="false">   
          <schema name="VIPorder" dml="1111">  
             <table name="orderindex" dml="1111">
             <table name="orderinfo" dml="1111">
             <table name="orderdetail" dml="1111">
          </schema>
     </privileges>                
</user>

<user name="root">
     <property name="password">123456</property>                                                          
     <property name="schemas">VIPorder,VIPuser,VIPproduct</property>
      <property name="readOnly">true</property>         
</user>

0代表没权限 1代表有权限 dml顺序增删改查正好代表对数据库没有增加权限,有删除修改权限,没有查训权限,schema--数据库,table--数据表
配置用户,可以配置用户权限比如一个账号读写都可以,一个账号只能读,其中有属性 
  • mycat的schema.xml
//mycat 逻辑表
<schema name="VIPorder">
     <table name="orderindex" primarykey="id" dataNode="dn1,dn2,dn3" rule="mode-log"></table>
     <table name="orderinfo" primarykey="id" dataNode="dn1,dn2,dn3" rule="mode-log"></table>
     <table name="orderdetail" primarykey="id" dataNode="dn1,dn2,dn3" rule="mode-log"></table>
</schema>
<schema name="VIPuser">
     <table name="userindex" primarykey="id" dataNode="dn4,dn5,dn6" rule="mode-log"></table>
     <table name="userinfo" primarykey="id" dataNode="dn4,dn5,dn6" rule="mode-log"></table>
     <table name="userdetail" primarykey="id" dataNode="dn4,dn5,dn6" rule="mode-log"></table>
</schema>
<schema name="VIPproduct">
     <table name="productindex" primarykey="id" dataNode="dn7,dn8,dn9" rule="mode-log"></table>
     <table name="productinfo" primarykey="id" dataNode="dn7,dn8,dn9" rule="mode-log"></table>
     <table name="productdetail" primarykey="id" dataNode="dn7,dn8,dn9" rule="mode-log"></table>
</schema>

//mycat 逻辑表 对应真实数据库中的表
<dataNode name="dn1" dataHost="localhost1" database=VIPorder1></dataNode>
<dataNode name="dn2" dataHost="localhost1" database=VIPorder2></dataNode>
<dataNode name="dn3" dataHost="localhost1" database=VIPorder3></dataNode>
<dataNode name="dn4" dataHost="localhost1" database=VIPuser1></dataNode>
<dataNode name="dn5" dataHost="localhost1" database=VIPuser2></dataNode>
<dataNode name="dn6" dataHost="localhost1" database=VIPuser3></dataNode>
<dataNode name="dn7" dataHost="localhost1" database=VIPproduct1></dataNode>
<dataNode name="dn8" dataHost="localhost1" database=VIPproduct2></dataNode>
<dataNode name="dn9" dataHost="localhost1" database=VIPproduct3></dataNode>

<dataHost name="localhost1" balance=“1”>
   <heartbeat>select user()</heartbeat>      
   <writeHost host="host1"url="192.168.204.200:3306" user="taibai" password="123456">   
     <readHost host="host2"url="192.168.204.201:3306" user="root" password="123456">
   </writeHost>    
</dataHost>
mycat这个会映射到真实数据库中垂直切分的多个数据库,比如VIPorder1,VIPorder2,VIPorder3,table数据库中的表,primarykey代表表的主键,
datanode映射到真实数据库,database真实数据库名,heartbeat心跳语句,writehost写节点,readhost读节点,主从数据库,rule对应rule.xml里面的分片规则,balance=1代表读写分离
  • mycat分片规则
      • 为了能够执行t_user与t_user_detail的联合查询, MyCAT借鉴了NewSQL领域的新秀Foundation DB的设计思路,Foundation DB创新性的提出了Table Group的概念,其将子表的存储位置依赖于主表,并且物理上紧邻存放,因此彻底解决了JOIN的效率和性能问题,根据这一思路,提出了基于E-R关系的数据分片策略,子表的记录与所关联的父表记录存放在同一个数据分片上。
      • 以t_user与t_user_detail例子为例,schema.xml中定义如下的分片配置:
<table name="t_user" dataNode="dn$1-32" rule="mod-long">
    <childTable name="t_user_detail" primaryKey="id" joinKey="user_id" parentKey="user_id" />
</table>
      • t_user采用mod-long这个分片策略,分片在dn1-dn32上,t_user_detail依赖父表进行分片,两个表的关联关系为t_user_detail.user_id=t_user.id。于是数据分片和存储的示意图如下:
      • 这样一来,分片dn1-32上的t_user与hn1-32上的t_user_detail就可以进行局部的JOIN联合,再合并两个节点的数据即可完成整体的JOIN,试想一下,每个分片上t_user_detail表有1000万条,则10个分片就有1个亿,基于E-R映射的数据分片模式,基本上解决了80%以上的企业应用所面临的问题。
      • 多对多的表格如何处理?多对多的表格通常情况下,有以下几种
        • 主表+关系表+字典表
          • 字典表可以被定义为“全局表”,字典表的记录规模可以在几千到几十万之间,基本是变动比较少的表,由MyCAT自动实时同步到所有分片,这样就可以三个表都做JOIN操作了。
<table name="t_area" primaryKey="id" type="global" dataNode="dn1,dn2" />
        • 主表A+关系表+主表B
          • 暂时无法支持这种关系的rule
      • 主键分片 vs 非主键分片
        • 当你没人任何字段可以作为分片字段的时候,主键分片就是唯一选择,其优点是按照主键的查询最快,当采用自动增长的序列号作为主键时,还能比较均匀的将数据分片在不同的节点上。
        • 若有某个合适的业务字段比较合适作为分片字段,则建议采用此业务字段分片,选择分片字段的条件如下
          • 尽可能的比较均匀分布数据到各个节点上;
          • 该业务字段是最频繁的或者最重要的查询条件。
      • 分片规则–枚举法
        • 本规则适用于特定的场景,比如有些业务需要按照省 份或区县来做保存,而全国省份区县固定的,这类业务使用本条规则,配置如下
<tableRule name=”sharding-by-intfile”>
<rule>
    <columns>user_id</columns>
    <algorithm>hash-int</algorithm>
</rule>
</tableRule>
<function name=”hash-int” class=”org.opencloudb.route.function.PartitionByFileMap”>
<property name=”mapFile”>partition-hash-int.txt</property>
<property name=”type”>0</property>
<property name=”defaultNode”>0</property>
</function>

partition-hash-int.txt 配置:
10000=0
10010=1
DEFAULT_NODE=1

上面columns标识将要分片的表字段,algorithm分片函数。 
其中分片函数配置中
mapFile标识配置文件名称
type默认值为0,0表示Integer,非零表示String
defaultNode所有的节点配置都是从0开始,以及0代表节点1。

/**
 *defaultNode默认节点:小于0表示不设置默认节点,大于等于0表示设置默认节点
 *默认节点的作用:枚举分片时,如果碰到不识别的枚举值,就让它路由到默认节点。
 *如果不配置默认节点(defaultNode值小于0表示不配置默认节点),碰到不识别的枚*举值就会报错。
*like this:can’t find datanode for sharding column:column_name val:fffffff
 */

理解:
切分规则根据文件(partition-hash-int.txt)。此种分片规则理解为枚举分区,会比较适合于取值固定的场合,比如说性别(0,1),省份(固定值)。
优点:
用逗号分隔可以把多个值放在一个分区里面。
缺点:
其他非枚举情况不适合。
枚举分区:sharding-by-intfile
      • 分片规则–固定分片hash算法
        • 该算法类似于十进制的求模运算,但是为二进制的操作,例如,取 id 的二进制低 10 位 与 1111111111 进行 & 运算
        • 只能作用在纯数字的列上。好处可以理解,似乎只能适用在id之类的字段上。如文档中所说,适合在连续插入的时候使用。
<tableRule name=”rule1”>
<rule>
    <columns>user_id</columns>
    <algorithm>func1</algorithm>
</rule>
</tableRule>
<function name=”func1” class="org.opencloudb.route.function.PartitionByLong">
<property name=”partitionCount”>2,1</property>
<property name=”partitionLength”>256,512</property>
</function>
  • 配置说明:
    • 上面 columns 标识将要分片的表字段,algorithm 分片函数,partitionCount 分片个数列表,partitionLength分片范围列表分区长度:默认为最大2^n=1024,即最大支持1024分区。
  • 约束:
    • count,length两个数组的长度必须是一致的。
    • 1024 = sum((count[i] * length[i])).count和length两个向量的点积恒等于1024
  • 用法例子
    • 本例的分区策略:希望将数据水平分成3份,前两份各占25%,第三份占50%。(故本利非均匀分区)
// |<--------------------------1024----------------------------------->|
// |<------256----->|<------256----->|<--------------512-------------->|
// |     partition0 |     partition1 |            partition2          |

优点:
这种策略比较灵活,可以均匀分配也可以非均匀分配,各节点的分配比例和容量大小由count,length两个参数决定。
缺点:
跟求模法类似。
  • 分片规则–范围约定
<tableRule name="auto-sharding-long">
    <rule>
        <columns>user_id</columns>
        <algorithm>rang-long</algorithm>
    </rule>
</tableRule>
<function name="rang-long" class="org.opencloudb.route.function.AutoPartitionByLong">
    <property name="mapFile">autopartition-long.txt</property>
    <property name="defaultNode">0</property>
</function>

autopartition-long.txt
0-500M=0
500M-1000M=1
1000M-1500M=2

理解:
切分规则根据文件(autopartition-long.txt)。一种范围切分的方式,制定基准列的取值范围,然后把这一范围的所有数据都放到一个DN上面。
优点:
适用于整体数量可知或总数量为固定值的情况。
缺点:
dn划分节点是事先建好的,需要扩展时比较麻烦。
潜在的问题,如果在短时间发生海量的顺序插入操作,而每一个DN(分库)设定的数量比较高(比如说一个DN设定的放1000W条数据),那么在这个时候,会出现某一个DN(分库)IO压力非常高,而其他几个DN(分库)完全没有IO操作,就会出现类似于DB中常见的热块/热盘的现象。
  • 分片规则–取模
<tableRule name="mod-long">
<rule>
<columns>user_id</columns>
<algorithm>mod-long</algorithm>
</rule>
</tableRule>
<function name="mod-long" class="org.opencloudb.route.function.PartitionByMod">
<!-- how many data nodes  -->
<property name="count">3</property>
</function>

配置说明: 
上面 columns 标识将要分片的表字段
algorithm 分片函数,此种配置非常明确即根据 id 进行十进制求模预算,相比固定分片 hash,此种在批量插入时可能存在批量插入单 
事务插入多数据分片,增大事务一致性难度。
理解:
切分规则根据配置中输入的数值n。此种分片规则将数据分成n份(通常dn节点也为n),从而将数据均匀的分布于各节点上。
优点:
这种策略可以很好的分散数据库写的压力。比较适合于单点查询的情景。
缺点:
一旦出现了范围查询,就需要MyCAT去合并结果,当数据量偏高的时候,这种跨库查询+合并结果消耗的时间有可能会增加很多,尤其是还出现了order by的时候。
  • 分片规则–日期列分区法
<tableRule name="sharding-by-date">
      <rule>
        <columns>create_time</columns>
        <algorithm>sharding-by-date</algorithm>
      </rule>
   </tableRule> 
<function name="sharding-by-date" class="io.mycat.route.function..PartitionByDate">
   <property name="dateFormat">yyyy-MM-dd</property>
    <property name="sBeginDate">2014-01-01</property>
    <property name="sPartionDay">10</property>
  </function>
理解:
切分规则根据配置中输入的各项值。配置中配置了格式,开始日期,分区天数,即默认从开始日期算起,分隔10天一个分区。
配置说明: 
columns :标识将要分片的表字段 
algorithm :分片函数 
dateFormat :日期格式 
sBeginDate :开始日期 
sPartionDay :分区天数,即默认从开始日期算起,分隔 10 天一个分区
  • 分片规则–取模范围约束
    • 此种规则是取模运算与范围约束的结合,主要为了后续数据迁移做准备,即可以自主决定取模后数据的节点分布。
<tableRule name="sharding-by-pattern">
<rule>
<columns>user_id</columns>
<algorithm>sharding-by-pattern</algorithm>
</rule>
</tableRule>
<function name="sharding-by-pattern"
class="org.opencloudb.route.function.PartitionByPattern">
<property name="patternValue">256</property>
<property name="defaultNode">2</property>
<property name="mapFile">partition-pattern.txt</property>
</function>

partition-pattern.txt

# id partition range start-end ,data node index
###### first host configuration
1-32=0
33-64=1
121

65-96=2
97-128=3
######## second host configuration
129-160=4
161-192=5
193-224=6
225-256=7
0-0=7


理解:
切分规则根据配置中输入的数值以及文件(partition-pattern.txt)。
patternValue 即求模基数
defaoultNode 默认节点,如果不配置了默认,则默认是0即第一个结点。
配置文件中,1-32 即代表id%256后分布的范围,如果在1-32则在分区1,其他类推,如果id非数字数据,则会分配在defaoultNode 默认节点。
优点:
这种策略可以很好的分散数据库写的压力。比较适合于单点查询的情景。
缺点:
一旦出现了范围查询,就需要MyCAT去合并结果,当数据量偏高的时候,这种跨库查询+合并结果消耗的时间有可能会增加很多,尤其是还出现了order by的时候。
  • 分片规则–截取数字做 hash 求模范围约束
<tableRule name="sharding-by-prefixpattern">
<rule>
<columns>user_id</columns>
<algorithm>sharding-by-prefixpattern</algorithm>
</rule>
</tableRule>
<function name="sharding-by-pattern"
class="org.opencloudb.route.function.PartitionByPrefixPattern">
<property name="patternValue">256</property>
<property name="prefixLength">5</property>
<property name="mapFile">partition-pattern.txt</property>
</function>

partition-pattern.txt

# range start-end ,data node index
# ASCII
# 8-57=0-9阿拉伯数字
# 64、65-90=@、A-Z
# 97-122=a-z
###### first host configuration
1-4=0
5-8=1
9-12=2
13-16=3
###### second host configuration
17-20=4
21-24=5
25-28=6
29-32=7

配置说明: 
上面 columns 标识将要分片的表字段,algorithm 分片函数,patternValue 即求模基数,prefixLength 
ASCII 截取的位数 
mapFile 配置文件路径 
配置文件中,1-32 即代表 id%256 后分布的范围,如果在 1-32 则在分区 1,其他类推 
此种方式类似方式 6 只不过采取的是将列种获取前 prefixLength 位列所有 ASCII 码的和进行求模 
sum%patternValue ,获取的值,在范围内的分片数
  • 分片规则–应用指定
<tableRule name="sharding-by-substring">
<rule>
<columns>user_id</columns>
<algorithm>sharding-by-substring</algorithm>
</rule>
</tableRule>
<function name="sharding-by-substring"
class="org.opencloudb.route.function.PartitionDirectBySubString">
<property name="startIndex">0</property><!-- zero-based -->
<property name="size">2</property>
<property name="partitionCount">8</property>
<property name="defaultPartition">0</property>
</function>

配置说明: 
上面 columns 标识将要分片的表字段,algorithm 分片函数 
此方法为直接根据字符子串(必须是数字)计算分区号(由应用传递参数,显式指定分区号)。 
例如 id=05-100000002 
在此配置中代表根据 id 中从 startIndex=0,开始,截取 siz=2 位数字即 05,05 就是获取的分区,如果没传默认分配到 defaultPartition
  • 分片规则–截取数字hash解析
    • 此规则是截取字符串中的int数值hash分片
<tableRule name="sharding-by-stringhash">
<rule>
    <columns>user_id</columns>
    <algorithm>sharding-by-stringhash</algorithm>
</rule>
</tableRule>
<function name="sharding-by-stringhash"
class="org.opencloudb.route.function.PartitionByString">
<property name="partitionLength">512</property><!-- zero-based -->
<property name="partitionCount">2</property>
<property name="hashSlice">0:2</property>
</function>

配置说明: 
上面 columns 标识将要分片的表字段,algorithm 分片函数 
函数中partitionLength 代表字符串 hash 求模基数, 
partitionCount 分区数, 
hashSlice hash 预算位,即根据子字符串中 int 值 hash 运算 
hashSlice : 0 means str.length(), -1 means str.length()-1

/** 
* “2” -> (0,2) 
* “1:2” -> (1,2) 
* “1:” -> (1,0) 
* “-1:” -> (-1,0) 
* “:-1” -> (0,-1) 
* “:” -> (0,0) 
*/
  • 分片规则–一致性hash
<tableRule name="sharding-by-murmur">
<rule>
<columns>user_id</columns>
<algorithm>murmur</algorithm>
</rule>
</tableRule>
<function name="murmur" class="org.opencloudb.route.function.PartitionByMurmurHash">
<property name="seed">0</property><!--默认是0-->
<property name="count">2</property><!--要分片的数据库节点数量,必须指定,否则没法分片-->
<property name="virtualBucketTimes">160</property><!--一个实际的数据库节点被映射为这么多虚拟
节点,默认是160倍,也就是虚拟节点数是物理节点数的160倍-->
<!--
<property name="weightMapFile">weightMapFile</property>
节点的权重,没有指定权重的节点默认是1。以properties文件的格式填写,以从0开始到count-1的整数值也就
是节点索引为key,以节点权重值为值。所有权重值必须是正整数,否则以1代替   -->
<!--
<property name="bucketMapPath">/etc/mycat/bucketMapPath</property>
用于测试时观察各物理节点与虚拟节点的分布情况,如果指定了这个属性,会把虚拟节点的murmur hash值与物理节
点的映射按行输出到这个文件,没有默认值,如果不指定,就不会输出任何东西  -->
</function>
优点:
一致性hash预算有效解决了分布式数据的扩容问题,前1-9中id规则都多少存在数据扩容难题,而10规则解决了数据扩容难点
  • 进入安装的MyCat目录,添加tableRule:
<!--针对项目做自定义配置-->
<tableRule name="mod50-long">
<rule>
<columns>id</columns>
        <algorithm>mod50-long</algorithm>
    </rule>
</tableRule>
  • 添加function内容:
<function name="mod50-long" class="io.mycat.route.function.PartitionByMod">
<!-- how many data nodes -->
    <property name="count">50</property>
</function>
  • schema.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://org.opencloudb/">

    <!--schema就是逻辑库,相当于MySQL实例的数据库,一个MySQL实例可以有多个数据库,同样的一个MyCat实例也可以有多个schema-->
    <!-- checkSQLschema就是打开SQL语句检查,把带schema名字的查询改写成不带的,一般查询最好不要带schema名字 -->
    <!--sqlMaxLimit每条执行的SQL语句,如果没有加上limit语句,MyCat也会自动的加上所对应的值-->
    <schema name="schema1" checkSQLschema="false" sqlMaxLimit="10000" dataNode="test4">
        <!-- 运单表,分片列在rule.xml配置,这里正好就是主键id,所以分片规则是主键id对3取模-->
        <table name="orders" primaryKey="id" dataNode="test$1-3" rule="mod-long-orders">
            <!-- 运单子母件表,运单表的子表,order_id与orders的id列对应 -->
            <childTable name="orders_cargo" joinKey="order_id" parentKey="id">
            </childTable>
            <!-- 客户运单关系表,运单表的子表,order_id与orders的id列对应 -->
            <childTable name="customer_order_rel" joinKey="order_id" parentKey="id">
            </childTable>
            <!-- 快递员运单关系表,运单表的子表,order_id与orders的id列对应 -->
            <childTable name="courier_order_rel" joinKey="order_id" parentKey="id">
            </childTable>
        </table>
        <!-- 运单状态信息表,公共表,放在和运单表同样的分片上 -->
        <table name="order_status_interception" primaryKey="id" type="global" dataNode="test$1-3">
        </table>
        <!-- 快递员表,非分片表 -->
        <table name="courier" primaryKey="id" dataNode="test4">
        </table>
        <!-- 客户表,对主键id对2取模 -->
        <table name="customer" primaryKey="id" dataNode="test$5-6" rule="mod-long-customer">
        </table>
    </schema>


    <!-- 规定dataNode,就是分片的位置-->
    <dataNode name="test1" dataHost="test" database="db1" />
    <dataNode name="test2" dataHost="test" database="db2" />
    <dataNode name="test3" dataHost="test" database="db3" />
    <dataNode name="test4" dataHost="test" database="db4" />
    <dataNode name="test5" dataHost="test" database="db5" />
    <dataNode name="test6" dataHost="test" database="db6" />

    <!-- 规定每个分片host的读写服务器以及登录用户名密码,还有心跳语句-->
    <dataHost name="test" maxCon="1000" minCon="10" balance="0"
              writeType="0" dbType="mysql" dbDriver="native" switchType="-1"  slaveThreshold="100">
        <heartbeat>select 1 from dual</heartbeat>
        <writeHost host="test" url="10.202.4.181:3306" user="test" password="test">
            <readHost host="slave" url="10.202.4.181:3307"  user="root" password="sf123456"/>           
        </writeHost>
    </dataHost>


</mycat:schema>
  • 利用mycat的catlets跨片join
跨分片联合查询注解支持
/*!mycat:catlet=io.mycat.catlets.ShareJoin */
SELECT * from t_service s,t_user u ON u.u_id=s.s_uid;
说明:目前只支持两张分片表的Join,如果要支持多张表需要自己改造程序代码或者改造Mycat的源代码
对应Mycat源码:
io.mycat.catlets.ShareJoin
io.mycat.catlets.Catlet 
public class ShareJoin implements Catlet

批量插入与ID自增长结合的支持
/*!mycat:catlet=io.mycat.route.sequence.BatchInsertSequence */insert into user(name) values('Tom'),('Cat'),('Alan');
  • 利用mycat的分页
在对应的分片上去查询分页数据的时候是从第一条记录开始扫描,然后再取出对应的分页数据,如
SELECT * FROM customer ORDER BY id LIMIT 1000100, 100;

这个sql语句被Mycat转化后
1 -> dn1{SELECT * FROM customer ORDER BY id LIMIT 0, 1000100}
2 -> dn2{SELECT * FROM customer ORDER BY id LIMIT 0, 1000100}

所以要在Mycat的server.xm里面开启使用非堆内存。否则内存会爆掉
<property name="useOffHeapForMerge">1</property>


优化:

1)先查出id
SELECT id FROM customer ORDER BY id LIMIT 1000100, 100;

这个sql语句被mycat转化后
1 -> dn1{SELECT  id  FROM customer ORDER BY id LIMIT 0, 1000100}
2 -> dn2{SELECT  id  FROM customer ORDER BY id LIMIT 0, 1000100}

2) 拿到所有的id以后再取获取需要的数据
SELECT * FROM customer where id in(1,2,3....);
这个sql语句被mycat转化后

1 -> dn1{SELECT * FROM customer where id in(1,2,3....);}
2 -> dn2{SELECT * FROM customer where id in(1,2,3....);}
  • 利用mycat的注解
语法:
/*!mycat:sql=Mycat注解SQL语句*/真正执行的SQL   !号方式
/*#mycat:sql=Mycat注解SQL语句*/真正执行的SQL  #号方式
/**mycat:sql=Mycat注解SQL语句*/真正执行的SQL   *号方式

Mycat注解规范:

1) 注解SQL使用select语句,不允许使用delete/update/insert等语句;虽然delete/update/insert等语句也能用在注解中,但这些语句在Sql处理中有额外的逻辑判断,从性能考虑,请使用select语句。
2) 注解SQL禁用表关联语句。
3) 注解SQL尽量用最简单的SQL语句,如select id from tab_a where id=’10000’(如果必要,最好能在注解中指定分片)
4) 无论是原始SQL 还是注解SQL,禁止DDL语句 
5) 能不用注解的尽量不用

Mycat注解解决不支持insert into......select.....
/*!mycat:sql=select 1*/insert into travelrecord(id,user_id,traveldate,fee,days) select 3,'Tom','20180826',100,8;

Mycat注解创建表
/*!mycat:sql=select 1 from test */create table test2(id int);

Mycat注解创建存储过程
/*!mycat:sql=select 1 from test */create procedure 'test_proc()' begin end;

Mycat注解调用存储过程
/*!mycat:sql=select * from user where id=1 */call test_proc();
注:目前执行存储过程通过mycat注解的方式执行,注意需要把存储过程中的sql写到注解中; 

Mycat注解读写分离数据源选择
/*!mycat:db_type=master */select * from travelrecord;(强制走主库)
/*!mycat:db_type=slave */select * from travelrecord;(强制走从库)
  • MyCAT定义了一种特殊的表,称之为“全局表”,全局表具有以下特性:
    • 全局表的插入、更新操作会实时在所有节点上执行,保持各个分片的数据一致性
    • 全局表的查询操作,只从一个节点获取
    • 全局表可以跟任何一个表进行JOIN操作
  • 全局表的配置
先在server.xml里面全局表一致性检测
<property name="useGlobleTableCheck">1</property>  <!-- 1为开启全局表一致性检测、0为关闭 -->
在schema.xml里面配置全局表
<table name="company" primaryKey="ID" type="global" dataNode="dn1,dn2,dn3" />
  • 水平切分和垂直切分
  • 垂直切分是根据业务来拆分数据库,同一业务的数据库拆分到一个独立的数据库,另一类业务拆分到其他数据库
  • 垂直切分解决的问题,降低单节点数据库的负载,原来所有的数据表都放在一个数据库节点,所有的读写请求都发到这台MySQL上面,这台数据库负载很高,拆分出去之后降低负载
  • 垂直切分不能解决的问题是缩表,无论划分给那个数据库节点,商品表记录还是那么多,数据量是没有变化的
  • 水平切分是把数据表某个字段按照规则拆分,数据分到多张表,可以起到每张表缩表的效果
  • 水平切分扩容很麻烦,需要定期的做归档
  • 系统最好先水平切分,然后垂直切分,随着数据量变大,需要分片利用多块硬盘增大数据io和存储能力,随着数据量越来越大,需要按照业务拆分系统,垂直切分
  • 执行计划
    • id介绍这个id不是主键的意思,他是用来标识select查询的序列号,包含一组数字,表示查询中执行select子句或者操作表的顺序。会出现以下情况:
      • id相同:按从上到下顺序执行id不同:
      • id值越大,优先级越高,越先被执行
      • id相同不同的同时存在:优先执行id值大的,如果id值相同,则按从上到下的顺序执行
      • id为null表示是用来合并结果集的,在sql使用union关键字合并结果集就会出现他。
    • select_type表示查询中每个select子句的类型(简单 OR复杂)
      • SIMPLE:查询中不包含子查询或者UNION
      • 查询中若包含任何复杂的子部分,最外层查询则被标记为:PRIMARY
      • 在SELECT或WHERE列表中包含了子查询,该子查询被标记为:SUBQUERY
      • 在FROM列表中包含的子查询被标记为:DERIVED(衍生)
      • 若第二个SELECT出现在UNION之后,则被标记为UNION
      • 若UNION包含在 FROM子句的子查询中,外层SELECT将被标记为:DERIVED
      • 从UNION表获取结果的SELECT被标记为:UNION RESULT
    • table 就是表名
    • type
      • system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL表示MySQL在表中找到所需行的方式,又称“访问类型”,常见类型如下:由上至下,由最差到最好
      • ALL:全表扫描无疑是最差,若是百万千万级数据量,全表扫描会非常慢
      • index:全索引文件扫描比all好很多,毕竟从索引树中找数据,比从全表中找数据要快
      • range:只检索给定范围的行,使用索引来匹配行。范围缩小了,当然比全表扫描和全索引文件扫描要快。sql语句中一般会有between,in,>,< 等查询
      • ref:非唯一性索引扫描,本质上也是一种索引访问,返回所有匹配某个单独值的行。比如查询公司所有属于研发团队的同事,匹配的结果是多个并非唯一值
      • eq_ref::唯一性索引扫描,对于每个索引键,表中有一条记录与之匹配。比如查询公司的CEO,匹配的结果只可能是一条记录
      • const:表示通过索引一次就可以找到,const用于比较primary key 或者unique索引。因为只匹配一行数据,所以很快,若将主键至于where列表中,MySQL就能将该查询转换为一个常量。
      • system:表只有一条记录(等于系统表),这是const类型的特列,平时不会出现,了解即可
      • NULL:MySQL在优化过程中分解语句,执行时甚至不用访问表或索引
    • possible_keys
      • 指出MySQL能使用哪个索引在表中找到行,查询涉及到的字段上若存在索引,则该索引将被列出,但不一定被查询使用
    • key
      • 显示MySQL实际决定使用的键(索引)。如果没有选择索引,键是NULL
    • key_len
      • 显示MySQL决定使用的键长度。如果键是NULL,则长度为NULL。使用的索引的长度。在不损失精确性的情况下,长度越短越好。key_len 显示的值为索引字段的最可能长度,并非实际使用长度,即key_len是根据表定义计算而得,并不是通过表内检索出的。
    • ref
      • 显示使用哪个列或常数与key一起从表中选择行
    • rows
      • 显示MySQL认为它执行查询时必须检查的行数,值越大越不好。
    • Extra:包含MySQL解决查询的详细信息,也是关键参考项之一
      • Distinct
        • 一旦MYSQL找到了与行相联合匹配的行,就不再搜索了
      • Not exists
        • MYSQL 优化了LEFT JOIN,一旦它找到了匹配LEFT JOIN标准的行,就不再搜索了Range checked for eachRecord(index map:#)没有找到理想的索引,因此对于从前面表中来的每一 个行组合,MYSQL检查使用哪个索引,并用它来从表中返回行。这是使用索引的最慢的连接之一Using filesort看到这个的时候,查询就需要优化了。MYSQL需要进行额外的步骤来发现如何对返回的行排序。它根据连接类型以及存储排序键值和匹配条件的全部行的行指针来排序全部行
      • Record(index map:#)
        • 没有找到理想的索引,因此对于从前面表中来的每一 个行组合,MYSQL检查使用哪个索引,并用它来从表中返回行。这是使用索引的最慢的连接之一
      • Using filesort
        • 看到这个的时候,查询就需要优化了。MYSQL需要进行额外的步骤来发现如何对返回的行排序。它根据连接类型以及存储排序键值和匹配条件的全部行的行指针来排序全部行
      • Using index
        • 列数据是从仅仅使用了索引中的信息而没有读取实际的行动的表返回的,这发生在对表的全部的请求列都是同一个索引的部分的时候。
          • 覆盖索引(Covering Index) :也叫索引覆盖,就是select 的数据列只用从索引中就能够取得,不必读取数据行,MySQL可以利用索引返回select 列表中的字段,而不必根据索引再次读取数据文件。
      • Using where
        • 表示MySQL服务器在存储引擎受到记录后进行“后过滤”(Post-filter),如果查询未能使用索引,Using where的作用只是提醒我们MySQL将用where子句来过滤结果集
      • Using temporary看到这个的时候,查询需要优化了。这里,MYSQL需要创建一个临时表来存储结果,这通常发生在对不同的列集进行ORDER BY上,而不是GROUP BY上
      • Using where使用了WHERE从句来限制哪些行将与下一张表匹配或者是返回给用户。如果不想返回表中的全部行,并且连接类型ALL或index, 这就会发生,或者是查询有问题
    • char和varchar区别
      • char是固定的,定义char(10),存入“csdn”,后面补足6个空格,虽然补足空格,但是因为是固定长度,查询速度快,但是消耗空间
      • varchar是不固定的,定义varchar(10),存入“csdn”,后面不会补,没有补足空间,空间节省了,因为长度不一,查询速度降低
      • char存入英文字符占用1个字节,存入汉子占用2个字节
      • varchar存入英文字符占用2个字节,存入汉子占用2个字节
      • 两者都是存入非unicode的字符数据
  • show engine innodb status 查看死锁状态
  • 共享锁【S锁】
    • 又称读锁,若事务T对数据对象A加上S锁,则事务T可以读A但不能修改A,其他事务只能再对A加S锁,而不能加X锁,直到T释放A上的S锁。
    • 这保证了其他事务可以读A,但在T释放A上的S锁之前不能对A做任何修改。
  • 排他锁【X锁】
    • 又称写锁。若事务T对数据对象A加上X锁,事务T可以读A也可以修改A,其他事务不能再对A加任何锁,直到T释放A上的锁。
    • 这保证了其他事务在T释放A上的锁之前不能再读取和修改A。
    原文作者:知识海洋
    原文地址: https://zhuanlan.zhihu.com/p/68425399
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞