EduSoho做为“服务了十万家网校用户”的在线教育系统,汇集了很多开发者的心血,系统在不断发展中越来越完善。一个具有良好性能的系统取决于代码的设计和质量,在此和大家分享几个 EduSoho 开发的小技巧。

文章配图-1

一、在代码中降低资源消耗,提高加载速度

程序的性能好坏直接影响到网站的整体体验。在EduSoho开发过程中,如果能正确地使用系统默认提供的一些接口和函数,可以有效地提高代码的执行效率。

1. 使用查询最佳实践

EduSoho 中的 Dao 层提供了一部分内置的查询函数,主要有 get、search、findByFields。简单地按照功能区分如下:

单行数据查询:get,性能提升的空间不大

多条数据查询:search、findByFields,有性能提升的空间

1.1 search

search 函数接口

search($conditions, $orderBys, $start, $limit, $columns = array())

1

search 使用场景:在一些数据量不可控、需要排序、需要分页限制的场景中。

场景1 列表展示的场景

应用到一些列表展示的场景,只要控制好查询条件和分页,毫无问题。

场景2 查找课程下所有学员的id

比如仅仅想要查找课程下所有学员的id,在考虑选取具体措施的时候可以说是非常的纠结。

考虑1: 直接写个 SQL 语句去获取觉得有点多余;

考虑2: 课程的学员数量又是不可控的,课程学员数量多的时候,使用 findByFields 全部搜索出来必然会增加进程内存的消耗。

场景2 解决方案 —— columns

我们可以使用 search 函数的第五个参数 columns 来解决这个难点。columns 含义是限制输出的列。巧妙地使用 columns,就可以降低内存占用率。它的优势如下:

大大降低了 MySQL 的查询时间;

减少了输出数据。

$this->getCourseDao()->search(

array(...),

array(),

0,

PHP_INT_MAX, // 在输出字段 columns 可控的时候,使用PHP_INT_MAX可以接受

array('id') // columns,按需取列,降低内存消耗,提高性能的利器

);

1.2 findByFields

findByFields 函数接口

findByFields 只有 查询条件 一个参数。

findByFields($fields);

1

findByFields 使用场景:应用在一些查询条件可控、表的数据量可控、无分页要求、无需排序的场景中。

使用最多的场景类似于 findByIds、findByXXXId。$fields 有很明确的参数限制,这样可以增加代码的可读性,同时降低内存泄漏的风险。如果想要灵活设定参数值,建议使用 search 的 conditions。

2. 使用循环结构最佳实践

循环作为一个基本的逻辑代码块,大多出现在批量数据的处理上。而批量处理数据往往是最易消耗性能的地方。遵循以下原则,在一定程度上可以降低循环带来的时间和性能消耗。

2.1 使用循环的原则

2.1.1 避免循环多层嵌套

很多代码会出现循环中嵌套多层循环的现象。其实遇到这种情况,首先该思考业务的实现思路是否合理。多层嵌套循环会使代码的可读性变差,无论是开发当时还是后续回顾都极易杀死更多脑细胞。

2.1.2 将循环次数做到可控

举个栗子,我需要将某个课程下的所有学员都进行数据处理后再更新。如果将所有学员信息直接一次性

搜索出来,在课程学员数据量较大的情况下,往往还没开始循环,内存就先炸了。需要对循环的数据进行分组, 利用 search 方法一次只取出 500 条数据进行处理,处理完一批再处理下一批,这样的好处是可以保证内存可控。

2.1.3 循环代码块中避免 MySQL 取操作

避免在循环逻辑块中执行像 MySQL 读这样的操作。这样会大大增加程序的执行时间。正确的做法是将一组数据需要的资源批量准备好。这样在一定程度上可以减小 MySQL 的压力,减少执行时间。

2.1.4 循环代码块中避免 MySQL 写操作

避免在循环中更新数据。建议先在循环中准备好要更新的数据,等到一组循环执行结束后,批量更新。AdvancedDao 提供了 batchUpdate、batchCreate 等功能,可以配合循环使用。

二、减少代码中的危险操作

数据库是一个程序宝贵的持久化资源,代码中如果使用不当,极易造成数据的丢失。

优雅地使用 Update 和 Delete 函数

EduSoho 提供了更新、删除数据的函数 update、 delete

AdvancedDao 提供了 batchUpdate、batchDelete

update|delete 使用场景

适用于将一组数据批量更新或者批量删除同一组值。

update 函数实践

// $identifier : int型会通过$id更新, 数组会通过$conditions更新

update($identifier, array $fields)

更新形式取决于传入 $identifier 的参数类型:

int 型会通过 $id 更新单条数据,简单明了,操作简单,一般不会出现数据安全问题;

数组会通过 $conditions 更新,可能存在数据的“越权操作”风险。

数据的越权操作

需要注意的是,根据查询条件去更新极易造成数据的“越权操作”。比如,我们本来要更新某个课程下所有课时的某个字段,正常情况下会有类似这样的操作。

update(

array(

'courseId' => 1

),

$fields

)

以上代码集齐了四个因素就能解锁将整个数据库更新的技能!

第一个参数是数组类型,意味着此处的查询条件会很灵活。

php 是弱类型的语言,很多情况下数据的类型不会这么敏感。

传入一个'courseId' => null 的值

恰好使用了 array_filter 函数进行筛选

surprise !!! 灾难就发生了,我将整个数据库的数据全部更新了!不要以为我在开玩笑,很多代码就是在这种无意识中被写出来了。并且常规情况不会触发,只有在某些极端情况下被触发,极其难被测试出来。

update 批量更新原则

保证参数严格可控,业务层永远不能直接通过调用 update 实现批量更新

引用上文的例子,我们需要根据业务封装一个严格控制参数的函数。

public function updateByCourseId($courseId, $fields)

{

// 保证参数courseId有效

if (empty($courseId)) {

return false;

}

// 保证参数严格限定,不会有额外的参数

$conditions = array('courseId' => $courseId);

return $this->getLessonService()->update($conditions, $fields);

}

提取需要更新数据的唯一标识符

批量更新的时候,可以将要更新数据的唯一标识符取出来,然后再去更新,也不失为一个好办法。

public function updateByCourseId($courseId, $fields)

{

// 保证参数courseId有效

if (empty($courseId)) {

return false;

}

// 查找符合条件的ids

$ids = $this->getLessonService()->search(

array('courseId' => $courseId),

array(),

0,

PHP_INT_MAX,

array(id)

);

$ids = ArrayToolkit::column($ids, 'id');

if (!$ids) {

return false;

}

return $this->getLessonService()->updateByIds($ids, $fields);

}

batchUpdate|batchDelete 使用场景

适用于更新和删除一批数据中,并且值不同的状况。

批量更新和删除依赖于 AdvancedDao 的扩展功能具体使用很简单,不再赘述。

结束语

程序开发从来都不是一件枯燥的事情,当你乐意去思考代码的细节和实现思路,你将会爱上你的代码。

如文章中有不同的观点和建议,欢迎斧正~

我们正在寻求外包团队

EduSoho官方开发文档地址

EduSoho官网 https://www.edusoho.com/

EduSoho开源地址 https://github.com/edusoho/edusoho

---------------------

原创文章,作者:EduSoho小阔

版权归作者所有,请勿转载

想了解更多教育资讯

请关注【EduSoho网校系统】微信公众号