Yii2 Application Optimization

When projects are behind schedule, programmers may be tempted to implement solutions that are not the most optimal. This can result in clients having to deal with a variety of application issues, such as speed. In our applications, we often use Yii2 framework and through our experience we have learned how to make our applications faster by using optimal solutions.

Vitaliy Shulyak
Fullstack Developer

In order to implement the best possible solutions, we typically prepare a roadmap. This roadmap consists of easiest to most labor-intensive steps to help us optimize our applications and our time.

One simple and quick solution is to upgrade our servers and make use of more memory, CPU, etc. Alas, this route has considerable drawbacks, such as cost. However, there are other ways to achieve faster speeds in your applications and in this blog post, I’d like to share with you some tips that I’ve gained through my personal experience working with Yii2 applications.

My first tip is to conduct a small test using the Apache HTTP Server Benchmarking Tool. Here is an example:

ab -n 100 -c 10 http://<your domain>/

In this example, we get poor results:

Requests per second:    4.39 [#/sec] (mean)
Time per request:       228.039 [ms] (mean, across all concurrent requests)

Using a few small tweaks, we can speed up the work:

  • Update the framework, php, and mysql to the latest versions.

Please note that this is only a recommendation. Unfortunately, it is not always possible to do this in real life projects. Nevertheless, using the newest versions can be much better in the long run and can resolve problems with speed, security, as well as add extra features. However, it should also be noted that updating versions should be very attentive. New versions can break existing features due to some points getting deprecated and deleted.

  • Check project environments and turn everything on in production mode. Furthermore, disable debugging mode on the production server.
defined('YII_DEBUG') or define('YII_DEBUG', false);
defined('YII_ENV') or define('YII_ENV', 'prod');
  • Add quick configuration changes.

  • Add caching

Yii2 framework supports a lot of cache methods, some examples are:

yii\caching\ApcCache
yii\caching\DbCache
ii\caching\FileCache
yii\caching\MemCache
yii\redis\Cache

FileCache being the simplest, it is also the least optimal one. Nonetheless, even it will help in optimizing your project. Decide what’s best for your needs.

  • Add in your config file:
return [
    'components' => [
        'cache' => [
            'class' => 'yii\caching\FileCache',
        ],
    ],
];
  • Enable database connection cache

  • Add in your config file:

return [
    'components' => [
        'db' => [
            'class' => 'yii\db\Connection',
            'dsn' => 'mysql:host=localhost;dbname=mydatabase',
            'username' => 'root',
            'password' => '',
            'enableSchemaCache' => true,
            'schemaCacheDuration' => 3600,
            'schemaCache' => 'cache',
        ],
    ],
];
  • Use a session storage, such as Redis server. Working with files is always slower than working with RAM.

  • Add in your config file:

return [
    'components' => [
        'session' => [
            'class' => 'yii\redis\Session',
            'redis' => [
                'hostname' => 'localhost',
                'port' => 6379,
                'database' => 0,
            ]
        ],
    ],
];
  • Enable page cache
class DefaultController extends Controller
{
	...
	public function filters()
    {
        return [
        	[
            	'COutputCache',
                'duration' => 60,
				'varyByParam' => ['id'],
            ],
        ];
    }
}
  • Add static file resources optimization.

The easiest way to do this is to enable nginx static file cache on the server side. This change can help with server connections and allows static files to be used in browser cache.

location ~ \.(js|css|png|jpg|gif|swf|ico|pdf|mov|fla|zip|rar)$ {
	expires max;
}

location ~* \.(?:jpg|jpeg|gif|png|ico|cur|gz|svg|svgz|mp4|ogg|ogv|webm)$ {
  expires 1M;
  access_log off;
  add_header Cache-Control "public";
}
  • Enable text compression on the server side. This additional setting can reduce the request content size.
server {
	...
	gzip on;
    gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript application/javascript;
	...
}
  • Add component loader optimization.

composer dumpautoload -o

Once these tweaks have been implemented, we should be able to see some significant improvements:

Requests per second:    22.74 [#/sec] (mean)
Time per request:       43.982 [ms] (mean, across all concurrent requests)

These are some of the quickest and easiest steps we can take to optimize our applications. Once these things have been implemented and/or reviewed, we can further optimize our applications by resolving bad solutions in code. This can take more time, resources and can be done in multiple different ways. Here are a few things to look into:

Database

Working with the database, we can use MySQL. If you’ve upgraded to the latest version, you can work with queries.

  • Optimize slow queries using MySQL Slow Query Log
log_slow_queries = 1;
slow_query_log_file = <some file name>;
  • Remove duplicate queries (can be searched using Yii2 debugger)
[
'bootstrap' => ['debug'],
'modules' => [
    'debug' => [
        'class' => 'yii\debug\Module',
    ],
]

]
  • Use cache on the most used queries
$result = $db->cache(function ($db) {

    // SQL queries that use query caching

    // do not use query caching for this command
    $customer = $db->createCommand('SELECT * FROM customer WHERE id=1')->noCache()->queryOne();

    // ...

    return $result;
});
  • Remove from the queries a selection of data that is not used

This:

SELECT id, column1, column2 ..
FROM table_name

is better than this:

SELECT *
FROM table_name
  • Normalize / Denormalize tables

  • Add table columns indexes

Remote services

Working with remote services can add many problems. If it’s possible, break away from remote services. You can also prepare and save data from remote services using background workers, queues or cron jobs. This should resolve problems when remote services are broken or have incorrect work.

Work with resources

  • Use cdn servers for css/js libraries
  • Use of minified libraries. Most libraries have min versions; this should be better than full versions on production mode.
  • For custom scripts / styles, you can combine and minify.

Project code

You can use php xhprof extension; this extension can help with dirty code and fix bottlenecks.

Work With Server Configuration and Refactoring Structure

There is not one perfect solution for this and all possible solutions should be analyzed and worked on individually.

There are numerous ways to optimize application speed and you should look into each possible solution. It is always better to use optimal solutions from the get-go in order to save yourself and your client a headache. The tips I’ve shared in this blog post are not all possible solutions and there may be other options you may want to explore to find what’s best for your needs.