range

If there were you, the world would be just right

1 查看自己的binlog日志目录

2 查看最新更新的binlog日志文件,例如我当前最新为 mysql-bin.000007

3 导出为可视化数据 注意自己的 mysqlbinlog 执行文件目录

/data/mysql/bin/mysqlbinlog --base64-output=DECODE-ROWS -v mysql-bin.000007 > /data/back.txt

4 下载back.txt,编辑器打开对数据进行筛选, 搜索被误操作表名,搜全部,copy整行,粘贴到新的文件页面,每行后面添加分号, 去掉最后误操作的delete语句,然后丢回去mysql执行

5 不出意外已修复


<?php

$db = new ran_pdo("qx_yisdk_user",true); 

$tables = [
    "yisdk_user",
];

foreach ($tables as $tab) {
    $count_sql = 'select count(*) c from yisdk_user where last_login_time>=1602518400 and uuid!=MD5(concat("new_",CONCAT_WS("_",channel,pf_game_id,user_id)))'; 

    $row = $db->get_one($count_sql);
    // 需要执行的条数
    $count = !empty($row["c"]) ? $row["c"] : 0;

    // 每次执行的条数 1000
    $do_num = 100;

    // 需要执行的次数
    $dc = ceil($count/$do_num);

    if($dc) for ($i=0; $i < $dc; $i++) { 
        $p = $i * $do_num;
        $sql = "select uuid,MD5(concat('new_',CONCAT_WS('_',channel,pf_game_id,user_id))) as new_uuid from yisdk_user where last_login_time>=1602518400 and uuid!=MD5(concat('new_',CONCAT_WS('_',channel,pf_game_id,user_id))) limit {$p},{$do_num}";

        $data = $db->get_all($sql);

        $update_sql = "UPDATE {$tab} a JOIN  (";
        $k = 0;
        foreach ($data as $key => $val) {

                $k = 1;
                $update_sql .= " SELECT '{$val['uuid']}' as uuid,'{$val['new_uuid']}' AS new_uuid UNION";

        }

        $sql = rtrim($update_sql,"UNION"); 
        $sql .= ") b USING(uuid) SET a.uuid=b.new_uuid";
        echo $sql;die;
        if($k){
            $db->query($sql);
        }
        sleep(1);
    }
}

UPDATE ucenter_user_money a, (select user_id,openid,unionid from ucenter_user_details where openid!='') b SET a.openid = b.openid,a.unionid=b.unionid WHERE a.user_id = b.user_id


结构与访问过程

MySQL = 客户端 + 服务端
客户端 = Connection(语言连接器例如:PHP-pdo,MySQLi)
服务端 = SQL层 + 存储引擎层
SQL层 = 链接/线程处理 + 查询缓存 + 分析器 + 优化器
存储引擎 = InnoDB + MariaDB + ...

一、系统文件

MySQL物理文件类型
    日志文件主要包括: 
        1. 错误日志(Error Log) 
        2. 二进制日志(Binary Log)
        3. 事务日志(InnoDB redo Log & undo Log)
        4. 慢查询日志(Slow Query Log)
        5. 查询日志(Query Log)
    数据库文件
        1. '.frm'文件 
        2. '.MYD'文件 
        3. '.MYI'文件 
        4. '.ibd'文件和'.ibdata'文件
    其他配置文件
        1. 系统配置文件(my.ini或者my.cnf)
        2. pid,err文 件 
        3. socket文 件

二、锁机制

1. MyISAM 和 Memory 存储引擎使用的是表级锁,BDB 引擎使用的是页级锁,也支持表级锁。
2. InnoDB 存储引擎既支持行级锁,也支持表级锁,默认情况下使用行级锁。 

    所谓表级锁,它直接锁住的是一个表,开销小,加锁快,不会出现死锁的情况,锁定粒度大,发生锁冲突的概率更高,并发度最低。 
    所谓行级锁,它直接锁住的是一条记录,开销大,加锁慢,发生锁冲突的概率较低,并发度很高。 
    所谓页级锁,它是锁住的一个页面,它的开销介于表级锁和行级锁中间,也可能会出现死锁,锁定粒度也介于表级锁和行级锁中间,并发度也介于表级锁和 行级锁中间。

    行级锁更适合大量按照索引条件并发更新少量不同的数据,同时还有并发查询的应用

