mysql 数据类型的选择

mysql的数据类型
1,整数类型
1
2:实数类型
1
3:varchar和char
varchar类型的存储特点
1),varvhar用于存储变长字符串,只占用必要的存储空间
2),列的最大长度小于255则只占用一个额外字节用于记录字符串长度
3),列的最大长度大于255则要占用两个额外字节用于记录字符串长度
varchar长度的选择问题
1),使用最小的符合需求的长度
2),varchar(2)和varchar(200)存储mysql字符串性能不同
varcahr的适用场景
1),字符串列的最大长度比平均长度大很多
2),字符串很少被更新
3),使用了多字节字符集存储字符串

char类型的存储特点
1),char类型是定长的
2),字符串存储在char类型的列中会删除末尾的空格
3),char类型的最大宽度为255
char类型的使用场景
1),char类型适合存储所长度近似的值
2),char类型适合存储短字符串
3),char类型适合存储经常更新的字符串列
4:日期数据
DATATIME类型,占用8个字节的存储空间
TIMESTAMP类型
date类型(占用的字节数比使用字符串、datetime、int存储要少,使用date类型只需要3个字节;
使用date类型还可以利用日期时间函数进行日期之间的计算)
time类型(存储时间数据,格式为HH:MM:SS)
存储日期时间数据的注意事项
1)不要使用字符串类型来存储日期时间数据(日期时间类型通常比字符串占用的存储空间小;
日期时间类型在进行查找过滤时可以利用日期来进行对比;日期时间类型还有着丰富的处理函数,可以方便的
对时期类型进行日期计算;)
2)不要勇士int存储日期时间不如使用timestamp类型

mysql Innodb存储引擎

Innodb使用表空间进行 数据存储

1,Innodb存储引擎的特性
Innodb是一种事务性存储引擎
完全支持事务的ACID特性
redo Log 和 undo log(已提交的事务和未提交的事务)
Innodb支持行级锁(行级锁可以最大支持的支持并发,行级锁是由存储引擎层实现的)
什么是锁:
锁最主要作用是管理共享资源的并发访问
锁用于实现事务的隔离性
锁的类型:
共享锁(读锁)
独占锁(写锁)
1
锁的粒度:
表级锁
行级锁

2,innodb_file_per_table
参数:
ON:独立表空间:tablename.ibd
OFF:系统表空间:idbataX
比较:
系统表空间无法简单的收缩文件大小
独立表空间可以通过optimize table 命令收缩系统文件

系统表空间会产生IO瓶颈
独立表空间可以同时向多个文件刷新数据

建议:对innodb 使用独立表空间

3,系统表空间转移的到独立表空间
步骤:
使用mysqldump导出所有数据库表数据
停止mysql服务,修改参数,并删除innodb相关文件
重启mysql服务,重建innodb系统表空间
重新导入数据

4,适用场景:
Innodb适用于大多数OLTP应用

php 重载

php 重载
重载
[p2em]PHP所提供的”重载”(overloading)是指动态地”创建”类属性和方法。我们是通过魔术方法(magic methods)来实现的。这些魔术方法的参数都不能通过引用传递。 当调用当前环境下未定义或不可见的类属性或方法时,重载方法会被调用。PHP中的”重载”与其它绝大多数面向对象语言不同。传统的”重载”是用于提供多个同名的类方法,但各方法的参数类型和个数不同。 本节后面将使用”不可访问属性(inaccessible properties)”和”不可访问方法(inaccessible methods)”来称呼这些未定义或不可见的类属性或方法。 所有的重载方法都必须被声明为 public。 [/p2em]
一、属性重载

public void __set ( string$name , mixed$value )
public mixed __get ( string$name )
public bool __isset ( string$name )
public void __unset ( string$name )

在给不可访问属性赋值时,__set() 会被调用。
读取不可访问属性的值时,__get() 会被调用。
当对不可访问属性调用 isset() 或 empty() 时,__isset() 会被调用。
当对不可访问属性调用 unset() 时,__unset() 会被调用。
参数 $name 是指要操作的变量名称。__set() 方法的 $value 参数指定了 $name 变量的值。
属性重载只能在对象中进行。在静态方法中,这些魔术方法将不会被调用。所以这些方法都不能被 声明为 static。从 PHP 5.3.0 起, 将这些魔术方法定义为 static 会产生一个警告。

[gray-cue]Note:
因为 PHP 处理赋值运算的方式,__set() 的返回值将被忽略。类似的, 在下面这样的链式赋值中,__get() 不会被调用:
$a = $obj->b = 8; [/gray-cue]
[gray-cue]Note:
在除 isset() 外的其它语言结构中无法使用重载的属性,这意味着当对一个重载的属性使用 empty() 时,重载魔术方法将不会被调用。
为避开此限制,必须将重载属性赋值到本地变量再使用 empty()。
[/gray-cue]

使用 __get(),__set(),__isset() 和 __unset() 进行属性重载

<?php
class PropertyTest {
     /**  被重载的数据保存在此  */
    private $data = array();

 
     /**  重载不能被用在已经定义的属性  */
    public $declared = 1;

     /**  只有从类外部访问这个属性时,重载才会发生 */
    private $hidden = 2;

    public function __set($name, $value) 
    {
        echo "Setting '$name' to '$value'\n";
        $this->data[$name] = $value;
    }

    public function __get($name) 
    {
        echo "Getting '$name'\n";
        if (array_key_exists($name, $this->data)) {
            return $this->data[$name];
        }

        $trace = debug_backtrace();
        trigger_error(
            'Undefined property via __get(): ' . $name .
            ' in ' . $trace[0]['file'] .
            ' on line ' . $trace[0]['line'],
            E_USER_NOTICE);
        return null;
    }

    /**  PHP 5.1.0之后版本 */
    public function __isset($name) 
    {
        echo "Is '$name' set?\n";
        return isset($this->data[$name]);
    }

    /**  PHP 5.1.0之后版本 */
    public function __unset($name) 
    {
        echo "Unsetting '$name'\n";
        unset($this->data[$name]);
    }

    /**  非魔术方法  */
    public function getHidden() 
    {
        return $this->hidden;
    }
}


echo "<pre>\n";

$obj = new PropertyTest;

$obj->a = 1;
echo $obj->a . "\n\n";

var_dump(isset($obj->a));
unset($obj->a);
var_dump(isset($obj->a));
echo "\n";

echo $obj->declared . "\n\n";

echo "Let's experiment with the private property named 'hidden':\n";
echo "Privates are visible inside the class, so __get() not used...\n";
echo $obj->getHidden() . "\n";
echo "Privates not visible outside of class, so __get() is used...\n";
echo $obj->hidden . "\n";

以上例程会输出:

Setting 'a' to '1'
Getting 'a'
1Is 'a' set?
bool(true)
Unsetting 'a'
Is 'a' set?
bool(false)1Let's experiment with the private property named 'hidden':
Privates are visible inside the class, so __get() not used...
2
Privates not visible outside of class, so __get() is used...
Getting 'hidden'Notice:  Undefined property via __get(): hidden in <file> on line 70 in <file> on line 29

二、方法重载

public mixed __call ( string$name , array$arguments )
public static mixed __callStatic ( string$name , array$arguments )

在对象中调用一个不可访问方法时,__call() 会被调用。

用静态方式中调用一个不可访问方法时,__callStatic() 会被调用。

$name 参数是要调用的方法名称。$arguments 参数是一个枚举数组,包含着要传递给方法 $name 的参数。

使用 __call() 和 __callStatic() 对方法重载

<?php
class MethodTest 
{
    public function __call($name, $arguments) 
    {
        // 注意: $name 的值区分大小写
        echo "Calling object method '$name' "
             . implode(', ', $arguments). "\n";
    }

