宿主机可以连通RDS,容器内无法连通。
经检查原因是VPC的网段与docker容器默认网段冲突,修改docker配置,加入"bip": "172.16.10.1/24",重启服务解决

某些场景下很好用的东西,官方没有文档,抽空再整理,下面是使用的基本方法

配置文件定义表结构,文件config/autoload/table.php

<?php
declare(strict_types = 1);

return [
    'reception_id'        => [
        'size'    => 1024 * 5,
        'columns' => [
            'user_id' => [
                'type' => Table::TYPE_INT,
                'size' => 20
            ],
            'kf_id' => [
                'type' => Table::TYPE_INT,
                'size' => 20
            ],
            'user_type' => [
                'type' => Table::TYPE_INT,
                'size' => 20
            ],
            'user_status' => [
                'type' => Table::TYPE_INT,
                'size' => 20
            ],
            'kf_fd' => [
                'type' => Table::TYPE_INT,
                'size' => 20
            ],
            'user_fd' => [
                'type' => Table::TYPE_INT,
                'size' => 20
            ],
            'kf_status' => [
                'type' => Table::TYPE_INT,
                'size' => 20
            ],
        ],
    ],
    'kf_id'        => [
        'size'    => 1024 * 5,
        'columns' => [
            'kf_fd' => [
                'type' => Table::TYPE_INT,
                'size' => 20
            ],
            'kf_status' => [
                'type' => Table::TYPE_INT,
                'size' => 20
            ],
            'merch_id' => [
                'type' => Table::TYPE_INT,
                'size' => 20
            ],
        ],
    ],
    'user_id' => [
        'size'    => 1024 * 5,
        'columns' => [
            'reception_id' => [
                'type' => Table::TYPE_STRING,
                'size' => 20
            ],
            'user_fd' => [
                'type' => Table::TYPE_INT,
                'size' => 20
            ],
        ],
    ],
// ...
];

事件触发执行初始化内存表,文件app/Listener/BeforeMainServerStartListener.php

//...
    public function listen() : array
    {
        return [
            BeforeMainServerStart::class
        ];
    }

    public function process(object $event)
    {
        $tableConfig = config('table');
        foreach ($tableConfig as $key => $item) {
            TableManager::initialize($key, $item['size']);
            foreach ($item['columns'] as $columnKey => $column) {
                TableManager::get($key)->column($columnKey, $column['type'], $column['size']);
            }
            TableManager::get($key)->create();
        }
    }
//...

垃圾回收

简称GC。顾名思义,就是废物利用的意思。
说垃圾回收机制之前,先接触一下内存泄露。

内存泄露

内存泄漏(Memory Leak)是指程序中已动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。

C语言的垃圾回收机制

如果用过C语言,那么申请内存的方式是malloc或者是calloc,然后你用完这个内存后,一定不要忘了用free函数去释放掉,这就是手动垃圾回收,一般都是使用这种方式。

PHP的自动垃圾回收机制

我们都知道PHP是C语言实现的。你想想如何用C语言实现对一个变量的统计及释放?C语言是如何实现一个变量,从声明开始到没人使用了,就把这个变量所占的内存释放掉(被垃圾回收)。

PHP进行内存管理的核心算法一共两项:
一是引用计数,二是写时拷贝

声明一个PHP变量的时候,C语言就在底层生成了一个叫做zval的struct,如下:

zval {
    string "a" // 变量名为a
    value zend_value // 变量的值,联合体
    type string // 变量是字符串类型
}

zval struct结构体

  1. PHP $a 的变量名
  2. PHP $a 的变量类型
  3. PHP 变量 $a 的zend_value联合体
    如果给变量赋值了,比如"hello world",那么C语言就在底层再生成一个叫做zend_value的union
zend_value {
    string "hello world" // 值内容
    refcount 1 // 引用计数
}

zend_value联合体

  1. 保存PHP $a 的变量值
  2. 记录PHP $a 变量的引用计数

何为引用计数?

$a = "hello world";
echo xdebug_debug_zval('a'); // refcount=1
$b = $a; // $b引用变量$a
echo xdebug_debug_zval('a'); // refcount=2
$c = $a; // $c引用变量$a
echo xdebug_debug_zval('a'); // refcount=3
unset($c); // 删除了$c的引用
echo xdebug_debug_zval('a'); // refcount=2

何为拷贝复制?

$a = 'hello';
$b = $a; // $a赋值给$b的时候,并没有复制值
echo xdebug_debug_zval('a'); // $a的引用计数为2
$a = 'world'; // 当修改$a的值的时候,不得己进行复制,避免牵扯到$b
echo xdebug_debug_zval('a'); // $a的引用计数为1

垃圾回收机制:

当一个zval在被unset时,或者从一个函数中运行完毕出来(局部变量)等很多地方,都会产生zval与zend_value发生断开的行为,这个时候zend引擎需要检测的就是zend_value的refcount是否为0,如果为0则直接KO free空出内容来。如果zend_value的refcount不为0,这个value就不能被释放,但是也不代表这个zend_value是清白的,因为此zend_value依然可能是垃圾。

  1. 当php变量的refcount=0时,变量$a就会被垃圾回收
  2. 当php变量的refcount>0时,变量$a也可能被认为是垃圾

什么情况会导致zend_value的refcount不为0,但是zend_value确是垃圾

$arr = [1];
$arr[] = &$arr;
unset($arr);

这种情况下,zend_value不会释放,但也不能放过它,不然会产生内存泄露,所以这会儿zend_value会被扔到一个叫做垃圾回收堆中,然后zend引擎会依次对垃圾回收堆中的这些zend_value进行二次检测,检测是不是由于上述两种情况造成的refcount为1但是自身却没有人在使用了,一旦确认是上述两种情况造成的,那么就会将zend_value彻底抹掉释放内存。