2.1. 行级锁 
    1. 行级锁本身与表级锁的实现差别就很大,而事务的引入也带来了很多新问题,尤其是事务的隔离性,与锁机制息息相关。 
    2. 对于事务的基本操作,对于不同隔离级别可能引发的问题,像脏读、不可重复读等问题。 
    3. 数据库实现事务隔离的方式,基本可以分为两种: 
        (1)在操纵数据之前,先对其加锁,防止其他事务对数据进行修改。这就需要各个事务串行操作才可以实现。
        (2)不加任何锁,通过生成一系列特定请求时间点的一 致性数据快照,并通过这个快照来提供一致性读取 
    4. 上面的第二种方式就是数据多版本并发控制,也就是多版本数据库,一般简称为 MVCC 或者 MCC,它是 Multi Version Concurrency Control 的简写。 
    5. 数据库的事务隔离越严格,并发的副作用就越小,当然付出的代价也就越大,因为事务隔离机制实质上是使得事务在一定程度上”串行化”,这与并行是矛盾的 

    共享锁语句 
        select * from table_name lock in share mode; 
    排他锁语句 
        select * from table_name for update;

2.2 死锁
    为什么会死锁?
        官方定义:两个事务都持有对方需要的锁,并且在等待对方释放,并且双方都不会释放自己的锁。
    死锁如何处理?
        等待,直到超时(innodb_lock_wait_timeout=50s)
        发起死锁检测,主动回滚一条事务,让其他事务继续执行(innodb_deadlock_detect=on)
        由于性能原因,一般都是使用死锁检测来进行处理死锁。
    收集死锁信息:
        利用命令 SHOW ENGINE INNODB STATUS查看死锁原因。 
        调试阶段开启 innodb_print_all_deadlocks,收集所有死锁日志。
    减少死锁:
        使用事务,不使用 lock tables 。 
        保证没有长事务。 
        操作完之后立即提交事务,特别是在交互式命令行中。 
        如果在用 (SELECT ... FOR UPDATE or SELECT ... LOCK IN SHARE MODE),尝试降低隔离级别。
        修改多个表或者多个行的时候,将修改的顺序保持一致。 
        创建索引,可以使创建的锁更少。 
        最好不要用 (SELECT ... FOR UPDATE or SELECT ... LOCK IN SHARE MODE)。 
        如果上述都无法解决问题,那么尝试使用 lock tables t1, t2, t3 锁多张表

三、事务

事务处理可以确保除非事务性单元内的所有操作都成功完成,否则不会永远更新面向数据的资源。通过将一组相关操作组合为一个要么全部成功要么全部失败的 单元,可以简化错误恢复病史应用程序更加可靠。一个逻辑共奏单元要成为事务,必须满足所谓的ACID(原子性、一致性、隔离性和持久性)属性:
1. 原子性(A) 对于数据修改,要么全部都执行,要么全都不执行。 
2. 隔离性(C) 在所有的操作没有执行完毕之前,其他会话不能够看到中间改变的过程 
3. 一致性(I) 事务发生前和发生后,根据数据的规则,总额应该匹配 
4. 持久性(D) 事务一旦被提交,其结果就是永久性的,系统崩溃也不会影响

四、 索引

索引的结构,建立索引的注意点,回表问题
4.0. 索引结构
    平衡二叉树
         索引的结构叶子片的数据存储,以及数据新增与修改后的结构调整过程,不同数据类型的处理
4.1. 索引类型
    4.1.1. 主键索引和普通索引
        innodb存储引擎 =》主键索引与普通区别就在于:主键索引找的时候数据在磁盘的存储位置,而普通索引查找的是主键索引的位置 
        myisam存储引擎 =》主键索引与普通索引查找都是数据在磁盘空间的存储位置
    4.1.2. 单索引与联合索引: 
        单索引: 就是一个字段简历的索引 
        联合索引:就是多个字段建立的索引 (实际工作推荐使用联合索引) 
    4.1.3. 覆盖索引与回表 
    覆盖索引:就是查询的sql所有的字段都是在索引上
    回表:就是sql获取的字段没有在普通索引上,就需要去查询主键索引因此存在回表问题 (需要关注,数据量查询 30% 以内) 
4.2. 扩展 innodb的行锁实现原理 :
    基于索引定位数据 -》 锁住数据

五、 架构演变

单体架构的问题 
    1. 数据库读写瓶颈问题;
    2. 数据备份的问题; 
    3. 容灾处理问题;
    4. 数据量剧增读写问题

5.1. 主从复制 (关注):
    MySOL支持一台主服务器同时向多台从服务器进行复制操作,从服务器同时可以作为其他从服务器的主服务器,如果MySQL主服务器访问量比较大,可以通过复制数据,然后在从同各器上进行查询操作,从而降低主服 务器的访问压力,同时从服务器作为主服务器的备份,可以避免主服务器因为故障数据丢失的问题。

    延时问题,实现原理,搭建过程

    主从MySQL好处: 
        1. 性能问题--不一定提高; 
        2. 数据冗余

    注意:主从无法提高数据库的读写速度
     