    /**  PHP 5.3.0之后版本  */
    public static function __callStatic($name, $arguments) 
    {
        // 注意: $name 的值区分大小写
        echo "Calling static method '$name' "
             . implode(', ', $arguments). "\n";
    }
}

$obj = new MethodTest;
$obj->runTest('in object context');

MethodTest::runTest('in static context');  // PHP 5.3.0之后版本

以上例程会输出:

Calling object method 'runTest' in object context
Calling static method 'runTest' in static context

Centos6.5 编译安装nginx1.9.9

Centos6.5 安装nginx1.9.9
一、准备工作
添加用户组和用户

groupadd www
useradd -g www www -s /bin/false

约束

/usr/local/src    #下载文件存放目录
/usr/local        #安装nginx目录

①:下载nginx1.9.9
[p2em]http://nginx.org/download/nginx-1.9.9.tar.gz[/p2em]
②:下载pcre,用来作地址重写的功能
[p2em]ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/pcre-8.37.tar.gz[/p2em]
③:下载zlib ,nginx的gzip模块,传输数据打包,省流量但消耗资源
[p2em]http://zlib.net/zlib-1.2.8.tar.gz[/p2em]
④:下载openssl,提供ssl加密协议
[p2em]http://www.openssl.org/source/openssl-1.0.2d.tar.gz[/p2em]
⑤:安装依赖:

yum -y install gcc automake autoconf libtool make  libxml2-devel libxslt-devel perl-devel perl-ExtUtils-Embed
yum install gcc gcc-c++

二、开始安装

cd /usr/local/src/

①:安装pcre

tar pcre-8.37.tar.gz
cd pcre-8.37
./configure
make
make install

②:安装zlib

tar -zxv zlib-1.2.8.tar.gz
cd zlib-1.2.8
./configure
make
make install

③:安装openssl

tar -zxvf openssl-1.0.2d.tar.gz
cd openssl-1.0.2d

④:安装Nginx1.9.9

tar -zxvf nginx-1.9.9.tar.gz
cd nginx-1.9.9
./configure \
--prefix=/usr/local/nginx     #注意nginx安装目录变了
--with-http_ssl_module \
--with-pcre=/usr/local/src/pcre-8.37 \
--with-zlib=/usr/local/src/zlib-1.2.8 \
--with-openssl=/usr/local/src/openssl-1.0.1d
make
make install

三、设置开机自启动
编辑

vim  /etc/init.d/nginx

写入

#!/bin/bash
# nginx Startup script for the Nginx HTTP Server
# chkconfig: - 85 15
# description: Nginx is a high-performance web and proxy server.
# It has a lot of features, but it's not for everyone.
# processname: nginx
# pidfile: /usr/local/nginx/logs/nginx.pid
# config: /usr/local/nginx/conf/nginx.conf
nginxd=/usr/local/nginx/sbin/nginx
nginx_config=/usr/local/nginx/conf/nginx.conf
nginx_pid=/usr/local/nginx/logs/nginx.pid
RETVAL=0
prog="nginx"
# Source function library.
. /etc/rc.d/init.d/functions
# Source networking configuration.
. /etc/sysconfig/network
# Check that networking is up.
[ ${NETWORKING} = "no" ] && exit 0
[ -x $nginxd ] || exit 0
# Start nginx daemons functions.
start() {
if [ -e $nginx_pid ];then
echo "nginx already running...."
exit 1
fi
echo -n $"Starting $prog: "
daemon $nginxd -c ${nginx_config}
RETVAL=$?
echo
[ $RETVAL = 0 ] && touch /var/lock/subsys/nginx
return $RETVAL
}
# Stop nginx daemons functions.
stop() {
echo -n $"Stopping $prog: "
killproc $nginxd
RETVAL=$?
echo
[ $RETVAL = 0 ] && rm -f /var/lock/subsys/nginx /var/run/nginx.pid
}
# reload nginx service functions.
reload() {
echo -n $"Reloading $prog: "
#kill -HUP `cat ${nginx_pid}`
killproc $nginxd -HUP
RETVAL=$?
echo
}
# See how we were called.
case "$1" in
start)
start
;;
stop)
stop
;;
reload)
reload
;;
restart)
stop
start
;;
status)
status $prog
RETVAL=$?
;;
*)
echo $"Usage: $prog {start|stop|restart|reload|status|help}"
exit 1
esac
exit $RETVAL

赋予可执行权限

chmod a+x /etc/init.d/nginx

设置开机启动

chkconfig --add /etc/init.d/nginx
chkconfig nginx on

启动

service nginx start
Starting nginx:                                            [  OK  ]

四、启动报错处理

service nginx start
Starting nginx: /usr/local/nginx/sbin/nginx: error while loading shared libraries: libpcre.so.1: cannot open shared object file: No such file or directory

使用ldd看nginx包含的动态函式库

ldd $(which /usr/local/nginx/sbin/nginx)
linux-vdso.so.1 =>  (0x00007fff89fff000)
        libpthread.so.0 => /lib64/libpthread.so.0 (0x0000003978400000)
        libcrypt.so.1 => /lib64/libcrypt.so.1 (0x000000397b800000)
        libpcre.so.1 => not found
        libcrypto.so.10 => /usr/lib64/libcrypto.so.10 (0x00007ffd9a115000)
        libz.so.1 => /lib64/libz.so.1 (0x0000003977c00000)
        libc.so.6 => /lib64/libc.so.6 (0x0000003978000000)
        /lib64/ld-linux-x86-64.so.2 (0x0000003977800000)
        libfreebl3.so => /lib64/libfreebl3.so (0x000000397ac00000)
        libdl.so.2 => /lib64/libdl.so.2 (0x0000003978800000)
cd /
 libpcre.so.1 => not found  #看到错误

解决方法:

cd /lib64/
ln -s libpcre.so.0.0.1 libpcre.so.1

再启动即可

五、配置nginx支持PHP
编辑

vim /usr/local/nginx/conf/nginx.conf

注意下边代码的注释

user  www www;    #更改为之前添加的用户和用户组  www www
worker_processes  1;

#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;

#pid        logs/nginx.pid;


events {
    worker_connections  1024;
}


