让Lavarel Eloquent ORM 支持 mongodb数据库

Laravel 的 Eloquent ORM 提供了漂亮、简洁的 ActiveRecord 实现来和数据库的互动。 每个数据库表会和一个对应的「模型」互动。

组件说明

业务侧还在用PHP5.3,composer.json文件内容如下:

{
    "require": {
        "illuminate/database": "4.1.*",
        "jenssegers/mongodb": "1.3.*"
    }
}

实际安装的版本如下。

组件版本
illuminate/databasev4.1.30
jenssegers/laravel-mongodbv1.3.2

使用方式:

  1. 配置文件conf/database.php新增mongodb配置。

    <?php
    $database = array(
        'mongodb' => array(
            'driver'    => 'mongodb',
             'host'      => '100.98.173.13',
             'port'      => 5566,
             'database'  => 'bigdata',
         ),
    )
  2. 自动加载数据库,新增mongodb数据库类型。

    include conf/database.php;
    
    $capsule = new Capsule;
    
    // 支持mongodb数据库,如果name是mongodb,交给Jenssegers\Mongodb\Connection来处理
    $capsule->getDatabaseManager()->extend('mongodb', function($config)
    {
        return new Jenssegers\Mongodb\Connection($config);
    });
    
    foreach ($database[$env] as $name => $conf) {
        // 创建链接
        $capsule->addConnection($conf, $name);
    }
    
    // 设置全局静态可访问
    $capsule->setAsGlobal();
    
    // 启动Eloquent
    $capsule->bootEloquent();
  3. model继承Jenssegers\Mongodb\Model.

    <?php
    class UserModel extends Jenssegers\Mongodb\Model {
    
        /**
         * table
         *
         * @var string
         */
        protected $table = 'users';
    
        /**
         * connection
         *
         * @var string
         */
        protected $connection = 'mongodb';
    }

上面是按网上找到的资料配置的,然后跑起来后报PHP Fatal error: Call to a member function connection() on a non-object in /web/xxx/vendor/illuminate/database/Illuminate/Database/Eloquent/Model.php on line 2810的错误。

断点定位了半天,才解决掉问题,这里记录一下。

先看下2810行的代码是什么。

2808    public static function resolveConnection($connection = null)
2809    {
2810        return static::$resolver->connection($connection);
2811    }

注意这里用的是static::$resolver,关于php的static和self的区别可以参考这篇文章,这里不做过多描述。
php中self与static的区别

解决方法:

修改vendor\jenssegers\mongodb\src\Jenssegers\Mongodb\Model.php文件,注释下面这行代码。

protected static $resolver;

php输入csv文件的实现(文件内容含json数据)

用输出一个csv文件,内容类似如下,字段3是一个json字符串。

字段1字段2字段3
xy{"200":0,"404":0}

实现:

  1. csv里如果要显示",要用""表示。
    这里通过str_replace('"', '""', $data)进行替换处理。
  2. 下载的文件打开中文乱码。
header("content-type:application/csv;charset=UTF-8");
header("Content-Disposition:attachment;filename=" . $fileName);
echo "\xEF\xBB\xBF"; // 注意,一定要输出这个bom头,否则文件内容会乱码!

metabase迁移底层数据库从H2到Mysql

最近用metabase搞了一套内部BI可视化系统。

这段时间看资料,发现metabase默认是用的H2数据库,会有单点风险。

今天有时间,把底层数据库从H2直接迁移到了Mysql。