垃圾回收发生在什么时候

  1. FPM运行完毕后,一定会GC
  2. 运行过程中也会GC,内存都是即用即释放的

优化硬件

如果你需要庞大的数据库表(>2G),应考虑使用64位的硬件结构。因为MYSQL内部使用大量64位整数,64位的CPU将提供更好的性能。
对于大数据库,优化次序一般是 RAM、硬盘、CPU。
更多的内存通过将最常用的键码页面存放在内存中可以加速键码的更新。
如果不用事务安全(Transaction Safe)的表或有大表并且想避免长文件检查,一台UPS就能够在电源故障时让系统安全关闭。
对于数据库存放在一个专用的服务器系统,应考虑1G的以太网。延迟与吞吐量同样重要。

优化磁盘

为系统、程序和临时文件配备专用磁盘,如果确是进行很多修改工作,将更新日志和事务日志放在专用磁盘上。
低寻道时间对数据库磁盘非常重要。对于大表,你可以估计你将需要:
log(行数)/log(索引块长度/3*2/(键码长度+数据指针长度))+1 次寻道才能找到一行。
对于有50W行的表,索引Mediun int类型的列,需要:
log(500000)/log(1024/3*2/(3+2))+1 = 4次寻道。
上述索引需要 50000073/2=5.2M的空间。实际上,大多数块将被缓存,所以大概只需要1-2次寻道。
然而对于写入,你将需要4次寻道请求来找到在哪里存放新键码,而且一般要2次寻道来更新索引并写入一行。
对于非常大的数据库,你的应用将受到磁盘寻道速度限制,随着数据量的增加呈N log N数据集递增。
将数据库和表分在不同的磁盘上,在MYSQL中,你可以为此而使用符号链接。
条列磁盘(RAID 0)将提高读和写的吞吐量。带镜像的条列(RAID 0+1)将更安全并提高数据读的吞吐量。写入的吞吐量将有所降低。不要对临时文件或可以很容易重建的数据所在的磁盘使用镜像或RAID(除RAID 0)。
在Linux上,在引导时对磁盘使用命令hdparm -m16 -d1 以启用同时读写多个扇区和DMA功能。这可以将响应时间提高5%-50%。
在Linux上,用async和noatime挂载磁盘。对于某些特定应用,可以对某些特定表使用内存磁盘,但通常不需要。

优化操作系统

不要交换区。如果内存不足,则增加更多的内存或配置你的系统使用较少内存。
不要使用NFS磁盘。会有NFS锁定问题。
增加系统和MYSQL服务器的打开文件数量。(在safe_mysqld脚本中加入 ulimit -n #)
增加系统的进程和线程数量。
如果你有相对较少的大表,告诉文件系统不要将文件打碎在不同的磁道上(Solaris)。
使用支持大文件的文件系统(Solaris)。
选择使用哪种文件系统。在Linux上的Reiserfs对于打开、读写都非常快。文件检查只需要几秒钟。

优化应用

使用持续的连接
缓存应用中的数据以减少SQL服务器负载
不要查询应用中不需要的列
不要使用select * from table
测试应用程序的所有部分,但将大部分精力放在可能最坏的合理负载下的测试整体应用,通过模块化的方式进行来快速找到瓶颈。
如果在一个批处理中进行大量修改,使用lock tables。例如将多个update或delete集中在一起。

优化MYSQLD

挑选编译器和编译选项。
为你的系统寻找最好的启动选项。
多用EXPLAIN SELECT/SHOW VARIABLES/SHOW STATUS/SHOW PROCESSLIST。
优化表格式。
维护表(myisamchk、check table、optimize table)
使用MYSQL扩展让一切快速完成
避免使用表级或列级的GRANT

编译和安装MYSQL

通过为你的系统挑选可能最好的编译器,通常可以获得10%-30%的性能提升。
在Linux/Intel平台上,用pgcc编译MYsql。
对于一种特定的平台,使用MYSQL参考手册上的推荐优化项。
一般对特定CPU的原生编译器(如Sparc的Sun Workshop)应该比gcc提供更好的性能,但并不总是这样。
用你将使用的字符集编译MYSQL。
静态编译生成mysqld的执行文件(用--with-mysqld-ldflags=all-static)并用strip sql/mysqld整理最终的执行文件。
注意,既然MySQL不使用C++扩展,不带扩展支持编译MySQL将赢得巨大的性能提高。
如果操作系统支持原生线程,使用原生线程(而不用mit-pthreads)。
用MySQL基准测试来测试最终的二进制代码。

维护

如果可能,偶尔运行一下 OPTIMIZE table,这对大量更新的变长行非常重要。
偶尔用 myisamchk -a 更新一下表中的键码分布统计。记住在做之前关掉MySQL。
如果有碎片文件,可能值得将所有文件复制到另一个磁盘上,清除原来的磁盘并拷回文件。
如果遇到问题,用myisamchk或CHECK table检查表。

重要的MySQL启动项

back_log 如果需要大量新连接,修改它
thread_cache_size 如果需要大量新连接,修改它
key_buffer_size 索引页池,可以设置很大
bdb_cache_size BDB表使用的记录和键码的高速缓存
table_cache 如果有很多的表和并发连接,修改它
delay_key_write 如果需要缓存所有键码写入,设置它
log_show_queries 找出需要花费大量时间的查询
max_heap_table_size 用于 group by
sort_buffer 用于 order by 和 group by
myisam_sort_buffer_size 用于 repair table
join_buffer_size 在进行无键码的连接时使用

优化表

缓存

索引

优化SQL