http {
    include       mime.types;
    default_type  application/octet-stream;

    #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
    #                  '$status $body_bytes_sent "$http_referer" '
    #                  '"$http_user_agent" "$http_x_forwarded_for"';

    #access_log  logs/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    #keepalive_timeout  0;
    keepalive_timeout  65;

    #gzip  on;

    server {
        listen       80;
        server_name  localhost;    #绑定域名 默认本机

        #charset koi8-r;

        #access_log  logs/host.access.log  main;

        location / {
            root   html;
            index index.php index.html index.htm;
        }

        #error_page  404              /404.html;

        # redirect server error pages to the static page /50x.html
        #
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }

        # proxy the PHP scripts to Apache listening on 127.0.0.1:80
        #
        #location ~ \.php$ {
        #    proxy_pass   http://127.0.0.1;
        #}

        # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
        #打开支持PHP
        location ~ \.php$ {
            root           html;
            fastcgi_pass   127.0.0.1:9000;
            fastcgi_index  index.php;
            fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
            include        fastcgi_params;
        }

        # deny access to .htaccess files, if Apache's document root
        # concurs with nginx's one
        #
        #location ~ /\.ht {
        #    deny  all;
        #}
    }


    # another virtual host using mix of IP-, name-, and port-based configuration
    #
    #server {
    #    listen       8000;
    #    listen       somename:8080;
    #    server_name  somename  alias  another.alias;

    #    location / {
    #        root   html;
    #        index  index.html index.htm;
    #    }
    #}


    # HTTPS server
    #
    #server {
    #    listen       443 ssl;
    #    server_name  localhost;

    #    ssl_certificate      cert.pem;
    #    ssl_certificate_key  cert.key;

    #    ssl_session_cache    shared:SSL:1m;
    #    ssl_session_timeout  5m;

    #    ssl_ciphers  HIGH:!aNULL:!MD5;
    #    ssl_prefer_server_ciphers  on;

    #    location / {
    #        root   html;
    #        index  index.html index.htm;
    #    }
    #}
    # 包含所有的虚拟主机的配置文件,如需要多网站可以如下这样配置,引入多个配置文件
    include /usr/local/nginx/vhosts/*;
}

修改完成后重启即可支持PHP了

产品经理学习之产品经理的个人知识管理

什么是个人知识管理?
[p2em]个人知识管理是一种新的知识管理的 理念和方法 ,能将个人拥有的 各种资料、随手可得的信息变成更具价值的 知识 ,最终 利于 自己的 工作、生活 。通俗的说:个人知识管理(PKM:Personal  Knowledge  Management)的概念一般指个人通过工具建立知识体系并不断完善,进行知识的收集、消化吸收和创新的过程。通过对个人知识的管理,人们可以养成良好的 学习习惯 , 增强信息素养 , 完善自己的专业知识体系,提高自己的能力和竞争力,为实现个人价值和可持续发展打下坚实基础 。[/p2em]
产品经理学习之个人知识管理
个人知识管理的四个步骤
[p2em]1)获取知识[/p2em]
[p2em]为你需要获取的知识分类:产品经理、兴趣爱好、个人理财等 [/p2em]
[p2em]建立固定的知识获取渠道(相对固定,定期更新), 网络资讯、报刊杂志、报刊杂志、人际交流、培训教育[/p2em]
[p2em]2)学习知识[/p2em]
[p2em]阅读:行业网站、报刊杂志、书籍等[/p2em]
[p2em]参与培训:线下、线上[/p2em]
[p2em]工具:搜索引擎(百度,Google)、移动客户端、邮件订阅[/p2em]
[p2em]注意把碎片的知识点归纳总结。注意把不同的知识点关联起来思考。多和分享知识的人做交流(但不要骚扰别人)[/p2em]
[p2em]3)保存知识[/p2em]
[p2em]保存知识的技巧:归类(便于记忆,而不是分的越细越好)、便于查找、便于携带与同步、要有意识的知道,知识属于框架性的还是细节性的知识,切勿以点盖面,囫囵吞枣[/p2em]
[p2em]保存知识的工具:RSS工具、印象笔记(全系列),有道笔记、360云盘,腾讯微云等[/p2em]
[p2em]4)利用并分享知识[/p2em]
[p2em]别人可以偷走你的金钱,残害你的身体,永远抢不走的是你的才识[/p2em]
[p2em]学会把你的知识分享出来,你会得到更多[/p2em]
[p2em]分享知识的过程,就是梳理自己只是结构,系统复习的一次难得的机会[/p2em]
[p2em]你会认识更多的志同道合的朋友[/p2em]
方法与技巧
[p2em]尝试开通自己的博客,并坚持写作,不要在意是否有人来阅读,因为他本生就是知识沉淀的过程[/p2em]
[p2em]利用微博,记录自己的心得点滴,然后定期汇总到自己的博客[/p2em]
[p2em]尝试在自己擅长的唯独写一些文章然后投稿[/p2em]
[p2em]定期总结,找到自己的收获(情绪管理又要登场了)[/p2em]
回顾4个自我管理
[p2em]坚持自我管理,会让你在今后的人生里冲破荆坚持自我管理,会让你在今后的人生里冲破荆[/p2em]
[p2em]良好的的自我管理,比学好Axure,Visio,Mindmanager都来的重要,因为学会良好自我管理的人,是不用担心学会不会这些技术型工具的。[/p2em]
[p2em]坚持良好的自我管理,你就是一座金矿,你的人生将走上康庄大道。[/p2em]
[p2em]良好的自我管理,让你三年小成,十年大成。[/p2em]

产品经理学习之产品经理诞生的背景

1
背景
[p2em]互联网已经是一个巨大的产业,蕴含巨大的商业价值。[/p2em]
[p2em]一个互联网产品的诞生,已经不仅仅是技术活。[/p2em]
[p2em]互联网的发展进化对互联网产品提出了更高的要求[/p2em]
[p2em]企业需要更为专业的人才来满足与挖掘用户更具有商业价值的需求[/p2em]
[p2em]安全需求,沟通需求等等[/p2em]
一个互联网产品诞生的大致流程
[p2em]市场分析(市场机会、商业价值)[/p2em]
[p2em]需求调研(需求采集、需求分析)[/p2em]
[p2em]产品打造(产品规划、交互设计)[/p2em]
[p2em]产品运营(数据跟踪、运营反馈)[/p2em]
[p2em]持续迭代(版本规划、产品改进)[/p2em]
互联网产品诞生需要的组织成员
[p2em]项目经理[/p2em]
[p2em]设计团队[/p2em]
[p2em]研发团队[/p2em]
[p2em]测试团队[/p2em]
[p2em]运营团队[/p2em]
为什么要做一名产品经理
[p2em]思维方式的提升[/p2em]
[p2em]领袖力的提升[/p2em]
[p2em]沟通能力的提升[/p2em]
[p2em]执行力的提升[/p2em]
[p2em]对互联网产品专业能力的提升[/p2em]
[p2em]实战收获[/p2em]
产品经理和项目经理的区别
[p2em]产品经理主要靠想,负责做正确的事,提出做这件事情的方法和方式,全程跟进并保证这件事情是按照预先构思的方法和方式在前进。例如:提出产品规划,构思产品盈利方式,设计交互流程等。[/p2em]
[p2em]项目经理主要靠做,负责把事情做正确,尤其是在时间,成本,资源有一定限制的情况下。例如:在30天内,组织研发团队根据产品提出的需求,完成对产品的开发工作,并确保所有的功能点能正常使用。[/p2em]
[p2em]实际情况产品经理往往还是多少做点儿项目经理的事情。产品经理和项目经理,都是产品诞生与运营过程中必不可少的重要角色,只是工作方式和方法有截然的不同,不可混淆。[/p2em]

在CentOS下搭建自己的Git服务器

首先需要装好CentOS系统,作为测试,你可以选择装在虚拟机上,这样比较方便。这步默认你会,就不讲了。
有了CentOS,那么如何搭建Git服务器呢?
1、首先需要安装Git,可以使用yum源在线安装:

[root@localhost Desktop]# yum install -y git

2、创建一个git用户,用来运行git服务

# adduser git  

3、初始化git仓库:这里我们选择/data/git/learngit.git来作为我们的git仓库

[root@localhost git]# git init --bare learngit.git  
Initialized empty Git repository in /data/git/learngit.git/  

执行以上命令,会创建一个裸仓库,裸仓库没有工作区,因为服务器上的Git仓库纯粹是为了共享,所以不让用户直接登录到服务器上去改工作区,并且服务器上的Git仓库通常都以.git结尾。然后,把owner改为git:

[root@localhost git]# chown git:git learngit.git  

4、在这里,Git服务器就已经搭得差不多了。下面我们在客户端clone一下远程仓库

Zhu@XXX /E/testgit/8.34
$ git clone git@192.168.8.34:/data/git/learngit.git
Cloning into 'learngit'...
The authenticity of host '192.168.8.34 (192.168.8.34)' can't be established.
RSA key fingerprint is 2b:55:45:e7:4c:29:cc:05:33:78:03:bd:a8:cd:08:9d.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '192.168.8.34' (RSA) to the list of known hosts.
git@192.168.8.34's password:

这里两点需要注意:第一,当你第一次使用Git的clone或者push命令连接GitHub时,会得到一个警告:

The authenticity of host 'github.com (xx.xx.xx.xx)' can't be established.
RSA key fingerprint is xx.xx.xx.xx.xx.
Are you sure you want to continue connecting (yes/no)?

这是因为Git使用SSH连接,而SSH连接在第一次验证GitHub服务器的Key时,需要你确认GitHub的Key的指纹信息是否真的来自GitHub的服务器,输入yes回车即可。

Git会输出一个警告,告诉你已经把GitHub的Key添加到本机的一个信任列表里了:

Warning: Permanently added 'github.com' (RSA) to the list of known hosts.

这个警告只会出现一次,后面的操作就不会有任何警告了。
如果你实在担心有人冒充GitHub服务器,输入yes前可以对照GitHub的RSA Key的指纹信息是否与SSH连接给出的一致。
第二,这里提示你输入密码才能clone,当然如果你知道密码,可以键入密码来进行clone,但是更为常见的方式,是利用SSH的公钥来完成验证。

5、创建SSH Key
首先在用户主目录下,看看有没有.ssh目录,如果有,再看看这个目录下有没有id_rsa和id_rsa.pub这两个文件,如果已经有了,可直接跳到下一步。如果没有,打开Shell(Windows下打开Git Bash),创建SSH Key:

$ ssh-keygen -t rsa -C "youremail@example.com"  

你需要把邮件地址换成你自己的邮件地址,然后一路回车,使用默认值即可,由于这个Key也不是用于军事目的,所以也无需设置密码。

如果一切顺利的话,可以在用户主目录里找到.ssh目录,里面有id_rsa和id_rsa.pub两个文件,这两个就是SSH Key的秘钥对,id_rsa是私钥,不能泄露出去,id_rsa.pub是公钥,可以放心地告诉任何人。

6、Git服务器打开RSA认证
然后就可以去Git服务器上添加你的公钥用来验证你的信息了。在Git服务器上首先需要将/etc/ssh/sshd_config中将RSA认证打开,即:

   

  重要:

  修改 .ssh 目录的权限为 700

  修改 .ssh/authorized_keys 文件的权限为 600

1
2
3
chmod 700 .ssh
cd .ssh
chmod 600 authorized_keys
1.RSAAuthentication yes     
2.PubkeyAuthentication yes     
3.AuthorizedKeysFile  .ssh/authorized_keys

这里我们可以看到公钥存放在.ssh/authorized_keys文件中。所以我们在/home/git下创建.ssh目录,然后创建authorized_keys文件,并将刚生成的公钥导入进去。

然后再次clone的时候,或者是之后push的时候,就不需要再输入密码了:

Zhu@XXX/E/testgit/8.34
$ git clone git@192.168.8.34:/data/git/learngit.git
Cloning into 'learngit'...
warning: You appear to have cloned an empty repository.
Checking connectivity... done.

7、禁用git用户的shell登陆
出于安全考虑,第二步创建的git用户不允许登录shell,这可以通过编辑/etc/passwd文件完成。找到类似下面的一行:

git:x:1001:1001:,,,:/home/git:/bin/bash  

最后一个冒号后改为:

git:x:1001:1001:,,,:/home/git:/usr/bin/git-shell  

这样,git用户可以正常通过ssh使用git,但无法登录shell,因为我们为git用户指定的git-shell每次一登录就自动退出。

小程序配置

配置

我们使用app.json文件来对微信小程序进行全局配置,决定页面文件的路径、窗口表现、设置网络超时时间、设置多 tab 等。

以下是一个包含了所有配置选项的 app.json

{
  "pages": [
    "pages/index/index",
    "pages/logs/index"
  ],
  "window": {
    "navigationBarTitleText": "Demo"
  },
  "tabBar": {
    "list": [{
      "pagePath": "pages/index/index",
      "text": "首页"
    }, {
      "pagePath": "pages/logs/logs",
      "text": "日志"
    }]
  },
  "networkTimeout": {
    "request": 10000,
    "downloadFile": 10000
  },
  "debug": true
}

app.json 配置项列表

属性 类型 必填 描述
pages String Array 设置页面路径
window Object 设置默认页面的窗口表现
tabBar Object 设置底部 tab 的表现
networkTimeout Object 设置网络超时时间
debug Boolean 设置是否开启 debug 模式

pages

接受一个数组,每一项都是字符串,来指定小程序由哪些页面组成。每一项代表对应页面的【路径+文件名】信息,数组的第一项代表小程序的初始页面。小程序中新增/减少页面,都需要对 pages 数组进行修改。

文件名不需要写文件后缀,因为框架会自动去寻找路径.json,.js,.wxml,.wxss的四个文件进行整合。

如开发目录为:

pages/

pages/index/index.wxml

pages/index/index.js

pages/index/index.wxss

pages/logs/logs.wxml

pages/logs/logs.js

app.js

app.json

app.wxss

则,我们需要在 app.json 中写

{
  "pages":[
    "pages/index/index"
    "pages/logs/logs"
  ]
}

window

用于设置小程序的状态栏、导航条、标题、窗口背景色。

属性 类型 默认值 描述
navigationBarBackgroundColor HexColor #000000 导航栏背景颜色,如”#000000″
navigationBarTextStyle String white 导航栏标题颜色,仅支持 black/white
navigationBarTitleText String 导航栏标题文字内容
backgroundColor HexColor #ffffff 窗口的背景色
backgroundTextStyle String dark 下拉背景字体、loading 图的样式,仅支持 dark/light
enablePullDownRefresh Boolean false 是否开启下拉刷新,详见页面相关事件处理函数

注:HexColor(十六进制颜色值),如”#ff00ff”

如 app.json :

{
  "window":{
    "navigationBarBackgroundColor": "#ffffff",
    "navigationBarTextStyle": "black",
    "navigationBarTitleText": "微信接口功能演示",
    "backgroundColor": "#eeeeee",
    "backgroundTextStyle": "light"
  }
}

tabBar

如果我们的小程序是一个多 tab 应用(客户端窗口的底部或顶部有 tab 栏可以切换页面),那么我们可以通过 tabBar 配置项指定 tab 栏的表现,以及 tab 切换时显示的对应页面。

Tip: 通过页面跳转(wx.navigateTo)或者页面重定向(wx.redirectTo)所到达的页面,即使它是定义在 tabBar 配置中的页面,也不会显示底部的 tab 栏。

tabBar 是一个数组,只能配置最少2个、最多5个 tab,tab 按数组的顺序排序。

属性说明:

属性 类型 必填 默认值 描述
color HexColor tab 上的文字默认颜色
selectedColor HexColor tab 上的文字选中时的颜色
backgroundColor HexColor tab 的背景色
borderStyle String black tabbar上边框的颜色, 仅支持 black/white
list Array tab 的列表,详见 list 属性说明,最少2个、最多5个 tab
position String bottom 可选值 bottom、top

其中 list 接受一个数组,数组中的每个项都是一个对象,其属性值如下:

属性 类型 必填 说明
pagePath String 页面路径,必须在 pages 中先定义
text String tab 上按钮文字
iconPath String 图片路径,icon 大小限制为40kb,建议尺寸为 81px * 81px
selectedIconPath String 选中时的图片路径,icon 大小限制为40kb,建议尺寸为 81px * 81px

networkTimeout

可以设置各种网络请求的超时时间。

属性说明:

属性 类型 必填 说明
request Number wx.request的超时时间,单位毫秒,默认为:60000
connectSocket Number wx.connectSocket的超时时间,单位毫秒,默认为:60000
uploadFile Number wx.uploadFile的超时时间,单位毫秒,默认为:60000
downloadFile Number wx.downloadFile的超时时间,单位毫秒,默认为:60000

debug

可以在开发者工具中开启 debug 模式,在开发者工具的控制台面板,调试信息以 info 的形式给出,其信息有Page的注册页面路由数据更新事件触发 。 可以帮助开发者快速定位一些常见的问题。

page.json

每一个小程序页面也可以使用.json文件来对本页面的窗口表现进行配置。 页面的配置比app.json全局配置简单得多,只是设置 app.json 中的 window 配置项的内容,页面中配置项会覆盖 app.json 的 window 中相同的配置项。

页面的.json只能设置 window 相关的配置项,以决定本页面的窗口表现,所以无需写 window 这个键,如:

属性 类型 默认值 描述
navigationBarBackgroundColor HexColor #000000 导航栏背景颜色,如”#000000″
navigationBarTextStyle String white 导航栏标题颜色,仅支持 black/white
navigationBarTitleText String 导航栏标题文字内容
backgroundColor HexColor #ffffff 窗口的背景色
backgroundTextStyle String dark 下拉背景字体、loading 图的样式,仅支持 dark/light
enablePullDownRefresh Boolean false 是否开启下拉刷新,详见页面相关事件处理函数
disableScroll Boolean false 设置为 true 则页面整体不能上下滚动;只在 page.json 中有效,无法在 app.json 中设置该项
{
  "navigationBarBackgroundColor": "#ffffff",
  "navigationBarTextStyle": "black",
  "navigationBarTitleText": "微信接口功能演示",
  "backgroundColor": "#eeeeee",
  "backgroundTextStyle": "light"
}

小程序学习笔记一

文件结构

小程序包含一个描述整体程序的 app 和多个描述各自页面的 page。

一个小程序主体部分由三个文件组成,必须放在项目的根目录,如下:

文件 必填 作用
app.js  是 小程序逻辑
app.json 小程序公共设置
app.wxss 小程序公共样式表

一个小程序页面由四个文件组成,分别是:

文件类型 必填 作用
js 页面逻辑
wxml 页面结构
wxss 页面样式表
json 页面配置

 

注意:为了方便开发者减少配置项,描述页面的这四个文件必须具有相同的路径与文件名。

php spl_autoload_register() 函数

在了解这个函数之前先来看另一个函数:__autoload。

一、__autoload

这是一个自动加载函数,在PHP5中,当我们实例化一个未定义的类时,就会触发此函数。看下面例子:

 

printit.class.php 
 
<?php 
 
class PRINTIT { 
 
 function doPrint() {
  echo 'hello world';
 }
}
?> 
 
index.php 
 
<?
function __autoload( $class ) {
 $file = $class . '.class.php';  
 if ( is_file($file) ) {  
  require_once($file);  
 }
} 
 
$obj = new PRINTIT();
$obj->doPrint();
?>

运行index.PHP后正常输出hello world。在index.php中,由于没有包含printit.class.php,在实例化printit时,自动调用__autoload函数,参数$class的值即为类名printit,此时printit.class.php就被引进来了。
在面向对象中这种方法经常使用,可以避免书写过多的引用文件,同时也使整个系统更加灵活。

二、spl_autoload_register()
再看spl_autoload_register(),这个函数与__autoload有与曲同工之妙,看个简单的例子:

<?
function loadprint( $class ) {
 $file = $class . '.class.php';  
 if (is_file($file)) {  
  require_once($file);  
 } 
} 
 
spl_autoload_register( 'loadprint' ); 
 
$obj = new PRINTIT();
$obj->doPrint();
?>

将__autoload换成loadprint函数。但是loadprint不会像__autoload自动触发,这时spl_autoload_register()就起作用了,它告诉PHP碰到没有定义的类就执行loadprint()。
spl_autoload_register() 调用静态方法

<? 
 
class test {
 public static function loadprint( $class ) {
  $file = $class . '.class.php';  
  if (is_file($file)) {  
   require_once($file);  
  } 
 }
} 
 
spl_autoload_register(  array('test','loadprint')  );
//另一种写法:spl_autoload_register(  "test::loadprint"  ); 
 
$obj = new PRINTIT();
$obj->doPrint();
?>

centos设置虚拟内存(交换分区)

1. 添加swap文件大小为2G

默认情况下, of=/swapfile 即swapfile文件创建在/var/目录下。
若我在创建在/opt/image/目录下, 则下面所有的操作里有/swapfile的都要改为/opt/image/swap

# dd if=/dev/zero of=/swapfile bs=1k count=2048000

2. 创建SWAP文件

# mkswap /swapfile

3. 激活SWAP文件

# swapon /swapfile

4. 查看SWAP信息是否正确

# swapon -s

5. 添加到fstab文件中让系统引导时自动启动

注意, 这里是采用了swapfile文件的默认路径, 即/var/swapfile。若你上面的操作中swapfile文件不是在/var/目录下, 则下面的/var/swapfile也要相应修改为自己设写的。

# echo "/var/swapfile swap swap defaults 0 0" >> /etc/fstab

6. 用命令free检查2G交换分区生效

# free -m

或者, 检查meminfo文件

# grep SwapTotal  /proc/meminfo

7. 释放SWAP文件

# swapoff /swapfile

8. 删除SWAP文件

# rm -fr /swapfile

yii2实现分库分表的方案与思路

大家可以从任何一个gii生成model类开始代码上溯,会发现:yii2的model层基于ActiveRecord实现DAO访问数据库的能力。
而ActiveRecord的继承链可以继续上溯,最终会发现model其实是一个component,而component是yii2做IOC的重要组成部分,提供了behaviors,event的能力供继承者扩展。
(IOC,component,behaviors,event等概念可以参考http://www.digpage.com/学习)
先不考虑上面的一堆概念,一个站点发展历程一般是1个库1个表,1个库N个表,M个库N个表这样走过来的,下面拿订单表为例,分别说说。
1)1库1表:yii2默认采用PDO连接mysql,框架默认会配置一个叫做db的component作为唯一的mysql连接对象,其中dsn分配了数据库地址,数据库名称,配置如下:

'components' => [
 'db' => [
 'class' => 'yii\db\Connection',
 'dsn' => 'mysql:host=10.10.10.10;port=4005;dbname=wordpress',
 'username' => 'wp',
 'password' => '123',
 'charset' => 'utf8',
 ],

这就是yii2做IOC的一个典型事例,model层默认就会取这个db做为mysql连接对象,所以model访问都经过这个connection,可以从ActiveRecord类里看到。

class ActiveRecord extends BaseActiveRecord {
 
/**
 * Returns the database connection used by this AR class.
 * By default, the "db" application component is used as the database connection.
 * You may override this method if you want to use a different database connection.
 * @return Connection the database connection used by this AR class.
 */
