• php yaf框架扩展实践五——数据层


    从狭义角度上来理解数据层就是数据库,比较广义的理解来看数据库、远程数据、文件等都可以看做数据层。项目初期的时候一般单一的数据库就可以了,随着流量的增大就要对数据层做很多的改进,例如增加从库分散读压力,使用kv缓存增加系统性能,又或者使用分布式服务这样就会涉及到到远程数据调用。这么多东西该怎么整呢?项目好像越来越乱了。

    当涉及的东西多了,如果没有良好的项目结构就会导致项目层次越来越乱,很容易出问题。下面就分享一下在yaf中数据层设计经验。分为如下:

    • 数据抽象层DAO
    • 数据库Mysql
    • KV缓存Redis
    • 远程调用Http
    yaf扩展数据层

    yaf扩展数据层

    数据抽象层DAO

    DAO全称就是Data Access Object,通俗的讲就是数据访问对象。该层提供统一对外的数据访问接口,具体是要调用数据库、http、redis的数据由DAO决定处理。DAO层保存在目录:application/models/DAO

    例如我们需要根据用户编号获取用户信息,可以在DAO目录下新建一个User.php文件,其中有一个find方法:

    1
    2
    3
    4
    public function find($userId) {
        $mysql = MysqlUserModel::getInstance();
        return $mysql->find($userId);
    }

    这里就是直接从数据库里进行读取。当这个方法调用很频繁,数据库负载上来时,我们考虑使用kv缓存来缓解数据库压力:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    $redis = RedisDb0UserModel::getInstance();
    $user $redis->find($userId);
    if (!$user) {
        $mysql = MysqlUserModel::getInstance();
        $user $mysql->find($userId);
        if ($user) {
            $redis->update($userId$user);
        }
    }
    return $user;

    这里先从redis内读取用户信息,如果没有则从mysql里读取。因为使用了redis缓存,如果数据有更新的时候需要同步更新缓存里的内容,这个在DAO层内就很容易做到了。

    此外在DAO层的抽象方法里实现了一个方法,当访问的方法不存在的时候会自动调用数据库mysql对应的同名方法,这个在项目前期可以大大提高开发效率,缺点就是当用ide调用的时候没有代码提示。

    数据库Mysql

    yaf没有提供数据库操作的封装,这里使用了zend framework2的db类库进行数据库的操作。zend framework虽然整体性能慢,但是类库非常齐全、封装的也非常好,这里直接使用zend framework的db库进行操作避免重复造车轮子。mysql的保存目录:application/models/Mysql

    创建数据表操作类

    mysql层有一个抽象类,定义了表名和表主键属性,文件建议以表名进行命名而后继承mysql的抽象类,在类中指明相应的表和主键就可以了。如用户表文件User.php:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    class UserModel extends MysqlAbstractModel {
     
        /**
         * 表名
         *
         * @var string
         */
        protected $_tableName 'user';
     
        /**
         * 主键
         *
         * @var string
         */
        protected $_primaryKey 'user_id';
     
        ....
    }

    在mysql的抽象类里封装了find、fetchAll、insert、update、remove增删改查的方法,可以直接使用User的类实例进行调用,就像上面讲DAO时直接调用find方法。这些方法基本可以满足80%-90%的数据查询需求,倘使需要更加复杂的查询,可以调用父类的_getDbSelect方法获取endDbSqlSelect对象自定义查询,关于endDbSqlSelect可以参考zend framework手册。

    使用从库

    如果需要使用从库来进行均衡读负载,可以将从库的操作在mysql/Slave目录,Slave目录下有一个单独的抽象类,因为从库一般用来读,所以这里只封装了find、fetchAll方法。在需要定位到从库时的地方重写相应的find、fetchAll方法就可以了。例如读取用户列表数据可以考虑从从库读取,重写MysqlUserModel类的fetchAll方法:

    1
    2
    3
    4
    public function fetchAll($columns = null, $where = null, $order = null, $count = null, $offset = null, $group = null) {
        $slave = MysqlSlaveUserModel::getInstance();
        return $slave->fetchAll($columns$where$order$count$offset$group);
    }

    Tips:使用从库进行数据读取需要注意数据实时性的问题,虽然从库的数据同步一般都在毫秒级,但是在程序操作中可能出现主库已经插入数据,但是从库读取不到的问题。建议数据实时性要求不高的才从库进行读取。

    多个库

    有时一个项目涉及到要连接多个数据库,这时候可以考虑在mysql目录下新建目录,目录名使用数据库名进行命名。每个库内的抽象类文件可以考虑继承通用的抽象文件,重写其中的_getAdapter就可以了。

    KV缓存Redis

    Redis存放在:application/models/Redis目录。同mysql一样,这里也有一个抽象类文件,相应的文件继承该文件就可以了。下面讲下和mysql的不同点。

    redis多库

    一个redis实例总共有16个库,从db0~db15,在项目中以Db0、Db1这样的目录进行区分,每个db下的抽象类继承默认抽象类,重写$_db属性为相应的库就可以了。例如Db0这个库:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    class AbstractModel extends RedisAbstractModel {
        /**
         * 连接的库
         *
         * @var int
         */
        protected $_db = 0;
     
        ....
    }

    在开发中不建议将所有的缓存都放在一个库下,有的时候我们需要使用keys命令来查找到相应的key,并进行数据更新。如果该库下缓存太多会导致性能很低。建议按照数据的重要性和时效性进行分布存储。

    缓存设计思想

    kv缓存并没有表的概念,但是为了更好的对应用的整个缓存系统进行操作,这里抽象出表的概念。这里的表相当于key中的一个前缀,通过和id组合成实际的key可以很好的避免key值出现重复。例如上面DAO层从redis读取用户数据,抽象出了一个用户表。

    Tips:表名和id是通过一个分隔符号组成,这里是使用横线”-”进行拼接,可以根据实际需要进行修改,只需要保证最后组合成的key是唯一的就OK了。

    远程调用Http

    项目越来越多、访问量也越来越大,这时候我们考虑将通用的服务提取出来,部署到另外的服务器上。这时候就需要通过网络进行远程调用了,有一个专业术语叫RPC(Remote Procedure Call Protocol)。这里我们使用的http协议,方便在各个语言之间进行通讯, 当然也可以使用一些rpc框架来实现。

    Http存放在:application/models/Http目录。抽象类封装了request请求方法。建议以不同的应用进行目录划分,例如我们将用户中心分离出来之后,用户中心的数据可以通过http进行调用。可以在http目录下新建User目录,User目录下的抽象类继承默认抽象类,重新定义host就可以了。

    一般来讲一个应用的接口都有统一的数据格式定义,在实际的开发中通常会在User的抽象类中重写父类的request方法,并统一处理相应的数据格式。

    小结

    这里只列举了mysql、redis等库,类似文件存储、mongodb、memcache、sql server等都可以按照这样的思想进行处理。项目到后期基本上瓶颈都会在数据层上,只要在数据层方面处理妥当了,便能够很好的实施扩展以应对一些高并发场景的情况。

    http://www.01happy.com/php-yaf-ext-data/

  • 相关阅读:
    [LeetCode]Reverse Linked List II
    [LeetCode]Move Zeroes
    Next Greater Element I
    Keyboard Row
    Number Complement
    SQL语句学习(二)
    SQL语句学习(一)
    jQuery学习(三)
    jQuery学习(二)
    JQuery学习(一)
  • 原文地址:https://www.cnblogs.com/hellowzd/p/5391360.html
Copyright © 2020-2023  润新知