range

If there were you, the world would be just right

1 电脑打开软件
2 手机连接wifi与电脑同一网段网络,wifi设置代理到电脑
3 下载安装证书 手机浏览器打开:http://www.charlesproxy.com/getssl
4 设置 代理-SSL代理-启用SSL代理-主机添加* 端口添加443


结构与访问过程

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率先出现瓶颈 -> 水平分表。

记录AES加密算法

<?php

namespace Aes;
 
class Aes
{
    /**
     * var string $method 加解密方法,可通过openssl_get_cipher_methods()获得
     */
    protected $method;
 
    /**
     * var string $secret_key 加解密的密钥
     */
    protected $secret_key;
 
    /**
     * var string $iv 加解密的向量,有些方法需要设置比如CBC
     */
    protected $iv;
 
    /**
     * var string $options (不知道怎么解释,目前设置为0没什么问题)
     */
    protected $options;
 
    /**
     * 构造函数
     *
     * @param string $key 密钥
     * @param string $method 加密方式
     * @param string $iv iv向量
     * @param mixed $options 还不是很清楚
     *
     */
    public function __construct($key, $method = 'AES-128-ECB', $iv = '', $options = 0)
    {
        // key是必须要设置的
        $this->secret_key = isset($key) ? $key : 'morefun';
 
        $this->method = $method;
 
        $this->iv = $iv;
 
        $this->options = $options;
    }
 
    /**
     * 加密方法,对数据进行加密,返回加密后的数据
     *
     * @param string $data 要加密的数据
     *
     * @return string
     *
     */
    public function encrypt($data)
    {
        return openssl_encrypt($data, $this->method, $this->secret_key, $this->options, $this->iv);
    }
 
    /**
     * 解密方法,对数据进行解密,返回解密后的数据
     *
     * @param string $data 要解密的数据
     *
     * @return string
     *
     */
    public function decrypt($data)
    {
        return openssl_decrypt($data, $this->method, $this->secret_key, $this->options, $this->iv);
    }
}

demo:

class Rsa
{
    private $_config = [
        'public_key' => '',
        'private_key' => '',
    ];

    public function __construct($private_key_filepath, $public_key_filepath) {
        $this->_config['private_key'] = $this->_getContents($private_key_filepath);
        $this->_config['public_key'] = $this->_getContents($public_key_filepath);
    }

    /**
     * @uses 获取文件内容
     * @param $file_path string
     * @return bool|string
     */
    private function _getContents($file_path) {
        file_exists($file_path) or die ('密钥或公钥的文件路径错误');
        return file_get_contents($file_path);
    }

    /**     
     * @uses 获取私钥
     * @return bool|resource     
     */ 
    private function _getPrivateKey() {
       $priv_key = $this->_config['private_key'];
       return openssl_pkey_get_private($priv_key);
    }

    /**     
     * @uses 获取公钥
     * @return bool|resource     
     */    
    private function _getPublicKey() {        
        $public_key = $this->_config['public_key'];
        return openssl_pkey_get_public($public_key);
    }

    /**     
     * @uses 私钥加密
     * @param string $data     
     * @return null|string     
     */    
    public function privEncrypt($data = '') {        
        if (!is_string($data)) {
            return null;       
        }
        return openssl_private_encrypt($data, $encrypted, $this->_getPrivateKey()) ? base64_encode($encrypted) : null;
    }

    /**     
     * @uses 公钥加密     
     * @param string $data     
     * @return null|string     
     */    
    public function publicEncrypt($data = '') {        
        if (!is_string($data)) {
            return null;        
        }        
        return openssl_public_encrypt($data, $encrypted, $this->_getPublicKey()) ? base64_encode($encrypted) : null;
    }

    /**     
     * @uses 私钥解密     
     * @param string $encrypted     
     * @return null     
     */    
    public function privDecrypt($encrypted = '') {        
        if (!is_string($encrypted)) {
            return null;        
        }
        return (openssl_private_decrypt(base64_decode($encrypted), $decrypted, $this->_getPrivateKey())) ? $decrypted : null;
    }    

    /**     
     * @uses 公钥解密     
     * @param string $encrypted     
     * @return null     
     */    
    public function publicDecrypt($encrypted = '') {        
        if (!is_string($encrypted)) {
            return null;        
        }        
           return (openssl_public_decrypt(base64_decode($encrypted), $decrypted, $this->_getPublicKey())) ? $decrypted : null;
    }
}

测试:

$private_key = 'private_key.pem'; // 私钥路径
$public_key = 'rsa_public_key.pem'; // 公钥路径
$rsa = new Rsa($private_key, $public_key);

$origin_data = '这是一条测试数据';

$ecryption_data = $rsa->privEncrypt($origin_data);

$decryption_data = $rsa->publicDecrypt($ecryption_data);

echo '私钥加密后的数据为:' . $ecryption_data;
echo PHP_EOL;
echo '公钥解密后的数据为: ' . $decryption_data;
echo PHP_EOL;

最后要说明的是,公钥、私钥都可以加密,也都可以解密。其中:用公钥加密需要私钥解密,称为“加密”。由于私钥是不公开的,确保了内容的保密,没有私钥无法获得内容;用私钥加密需要公钥解密,称为“签名”。由于公钥是公开的,任何人都可以解密内容,但只能用发布者的公钥解密,验证了内容是该发布者发出的。


php 导出

下载PHPExcel

composer require phpoffice/phpexcel

导出方法

function exportExcel($field, $list, $title='文件')
{
    $objPHPExcel = new \PHPExcel();
    foreach ($list as $key => $value) {
        foreach ($field as $k => $v) {
            if ($key == 0) {
                $objPHPExcel->getActiveSheet()->setCellValue($k . '1', $v[1]);
            }
            $i = $key + 2; //表格是从2开始的
            $objPHPExcel->getActiveSheet()->setCellValue($k . $i, $value[$v[0]]);
        }

    }
    header("Pragma: public");
    header("Expires: 0");
    header("Cache-Control:must-revalidate, post-check=0, pre-check=0");
    header("Content-Type:application/force-download");
    header("Content-Type:application/vnd.ms-execl");
    header("Content-Type:application/octet-stream");
    header("Content-Type:application/download");;
    header('Content-Disposition:attachment;filename='.$title.'.xlsx');
    header("Content-Transfer-Encoding:binary");
    $objWriter = \PHPExcel_IOFactory::createWriter($objPHPExcel, 'Excel2007');
    $objWriter->save('php://output');
}