public static function getDb()
{
 return Yii::$app->getDb();
}

追踪下去,最后会走yii2的ioc去创建名字叫做”db”的这个component返回给model层使用。

abstract class Application extends Module {
/**
 * Returns the database connection component.
 * @return \yii\db\Connection the database connection.
 */
public function getDb()
{
 return $this->get('db');
}

yii2上述实现决定了只能连接了1台数据库服务器,选择了其中1个database,那么具体访问哪个表,是通过在Model里覆写tableName这个static方法实现的,ActiveRecord会基于覆写的tableName来决定表名是什么。

class OrderInfo extends \yii\db\ActiveRecord
{
 /**
 * @inheritdoc
 * @return
 */
 public static function tableName()
 {
 return 'order_info';
 }

2)1库N表:因为orderInfo数据量变大,各方面性能指标有所下降,而单机硬件性能还有较大冗余,于是可以考虑分多张order_info表,均摊数据量。假设我们要份8张表,那么可以依据uid(用户ID)%8来决定订单存储在哪个表里。
然而1库1表的时候,tableName()返回是的order_info,于是理所应当的重载这个函数,提供一种动态变化的能力即可,例如:

class OrderInfo extends \yii\db\ActiveRecord
{
 private static $partitionIndex_ = null; // 分表ID
 