注:metabase安装目录为/usr/local/metabase,下面所有操作命令都在改目录下进行。

  1. 备份H2数据库。
    进入metabase的启动目录,找到类似metabase.db.*的文件,有两个,拷贝到其他目录做备份。

    ll metabase/
    total 86712
    -rw-r--r-- 1 root root 57577472 Apr 19 18:23 metabase.db.mv.db
    -rw-r--r-- 1 root root 31212428 Apr 19 18:23 metabase.db.trace.db
  2. 配置metabase的mysql配置。往/etc/profile文件末尾,添加下面代码,将里面的配置信息改成你自己的,保存后记得加载一下配置。

    export MB_DB_TYPE=mysql
    export MB_DB_DBNAME=metabase数据库
    export MB_DB_PORT=mysql端口
    export MB_DB_USER=mysql用户
    export MB_DB_PASS=mysql密码
    export MB_DB_HOST=mysql机器ip
  3. 执行迁移脚本。

    java -jar metabase.jar load-from-h2 ./metabase.db

    输出结果类似如下:

    04-19 18:29:04 INFO metabase.util :: Loading Metabase...
    04-19 18:29:09 INFO util.encryption :: DB details encryption is DISABLED for this Metabase instance. ? 
    See http://www.metabase.com/docs/latest/operations-guide/start.html#encrypting-your-database-connection-details-at-rest for more information.
    04-19 18:29:20 INFO metabase.db :: Verifying mysql Database Connection ...
    04-19 18:29:21 INFO metabase.db :: Verify Database Connection ...  ?
    04-19 18:29:21 INFO metabase.db :: Running Database Migrations...
    04-19 18:29:21 INFO metabase.db :: Setting up Liquibase...
    04-19 18:29:21 INFO metabase.db :: Liquibase is ready.
    04-19 18:29:21 INFO metabase.db :: Checking if Database has unrun migrations...
    04-19 18:29:24 WARN liquibase :: modifyDataType will lose primary key/autoincrement/not null settings for mysql.  Use <sql> and re-specify all configuration if this is the case
    04-19 18:29:24 WARN liquibase :: modifyDataType will lose primary key/autoincrement/not null settings for mysql.  Use <sql> and re-specify all configuration if this is the case
    04-19 18:29:24 INFO metabase.db :: Database has unrun migrations. Waiting for migration lock to be cleared...
    04-19 18:29:24 INFO metabase.db :: Migration lock is cleared. Running migrations...
    04-19 18:29:25 WARN liquibase :: modifyDataType will lose primary key/autoincrement/not null settings for mysql.  Use <sql> and re-specify all configuration if this is the case
    04-19 18:29:25 WARN liquibase :: modifyDataType will lose primary key/autoincrement/not null settings for mysql.  Use <sql> and re-specify all configuration if this is the case
    04-19 18:30:28 INFO metabase.db :: Database Migrations Current ...  ?
    com.mchange.v2.cfg.DelayedLogItem [ level -> FINE, text -> "The configuration file for resource identifier 'hocon:/reference,/application,/c3p0,/' could not be found. Skipping.", exception -> null]
    Temporarily disabling DB constraints...
    [OK]
    Transfering 6 instances of Database....[OK]
    Transfering 6 instances of User....[OK]
    Transfering 12 instances of Setting....[OK]
    Transfering 103 instances of Table....[OK]
    Transfering 2270 instances of Field...........[OK]
    Transfering 1064 instances of FieldValues.......[OK]
    Transfering 352 instances of Revision.....[OK]
    Transfering 1409 instances of ViewLog........[OK]
    Transfering 73 instances of Session....[OK]
    Transfering 6 instances of Dashboard....[OK]
    Transfering 58 instances of Card....[OK]
    Transfering 42 instances of DashboardCard....[OK]
    Transfering 358 instances of Activity.....[OK]
    Transfering 4 instances of PermissionsGroup....[OK]
    Transfering 12 instances of PermissionsGroupMembership....[OK]
    Transfering 16 instances of Permissions....[OK]
    Transfering 4 instances of PermissionsRevision....[OK]
    Transfering 5 instances of Collection....[OK]
    Transfering 2 instances of CollectionRevision....[OK]
    Transfering 16 instances of DataMigrations....[OK]
    Re?nabling DB constraints...
    [OK]

    如果出现The configuration file for resource identifier 'hocon:/reference,/application,/c3p0,/' could not be found的错误,说明前面的配置没有加载,执行source /etc/profile加载配置后再执行上面命令。

  4. 重新启动metabase。

    nohup java -jar metabase.jar &

参考资料:
installing-and-running-metabase

记一次Mysql更新问题的排查

表结构如下:

mysql> desc daily_change;
+------------+---------------------+------+-----+---------------------+----------------+
| Field      | Type                | Null | Key | Default             | Extra          |
+------------+---------------------+------+-----+---------------------+----------------+
| dc_id      | bigint(20) unsigned | NO   | PRI | NULL                | auto_increment |
| date       | date                | NO   | MUL | NULL                |                |
| today      | decimal(20,8)       | NO   |     | NULL                |                |
| yesterday  | decimal(20,8)       | NO   |     | NULL                |                |
| diff       | decimal(20,8)       | NO   |     | NULL                |                |
| diff_rate  | decimal(2,2)        | NO   |     | NULL                |                |
| dc_created | datetime            | NO   |     | 0000-00-00 00:00:00 |                |
| dc_updated | datetime            | NO   |     | 0000-00-00 00:00:00 |                |
+------------+---------------------+------+-----+---------------------+----------------+

现象:
脚本会计算每天的统计情况,计算每天的使用量、昨日的使用、差值和波动比例,然后写到表里。
测试过程中,发现表里记录的diff_rate和更新语句里的diff_rate不一致。
好多记录的diff_rate都是0.99或-0.99,与实际值不一致;有一些记录的值又是对的。

一开始怀疑是不是有其他脚本有更新逻辑,查了下binlog日志,没找到其他更新请求。

在mysql命令行里做了如下测试。

update daily_change set diff_rate = 0 where dc_id = 1;
select * from daily_change where dc_id = 1; // diff_rate变成了0
update dail_change set diff_rate = 3.52 where dc_id = 1;
select * from daily_change where dc_id = 1; // diff_rate变成了0.99

这真是见鬼了,看来不是有其他脚本更新,我的更新sql是没问题的,看来是mysql自己的问题。

看了下diff_rate字段的类型,decimal(2,2)。

decimal(a,b)

参数说明

a指定指定小数点左边和右边可以存储的十进制数字的最大个数,最大精度38。
b指定小数点右边可以存储的十进制数字的最大个数。小数位数必须是从 0 到 a之间的值。默认小数位数是 0。

一下明了了,字段类型导致的锅。
改成decimal(20,2)之后,一切正常。