5.2. 集群
    延时问题,故障转移过程 
    MySQL集群实则是在主从的基础上进行扩展,如双主双从的架构,再利用Keepalived进行MySQL集群负载均衡与故障转移;

    分库分表
        IO瓶颈
            第一种:磁盘读IO瓶颈,热点数据太多,数据库缓存放不下,每次查询时会产生大量的IO,降低查询速度 -> 分库和垂直分表。 
            第二种:网络IO瓶颈,请求的数据太多,网络带宽不够 -> 分库。
            mysql io 定位工具 :https://yq.aliyun.com/articles/603735?utm_content=m_1000003799
        CPU瓶颈            
            第一种:SQL问题,如SQL中包含join,group by,order by,非索引字段条件查询等,增加CPU运算的操作 -> SQL优化,建立合适的索引,在业务Service层进行业务计算。 
            第二种:单表数据量太大,查询时扫描的行太多,SQL效率低,CPU率先出现瓶颈 -> 水平分表。

分表?
将单个大的数据库表进行拆分,拆分成多个数据表,然后用户访问的时候,根据一定的算法(hash/取模),让用户访问不同的表,这样数据分散到多个数据表中,减少了单个数据表的访问压力,缩短查询时间提升了数据库访问性能。

场景1:
1000万用户user表,拆分为100个分表,命名 user_00 - user_99 ,通过用户id 取模的方法把数据分散到这100个表中
场景2:
用户登录操作日志类,日流量高达500万+,通过使用日期分表方法,将数据按日期保存表名 xxlog_20190918

分表缺点
查询汇总数据难度增大

分区

  • 逻辑上表仍然是一张完整的表,只是将表中的数据在物理上存放到多个表空间(物理文件)
  • 一个表最多只能有1024个分区。
  • 如果分区字段中有主键或者唯一索引的列,那么多有主键列和唯一索引列都必须包含进来。
    即:分区字段要么不包含主键或者索引列,要么包含全部主键和索引列。
  • 分区表中无法使用外键约束

分区类型

  • RANGE分区:基于属于一个给定连续区间的列值,把多行分配给分区。
  • LIST分区:类似于按RANGE分区,区别在于LIST分区是基于列值匹配一个离散值集合中的某个值来进行选择。
  • HASH分区:基于用户定义的表达式的返回值来进行选择的分区,该表达式使用将要插入到表中的这些行的列值进行计算。这个函数可以包含MySQL 中有效的、产生非负整数值的任何表达式。
  • KEY分区:类似于按HASH分区,区别在于KEY分区只支持计算一列或多列,且MySQL服务器提供其自身的哈希函数。必须有一列或多列包含整数值。

建立分区表注意:

  • 顺应MYSQL的要求,primary key和unique key必须包含在分区key的一部分 所以把分区字段加入到主键中,组成复合主键
  • 查询sql 必须走分区键,避免查所有区
  • 在where语句中包含分区条件时,可以只扫描一个或多个分区表来提高查询效率;
  • 插入数据,如果数据对应本该插入的分区不存在会报错
  • 单张表最多是只能有1024个分区的(所以不按日做表)

分区选择

这个看个人业务,此处主要说我用到的

1、list 分区键必须为整数类型( 弃用 )

2、RANGE 区间范围( 与日期相关的统一采用range )

#维护相关
  查分区内容
    SELECT * FROM tableName PARTITION (p1,p2)
  添加分区:
    alter table tableName add partition (partition p0 values less than(1991));  //只能添加大于分区键的分区  
  删除分区数据
    alter table tableName  truncate partition p1,p2;  
    alter table tableName  truncate partition all; 
  删除分区
    alter table tableName drop partition p0; //可以删除任意分区

3、hash 分区键必须为整数类型( 可用hash则不用key,userId 为整形选择hash作为用户表分区 )

TIPS:
1)hash的分区必须是整数列
2)drop partition命令只能用在RANGE和LIST分区中。
3)分区的字段必须是要包含在主键字段之内,不然会报错

维护相关
  添加分区:
    ALTER TABLE tableName ADD PARTITION PARTITIONS 6; // 加6个区
  减少分区:
    ALTER TABLE tableName COALESCE PARTITION 4; // 减4个区
  移除表的分区
    ALTER TABLE tablename REMOVE PARTITIONING ;

注意:使用remove移除分区是仅仅移除分区的定义,并不会删除数据和drop PARTITION不一样,后者会连同数据一起删除

4、key 分区键字段不限制为整数类型( 设备为字符串可选择key分区 )

TIPS:
1) KEY分区支持除text和BLOB之外的所有数据类型的分区
2) 数据会分布不均(多次测试结果,创建分区个数为奇数 如99,可使数据相对分布更均匀)
3) 无法直接定位数据在哪个分区