 /**
 * 重置分区id
 * @param unknown $uid
 */
 private static function resetPartitionIndex($uid = null) {
 $partitionCount = \Yii::$app->params['Order']['partitionCount'];
 
 self::$partitionIndex_ = $uid % $partitionCount;
 }
 
 /**
 * @inheritdoc
 */
 public static function tableName()
 {
 return 'order_info' . self::$partitionIndex_;
 }

提供一个resetParitionIndex($uid)函数,在每次操作model之前主动调用来标记分表的下标,并且重载tableName来为model层拼接生成本次操作的表名。
3)M库N表:1库N表逐渐发展,单机存储和性能达到瓶颈,只能将数据分散到多个服务器存储,于是提出了分库的需求。但是从”1库1表”的框架实现逻辑来看,model层默认取db配置作为mysql连接的话,是没有办法访问多个mysql实例的,所以必须解决这个问题。
一般产生这个需求,产品已经进入中期稳步发展阶段。有2个思路解决M库问题,1种是yii2通过改造直连多个地址进行访问多库,1种是yii2仍旧只连1个地址,而这个地址部署了dbproxy,由dbproxy根据你访问的库名代理连接多个库。
如果此前没有熟练的运维过dbproxy,并且php集群规模没有大到单个mysql实例客户端连接数过多拒绝服务的境地,那么第1种方案就可以解决了。否则,应该选择第2种方案。
无论选择哪种方案,我们都应该进一步改造tableName()函数,为database名称提供动态变化的能力,和table动态变化类似。

class OrderInfo extends \yii\db\ActiveRecord {
 
private static $databaseIndex_ = null; // 分库ID
private static $partitionIndex_ = null; // 分表ID
 
 /**
 * 重置分区id
 * @param unknown $uid
 */
 private static function resetPartitionIndex($uid = null) {
 $databaseCount = \Yii::$app->params['Order']['databaseCount'];
 $partitionCount = \Yii::$app->params['Order']['partitionCount'];
 
 // 先决定分到哪一张表里
 self::$partitionIndex_ = $uid % $partitionCount;
 // 再根据表的下标决定分到哪个库里
 self::$databaseIndex_ = intval(self::$partitionIndex_ / ($partitionCount / $databaseCount));
 }
 
 /**
 * @inheritdoc
 */
 public static function tableName()
 {
 $database = 'wordpress' . self::$databaseIndex_;
 $table = 'order_info' . self::$partitionIndex_;
 return $database . '.' . $table;
 }

在分表逻辑基础上稍作改造,即可实现分库。假设分8张表,那么分别是00,01,02,03…07,然后决定分4个库,那么00,01表在00库,02,03表在01库,04,05表在02库,06,07表在03库,根据这个规律对应的计算代码如上。最终ActiveRecord生效的代码都会类似于”select * from wordpress0.order_info1″,这样就可以解决连接dbproxy访问多库的需求了。
那么yii直接访问多Mysql实例怎么做呢,其实类似tableName() ,我们只需要覆盖getDb()方法即可,同时要求我们首先配置好4个mysql实例,从而可以通过yii的application通过IOC设计来生成多个db连接,所有改动如下:
先配置好4个数据库,给予不同的component id以便区分,它们连接了不同的mysql实例,其中dsn里的dbname只要存在即可(防止PDO执行use database时候不存在报错),真实的库名是通过tableName()动态变化的。

'db0' => [
 'class' => 'yii\db\Connection',
 'dsn' => 'mysql:host=10.10.10.10;port=6184;dbname=wordpress0',
 'username' => 'wp',
 'password' => '123',
 'charset' => 'utf8',
 // 'tablePrefix' => 'ktv_',
],
'db1' => [
 'class' => 'yii\db\Connection',
 'dsn' => 'mysql:host=10.10.10.11;port=6184;dbname=wordpress2',
 'username' => 'wp',
 'password' => '123',
 'charset' => 'utf8',
 // 'tablePrefix' => 'ktv_',
],
'db2' => [
 'class' => 'yii\db\Connection',
 'dsn' => 'mysql:host=10.10.10.12;port=6184;dbname=wordpress4',
 'username' => 'wp',
 'password' => '123',
 'charset' => 'utf8',
 // 'tablePrefix' => 'ktv_',
],
'db3' => [
 'class' => 'yii\db\Connection',
 'dsn' => 'mysql:host=10.10.10.13;port=6184;dbname=wordpress6',
 'username' => 'wp',
 'password' => '123',
 'charset' => 'utf8',
 // 'tablePrefix' => 'ktv_',
],

覆写getDb()方法,根据库下标返回不同的数据库连接即可。

class OrderInfo extends \yii\db\ActiveRecord
{
 private static $databaseIndex_ = null; // 分库ID
 private static $partitionIndex_ = null; // 分表ID
 
 /**
 * 重置分区id
 * @param unknown $uid
 */
 private static function resetPartitionIndex($uid = null) {
 $databaseCount = \Yii::$app->params['Order']['databaseCount'];
 $partitionCount = \Yii::$app->params['Order']['partitionCount'];
 
 // 先决定分到哪一张表里
 
 self::$partitionIndex_ = $uid % $partitionCount;
 // 再根据表的下标决定分到哪个库里
 self::$databaseIndex_ = intval(self::$partitionIndex_ / ($partitionCount / $databaseCount));
 }
 
 /**
 * 根据分库分表,返回库名.表名
 */
 public static function tableName()
 {
 $database = 'wordpress' . self::$databaseIndex_;
 $table = 'order_info' . self::$partitionIndex_;
 return $database . '.' . $table;
 }
 
 /**
 * 根据分库结果,返回不同的数据库连接
 */
 public static function getDb()
 {
 return \Yii::$app->get('db' . self::$databaseIndex_);
 }

这样,无论是yii连接多个mysql实例,还是yii连接1个dbproxy,都可以实现了。
网上有一些例子,试图通过component的event机制,通过在component的配置中指定onUpdate,onBeforeSave等自定义event去hook不同的DAO操作来隐式(自动)的变更database或者connection或者tablename的做法,都是基于model object才能实现的,如果直接使用model class的类似updateAll()方法的话,是绕过DAO直接走了PDO的,不会触发这些event,所以并不是完备的解决方案。
这样的方案原理简单,方案对框架无侵入,只是每次DB操作前都要显式的resetPartitionIndex($uid)调用。如果要做到用户无感知,那必须对ActiveRecord类进行继承,进一步覆盖所有class method的实现以便插入选库选表逻辑,代价过高。
补充:关于分库分表的一些实践细节,分表数量建议2^n,例如n=3的情况下分8张表,然后确定一下几个库,库数量是2^m,但要<=表数量,例如这里1个库,2个库,4个库,8个库都是可以的,表顺序坐落在这些库里即可。 为什么数量都是2指数,是因为如果面临扩容需求,数据的迁移将方便一些。假设分了2张表,数据按uid%2打散,要扩容成4张表,那么只需要把表0的部分数据迁移到表2,表1的部分数据迁移到表3,即可完成扩容,也就是uid%2和uid%4造成的迁移量是很小的,这个可以自己算一下。

PHP数字字符串左侧补0、字符串填充和自动补齐的几种方法

一、数字补0.

如果要自动生成学号,自动生成某某编号,就像这样的形式“d0000009”、“d0000027”时,那么就会面临一个问题,怎么把左边用0补齐成这样8位数的编码呢?我想到了两种方法实现这个功能。
方法一:
先构造一个数字10000000,千万,也就是一个1,7个0,然后加上当前的编号(比如是3),那么就得到 10000003,用字符串截取 substr(‘10000003’,1,7)后就得到0000003,最后在与“d”拼接,就得到了最终的编号d0000003。
源码如下:

<?php
 $num = 3;
 $temp_num = 10000000;
 $new_num = $num + $temp_num;
 $real_num = "d".substr($new_num,1,7); //即截取掉最前面的“1”
 echo $real_num;
?>

方法二:
测出当前编号(比如是3)的长度strlen(‘3’)=1,用要生成编号的总长度减去当前编号长度,得到需要填充0的个数,然后再用for循环填充0即可。
源码如下:

<?php
 $num = 3;
 $bit = 7;//产生7位数的数字编号
 $num_len = strlen($num);
 $zero = '';
 for($i=$num_len; $i<$bit; $i++){
  $zero .= "0";
 }
 $real_num = "d".$zero.$num;
 echo $real_num;
?>

方法三:另外几种方法

<?php
    $sourceNumber = "1";
    $newNumber = substr(strval($sourceNumber+1000),1,3);
    echo "$newNumber";
?>
/*这个时候就会出现:001
如果要增加位数的话可以将1000加大,然后把3也加大。
举例:如果我要补上 "4个0" 第03行 就要变成这样。*/
<?php
    $newNumber = substr(strval($sourceNumber+100000),1,5);
?>
/*其实就是总共要显示几位数字,就把$sourceNumber+1后面补上多少个0,最后一个数字就直接改成显示几位数字。*/
/*string str_pad ( string $input, int $pad_length [, string $pad_string [, int $pad_type]] )*/
<?php 
$input = "Alien"; 
echo str_pad($input, 10); 
// produces "Alien " 
echo str_pad($input, 10, "-=", STR_PAD_LEFT); 
// produces "-=-=-Alien" 
echo str_pad($input, 10, "_", STR_PAD_BOTH); 
// produces "__Alien___" 
echo str_pad($input, 6 , "___"); 
// produces "Alien_" 
?>
/*补齐字符串的长度.以pad_string 补.默认补在右边,如果STR_PAD_LEFT就补到左边,STR_PAD_BOTH两边一起补。下次用str_pad,毕竟是内置的,肯定比自定义的快。*/
/*
你上面的方法我觉得不怎么好,介绍一下我写的一个方法。方法函数如下,这样当你要的结果001的话,方法:dispRepair('1',3,'0')
功能:补位函数
str:原字符串
type:类型,0为后补,1为前补
len:新字符串长度
msg:填补字符
*/
function dispRepair($str,$len,$msg,$type='1') {
  $length = $len - strlen($str);
  if($length<1)return $str;
  if ($type == 1) {
    $str = str_repeat($msg,$length).$str;
  } else {
    $str .= str_repeat($msg,$length);
  }
  return $str;
}

二、字符串填充、自动补齐、自动补全
遇到要输出一定长度字符串的时候,可以使用一下两种方法进行PHP字符串自动填充、自动补全 。
方法一:

$newStr= sprintf('%05s', $str);

sprintf()的功能非常灵活,上面的格式字符串中,“%05s ”表示输出成长度为5的字符串,如果长度不足,左边以零补全;如果写成 “%5s ”,则默认以空格补全;如果希望使用其它字符补全,则要在该字符前加上单引号,即形如“%’#5s ”的表示以井号补全;最后,如果希望补全发生在 字符串右边,则在百分号后加上减号,“%-05s ”。

方法二:

$cd_no = str_pad(++$next_cd_no,8,'#',STR_PAD_LEFT);

tr_pad(string,length,pad_string,pad_type):具体用法查看手册。
string 必需。规定要填充的字符串。
length 必需。规定新字符串的长度。如果该值小于原始字符串的长度,则不进行任何操作。
pad_string 可选。规定供填充使用的字符串。默认是空白。
pad_type 可选。规定填充字符串的那边。
这两种方法很方便的实现了PHP字符串的自动补全功能。

yii2 队列使用

一、安装yii2 queue

php composer.phar require --prefer-dist gittmy/yii2-queue "dev-master"

github地址:https://github.com/gittmy/yii2-queue
二、使用教程
1:在配置文件中配置好需要使用的队列,完整的配置代码如下:
redis队列

'queue' => [
            'class' => 'gittmy\queue\drives\RedisQueue',
            'hostname' => '127.0.0.1',
            'port' => 6379,
            'database' => 2,
//            'password' => '',
        ],

2:在components数组配置项中配置好队列后,就可以开始使用队列了,首先是任务入队列,提供两个方法: \Yii::$app->queue->pushOn($hander,$data,$queue=’default’) 即时任务入队列:这样的任务入队列后,如果队列监听在运行,那么任务会立刻进入ready状态,可以被监听进程执行。 该方法有3个参数,第一个为任务处理handler,第二个为任务数据,第三个为队列名称,默认为 default。 \Yii::$app->queue->laterOn($delay,$handler,$data,$queue=’default’) 延时任务入队列:这样的任务入队列后不会立刻被队列监听进程之行,需要等待 $delay秒后任务才就绪。

目前支持的handler有: 1,新建自己的队列处理handler,继承、gittmy\queue\JobHandler,并实现任务处理方法handle()和失败处理方法failed()。 2, 一个php闭包,形如 function($job,$data){}

\Yii::$app->queue->pushOn(new SendMial(),['email'=>'4006690@qq.com','title'=>'test','content'=>'email test'],'email');
\Yii::$app->queue->pushOn(function($job,$data){var_dump($data)},['email'=>'4006690@qq.com','title'=>'test','content'=>'email test'],'email');

\Yii::$app->queue->laterOn(120,new SendMial(),['email'=>'4006690@qq.com','title'=>'test','content'=>'email test'],'email');
\Yii::$app->queue->pushOn(120,function($job,$data){var_dump($data)},['email'=>'4006690@qq.com','title'=>'test','content'=>'email test'],'email');

3:新建自己的队列处理handler,继承gittmy\queue\JobHandler,并实现任务处理方法handle和失败处理方法failed,一个发邮件的jobhandler类似:

class SendMail extends JobHandler
{

    public function handle($job,$data)
    {
        if($job->getAttempts() > 3){
            $this->failed($job);
        }

        $payload = $job->getPayload();

        //$payload即任务的数据,你拿到任务数据后就可以执行发邮件了
        //TODO 发邮件
    }

    public function failed($job,$data)
    {
        die("发了3次都失败了,算了");
    }
}

4:启动后台队列监听进程,对任务进行处理,您可以使用yii console app来启动,你也可以使用更高级的如swoole来高效的运行队列监听, 目前提供了一个Worker类,在控制台程序使用Worker::listen(Queue $queue,$queueName=’default’,$attempt=10,$memory=512,$sleep=3,$delay=0)可以 启动队列监听进程,其中 $attempt是任务尝试次数,$memory是允许使用最大内存,$sleep表示每次尝试从队列中获取任务的间隔时间,$delay代表把任务重新加入队列 时是否延时(0代表不延时),一个标准yii console app 启动队列监听进程代码如下;

class WorkerController extends \yii\console\Controller
{
    public function actionListen($queueName='default',$attempt=10,$memeory=128,$sleep=3 ,$delay=0){
        Worker::listen(\Yii::$app->queue,$queueName,$attempt,$memeory,$sleep,$delay);
    }
}
//需要在控制台也配置上队列组件配置方法跟第一步的配置相同
yii worker/listen default 10 128 3 0

当后台监听任务启动起来后,一但有任务加入队列,队列就会调用入队列时设置的handler对队列任务进行处理了。每次会pop出一个任务进行处理,处理完成后删除任务,直到队列为空。

5:关于任务失败处理: 默认情况下,一个任务在执行时出现异常或者一个任务失败时并不是认为它真正失败了,此时会检测它的尝试次数是否已经超出设置的attempt,如果没超出会重新入队列尝试,如果超出了, 则该任务才是真正失败,这是会先调用任务处理handler类的failed()方法处理失败操作,如果没有failed()方法(比如handler为闭包或者您自定义的继承自gittmy\queue\JobHandler 的类没有写failed()方法),则会尝试使用扩展自身的失败日志处理机制(配置项里的failed配置),会尝试把失败任务的详细信息写入到数据库表中(目前只支持数据库方式)。 建议您采用继承gittmy\queue\JobHandler的方式生成任务处理handler并写自己的failed方法处理失败任务。

6:任务事件支持: 目前任务支持2个事件(beforeExecute,beforeDelete), beforeExecute是在任务被pop出来之后,执行之前执行。beforeDelete是任务在被删除之前执行 您可以使用这两个事件做自定易操作,只需要像上面配置文件里配置 jobEvent那样绑定事件处理handler即可

mysql千万级数据库插入速度和读取速度的调整记录

一般情况下mysql上百万数据读取和插入更新是没什么问题了,但到了上千万级就会出现很慢,下面我们来看mysql千万级数据库插入速度和读取速度的调整记录吧。
1)提高数据库插入性能中心思想:尽量将数据一次性写入到Data File和减少数据库的checkpoint 操作。这次修改了下面四个配置项:
1)将 innodb_flush_log_at_trx_commit 配置设定为0;按过往经验设定为0,插入速度会有很大提高。
0: 日志缓冲每秒一次地被写到日志文件,并且对日志文件做到磁盘操作的刷新,但是在一个事务提交不做任何操作。
1:在每个事务提交时,日志缓冲被写到日志文件,对日志文件做到磁盘操作的刷新。
2:在每个提交,日志缓冲被写到文件,但不对日志文件做到磁盘操作的刷新。对日志文件每秒刷新一次。
默认值是 1,也是最安全的设置,即每个事务提交的时候都会从 log buffer 写
到日志文件,而且会实际刷新磁盘,但是这样性能有一定的损失。如果可以容忍在数
据库崩溃的时候损失一部分数据,那么设置成 0 或者 2 都会有所改善。设置成 0,则
在数据库崩溃的时候会丢失那些没有被写入日志文件的事务,最多丢失 1 秒钟的事
务,这种方式是最不安全的,也是效率最高的。设置成 2 的时候,因为只是没有刷新
到磁盘,但是已经写入日志文件,所以只要操作系统没有崩溃,那么并没有丢失数据 ,
比设置成 0 更安全一些。
在 mysql 的手册中,为了确保事务的持久性和复制设置的耐受性、一致性,都是
建议将这个参数设置为 1 的。

2)将 innodb_autoextend_increment 配置由于默认8M 调整到 128M
此配置项作用主要是当tablespace 空间已经满了后,需要MySQL系统需要自动扩展多少空间,每次tablespace 扩展都会让各个SQL 处于等待状态。增加自动扩展Size可以减少tablespace自动扩展次数。

3)将 innodb_log_buffer_size 配置由于默认1M 调整到 16M
此配置项作用设定innodb 数据库引擎写日志缓存区;将此缓存段增大可以减少数据库写数据文件次数。

4)将 innodb_log_file_size 配置由于默认 8M 调整到 128M
此配置项作用设定innodb 数据库引擎UNDO日志的大小;从而减少数据库checkpoint操作。
经过以上调整,系统插入速度由于原来10分钟几万条提升至1秒1W左右;注:以上参数调整,需要根据不同机器来进行实际调整。特别是 innodb_flush_log_at_trx_commit、innodb_log_buffer_size和 innodb_log_file_size 需要谨慎调整;因为涉及MySQL本身的容灾处理。

(2)提升数据库读取速度,重数据库层面上读取速度提升主要由于几点:简化SQL、加索引和分区; 经过检查程序SQL已经是最简单,查询条件上已经增加索引。我们只能用武器:表分区。
数据库 MySQL分区前准备:在MySQL中,表空间就是存储数据和索引的数据文件。
将S11数据库由于同享tablespace 修改为支持多个tablespace;
将wb_user_info_sina 和 wb_user_info_tx 两个表修改为各自独立表空间;(Sina:1700W数据,2.6G 大数据文件,Tencent 1400W,2.3G大数据文件);
分区操作:
将现有的主键和索引先删除
重现建立id,uid 的联合主键
再以 uid 为键值进行分区。这时候到/var/data/mysql 查看数据文件,可以看到两个大表各自独立表空间已经分割成若干个较少独立分区空间。(这时候若以uid 为检索条件进行查询,并不提升速度;因为键值只是安排数据存储的分区并不会建立分区索引。我非常郁闷这点比Oracle 差得不是一点半点。)
再以 uid 字段上进行建立索引。再次到/var/data/mysql 文件夹查看数据文件,非常郁闷地发现各个分区Size竟然大了。MySQL还是老样子将索引与数据存储在同一个tablespace里面。若能index 与 数据分离能够更加好管理。
经过以上调整,暂时没能体现出系统读取速度提升;基本都是在 2~3秒完成5K数据更新。
MySQL数据库插入速度调整补充资料:
MySQL 从最开始的时候 1000条/分钟的插入速度调高至 10000条/秒。 相信大家都已经等急了相关介绍,下面我做调优时候的整个过程。提高数据库插入性能中心思想:
1、尽量使数据库一次性写入Data File
2、减少数据库的checkpoint 操作
3、程序上尽量缓冲数据,进行批量式插入与提交
4、减少系统的IO冲突
根据以上四点内容,作为一个业余DBA对MySQL服务进行了下面调整:
修改负责收录记录MySQL服务器配置,提升MySQL整体写速度;具体为下面三个数据库变量值:innodb_autoextend_increment、innodb_log_buffer_size、innodb_log_file_size;此三个变量默认值分别为 5M、8M、8M,根据服务器内存大小与具体使用情况,将此三只分别修改为:128M、16M、128M。同时,也将原来2个 Log File 变更为 8 个Log File。此次修改主要满足第一和第二点,如:增加innodb_autoextend_increment就是为了避免由于频繁自动扩展Data File而导致 MySQL 的checkpoint 操作;
将大表转变为独立表空并且进行分区,然后将不同分区下挂在多个不同硬盘阵列中。
完成了以上修改操作后;我看到下面幸福结果:
获取测试结果:
Query OK, 2500000 rows affected (4 min 4.85 sec)
Records: 2500000 Duplicates: 0 Warnings: 0
Query OK, 2500000 rows affected (4 min 58.89 sec)
Records: 2500000 Duplicates: 0 Warnings: 0
Query OK, 2500000 rows affected (5 min 25.91 sec)
Records: 2500000 Duplicates: 0 Warnings: 0
Query OK, 2500000 rows affected (5 min 22.32 sec)
Records: 2500000 Duplicates: 0 Warnings: 0
最后表的数据量:
+————+
| count(*) |
+————+
| 10000000|
+————+
从上面结果来看,数据量增加会对插入性能有一定影响。不过,整体速度还是非常面议。一天不到时间,就可以完成4亿数据正常处理。