Translate

Monday, November 2, 2009

How to make rounded corners in Internet Explorer without images [experimental]


Have you ever missed the CSS3 features in IE? If you are a web designer or a Web developer, your answer should obviously be “YES”. The very basic and popular CSS3 feature is to make rounded corners of DIVs and elements. Firefox, Safari, Chrome are now having full support to CSS3 but IE is still far behind in this race. Jonathan Snook, from snook.ca has already found a solution which defines a VML behavior in IE. VML is an XML-based approach that Microsoft came up with and proposed it to the W3C. So, lets now try this method to make rounded corners in IE.

Step 1: You have to define a behavior for VML elements, include the VML namespace and then use the VML syntax. Following code that includes the VML namespace and declares a “v” prefix to be used for all VML elements.

 ns="urn:schemas-microsoft-com:vml" prefix="v" />

Step 2: VML has support for rounded rectangles using the roundrect element. Here is the syntax to define it.

:roundrect arcsize=".02" fillcolor="#000"> Lorem ipsum dolor sit amet, consectetuer adipiscing  >

Step 3: We need CSS support to enable the VML. Here is the syntax:

v\:roundrect  {     behavior:url(#default#VML);     }

Step 4: Now, style this element in CSS, like we do for any other element.

v\:roundrect {     color:#FFF;     display:block;     background-color:#000;     -moz-border-radius:10px;     -webkit-border-radius: 10px;     border-radius: 10px;     padding:20px;     height:100%;       /* IE-specific */     behavior:url(#default#VML);     /background-color:transparent;     }

v\:roundrect {     color:#FFF;     display:block;     background-color:#000;     -moz-border-radius:10px;     -webkit-border-radius: 10px;     border-radius: 10px;     padding:20px;     height:100%;       /* IE-specific */     behavior:url(#default#VML);     /background-color:transparent;     }

That’s it (try a demo below).



Sunday, October 18, 2009

CodeIgniter

An Introduction to CodeIgniter

CodeIgnter is an MVC PHP Framework that helps you write kick-ass applications. It has a number of libraries, helpers and plugins to streamline development and cut overall development time. This tutorial will show you how to get started with CodeIgniter.

Step 1 – About CodeIgniter

CodeIgniter is PHP framework with a rich set of libraries, helpers and plugins for PHP Developers who are in the real world of clients who want things “now” and shared hosting accounts. CI helps you build full-featured web applications with a small footprint, without the use of the Terminal or Command Line Interface. CI is compatible with PHP4 and PHP5, which is a great thing to have in a framework if you plan to use your scripts for different clients. This is a very simple framework to use, and as Rasmus Lerdorf stated he liked it “because it is faster, lighter and the least like a framework.”

CodeIgniter has many features that makes it stand out from the crowd: a powerful autoloading feature enables you to load components for use throughout the whole of your application; a routes file enables you to re-write the way their URL’s work; and it boasts a powerful database class that lets you use multiple databases such as MSSQL, MySQL (and MySQLi), PostgreSQL, SQLite, and ODBC – enabling you to switch databases without change to your codebase.

CodeIgniter has a strong following, and if you need something that isn’t in the core, you are highly likely to find it on the forums or in the wiki. With many solutions for User Authentication, CRUD and Template libraries, as a developer you really are spoiled for choice.

A quick overview of MVC

CodeIgniter uses the Model-View-Controller design pattern; this separates business logic (controllers) from the front end (views) and the database abstraction (models). MVC keeps your code structured and enables you to write more robust applications. For a more detailed overview, please consult the user guide on controllers, views, and models.

CodeIgniter URLs

Before you start developing an application in CodeIgniter, you need to know how that the URLs are constructed using URIs, which is the string following index.php in the URL. There are 3 segments to the URLs, the first is the controller, if you only supply the first segment, CodeIgniter will default to that controllers index function. The second parameter is the function inside the controller. The third represents any data you want to pass to your function. So when looking at a CodeIgniter URL, you will see something like this.

http://www.yourwebsite.ext/ci_dir/index.php/controller/function/parameters

On with the tutorial!

Step 2 – Install CodeIgniter

CodeIgniter is currently at version 1.7.0, this is a stable release and is the version I use in this tutorial. To get 1.7.0, visit codeigniter.com and click on download CodeIgniter, next to the green downward facing arrow.

Once you have downloaded the .zip folder, extract the contents of the folder into a web directory.

So now you have CodeIgniter installed on your system, it’s time to make a few changes.

Step 3 – A Few Tweaks

We now have a fully working version of CodeIgniter up and running either locally or on our web host. We’re now going to make a few changes to the system: we are going to move the application directory, and update our site URL.

Moving the application directory

This step isn’t necessary, but it helps when upgrading to a new version of CodeIgniter. What we need to do is go into our system/ directory and move the application/ directory out of there and into the base.

Thanks to the genius behind CodeIgniter, we don’t need to update any config file for this change. So that’s it! We’ve moved our application directory!

Updating the base URL

In this step, we’ll edit our first config file. Open application/config/config.php with your favourite text editor. I’m using TextMate. You will need to edit line 14 to be the URL of your CodeIgniter installation, in my case it’s http://localhost/codeigniter/. Remember, you need to add a trailing slash!!


Step 4 – The Welcome Controller

The Welcome Controller is a sign that our CodeIgniter install is working correctly. Simply go to your Codeigniter base URL (the one you changed in the config.php file) and you should see the welcome controller.

If your page is blank, you may have misspelled your base URL, check this and try again.

Step 5 – The File Structure

Up until now we haven’t really properly looked at CodeIgniter, we’ve just set up the base install and made a few changes to make our life easier down the line. Now it’s time to really get started. Let’s look at the file structure for the application/ directory.

  • The config folder holds all of our configuration files. We have already bee here to edit our main config.php file, but it also holds things like the autoload file, routes file and database details file.
  • The controllers folder holds all of our controllers. Controllers are the main backbone of the application and call upon Models and output Views to the browser. You can store controllers here in sub folders.
  • The errors folder holds the error templates that CodeIgniter throws when an error is found. The files inside here can be updated to look more like your application. For the purpose of this tutorial, I won’t be edited these files.
  • The helpers folder holds all of the helpers you make. Helpers are a collection of functions that help you do simpler tasks that doesn’t warrant the use of a library.
  • The hooks folder holds all the hooks you create. Hooks are designed to alter the system core at different points in the loading process. Hooks are a more advanced way of editing the system core than just extending libraries and are used in different ways depending on different applications. In most cases they are not needed.
  • The language folder is a place to store the language files for your site.
  • The libraries folder is there to hold all the custom libraries you use. Custom libraries vary from extended core libraries to completely new libraries to offer new functionality such as User Authentication, Templating systems and even CRUD libraries. You can store libraries here in sub folders.
  • The models folder holds all of your models for interacting with the database. You can store models here in sub folders.
  • The views folder will hold all of your view files. You can store views here in sub folders.

Step 6 – The Controller

Now you know all about CodeIgniter, we can go ahead and make ourselves a controller. Create a file called hello.php in our controllers folder. Here’s the top end of my application directory.

Next we get to add the code to our controller. Take a look at the code below.


01.<?php
02.
03.class Hello extends Controller
04.{
05. function Hello()
06. {
07. parent::Controller();
08. }
09.
10. function index()
11. {
12. $this->load->view('hello');
13. }
14.}
15.
16./* End of file hello.php */
17./* Location: application/controllers/hello.php */

As you can see our code begins with a class declaration. All controller classes in CodeIgniter must begin with an uppercase letter and extend to controller class, this enables us to use the CodeIgniter classes and objects. Our constructor function is the same name as our class, this makes our application PHP4 compatible – just like CodeIgniter! (Note: You don’t have to do it this way, I do both out of habit.)

The next function we have is the index function. The index function is the default function that CodeIgniter will turn to when accessing a controller. Taking this controller as an example, when you go to http://www.yourwebsite.ext/ci_dir/index.php/hello, the index function of the Hello class will be executed. The index function uses the powerful CodeIgniter loader class to load a view file. View files mostly will have a .php extension, so there is no need to put the extension in the first parameter of $this->load->view(), unless you use something other than .php.

Step 7 – The View

So our application is almost done, all we have left to do is to create our view file. So make a new view file called hello.php in our views directory. Your full directory tree should look like mine below.


Ok so as we found out earlier on our view files are the thing the user sees. So in this step we’ll just add some HTML to our file.


01.<html>
02.<head>
03. <title>My First CodeIgniter App - Hello World!</title>
04.</head>
05.<body>
06.
07. <p>Hello, World!</p>
08.
09.</body>
10.</html>

Conclusion

This tutorial gave you an overview of CodeIgniter, an overview of the MVC design pattern, how to install CodeIgniter on your server, a few tweaks to make your life easier, and an example Hello World application. You should be a fully fledged CodeIgniter in no time.

Wednesday, October 14, 2009

MySQL – optimize your query to be more scalable (Part 2/2)


databaseExperiment 3

The next experiment is to discover whether using the combination index can speed up the query. The following indices are created:

CREATE INDEX Id on T1 (Id);
CREATE INDEX Time_Id on T2 (Time, Id);

Note: The order of multiple columns index can affect how the query works. See MySQL documentation for more details.

We name this index scheme as E3 and use FORCE INDEX on Time_Id. Same again, the query is run 3 times with FLUSH TABLES.

E3
Test 129 secs
Test 231 secs
Test 332 secs
Average30.66 secs

In fact, this is fractionally slower. The reason may be that the index for T2 table becomes slightly larger and it takes slighltly longer for MySQL load up the index.

Another interesting question how will E3-Full (E3 index scheme with range query cover the full T2 table) compare to E2-Full?

E2-FullE3-Full
Test 12 mins 3 secs1 min 24 secs
Test 21 min 59 secs1 min 44 secs
Test 31 min 59 secs1 min 36 secs
Average2 mins .33 secs1 min 34 secs

Interestingly, using multi-columns index does further improve the performance on a larger scale of query.

Experiment 4

The last experiment uses full covering indices both tables and observes any significant improvement.

CREATE INDEX Id_A_B on T1 (Id, A, B);
CREATE INDEX Time_Id_C on T2 (Time, Id, C);

We name this index scheme as E4 and use FORCE INDEX on Time_Id_C. Same again, the query is run 3 times with FLUSH TABLES.

E4
Test 127 secs
Test 226 secs
Test 326 secs
Average26.33 secs

Having both sides of covering index seems to have a little impact on the performance on sub-range query. On the other hand, there is a noticeable improvement with full range query, E4-Full. This may indicate that the sub-range used (covers 3.9 million rows, 59% of T2 table) for the experiments hasn’t been substantial enough to see the benefit of multiple columns indexing. Nonetheless, there seems a potential for a much larger database table. Perhaps, this would be our next investigation in the future blog, ie varying the ratio of sub-range with covering indexes on a much larger table.

E2-FullE3-FullE4-Full
Test 12 mins 3 secs1 min 24 secs43 secs
Test 21 min 59 secs1 min 44 secs44 secs
Test 31 min 59 secs1 min 36 secs44 secs
Average2 mins .33 secs1 min 34 secs43.66 secs

Why range data is important for a large table?

Having some sort of range data is particular useful for queries with large table. It is because it:

  1. breaks the query and resolves it in smaller chunks. There are articles on the internet showing that it gives a better overall performance when a portion (let say 10% of the data) of a large table query is processed step by step, instead of processing the whole table in one go.
  2. gives better response between multiple client connections accessing the same table. For example, one client requests for read lock while another client operates a query on the whole giant table.
  3. avoids the dead lock of “Copying to tmp table” bug. The bug was first reported on Oct 2005 and it still hasn’t been fixed! See bug 14070. We came across this issue on a large table query with GROUP BY. However, when the same query is broken down into portions with addition of range data condition, deadlock is averted.
  4. provides a better integration with progress bar. It can actually inform the users how far the long query been processed.
  5. uses much less memory and less overall CPU usage.
  6. takes the full advantage of multi-core processor. You can multi-thread your application and run each thread with a range query.

What if the table doesn’t have the range data

Easy, make one. It is pretty common to have an Id column with AUTO_INCREMENT in MySQL tables. The range can then be acquired by MIN(Id) and MAX(Id).

Conclusion

In this article, we showed that we can’t always assume MySQL resolves the query with the optimal approach, especially for large tables. Even using a query with range condition, it is important to check with EXPLAIN first and experiment the benchmark. A significant performance improvement may be accomplished by slightly changing the query.

MySQL – optimize your query to be more scalable (Part 1/2)


databaseIntroduction

When we design and develop a database application, sometimes scalability is not considered or not a priority. When the size of data grows significantly, we often increase the hardware as the defacto solution. By understanding your MySQL queries, there may be easy steps to free up some processing power. In this article, I am going to discuss step by step, how to improve the performance of a simple MySQL query with a semi-large data set. The example shows a speedup from over 8 mins to just less than 30 secs.

Query & data

The query involves two relational tables. One table (T1) contains around 4.4 million rows and the other table (T2) has 6.6 million entries. Both tables are MyISAM and the file sizes are 886MB and 705MB respectively. Basically, T1 holds the key reference by T2 and T2 contains multiple entries for T1 with the same key representing the time base entities. For some reason, the following query takes a long time to perform:

SELECT T1.A, T1.B, SUM(T2.C) FROM T1, T2 WHERE (T1.Id = T2.Id) AND (T2.Time >= X AND T2.Time < style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; ">As you can see the above query involves joining the two tables within a certain time range and finally groups the results together.In each test, the query is run for 3 times and recorded each time taken. The timing starts from the second run of the query, not the first run. The reason is that there are other factors can affect the timing (ie takes slightly longer) such as kernel disk read cache, hardware cache, etc. which are beyond our control. In other words, we run the query first time to get the MySQL index & data into the system cache, then start the experiment.

Experiment 1
Our first choices of indices for T1 and T2 are as follows:

CREATE INDEX Id ON T1 (Id)
CREATE INDEX Id ON T2 (Id)
CREATE INDEX Time ON T2 (Time)

We name this scheme of indices as E1 for performance comparison. The query is running on an idle machine 3 times with FLUSH TABLES.

E1
Test 17 mins 35 secs
Test 26 mins 14 secs
Test 310 mins 56 secs
Average8 mins 15 secs

To understand what takes the query so long, EXPLAIN gives the following explanation:

test1

The query was actually performed without using any indices. The (ALL in type column) whole T1 table was scanned and then joined with table T2.Id (ref column) and finally only filtered with the time range condition. This seems a bit strange as I would expect MySQL to resolve the query by filtering T2.Time first as that would remove many entries before the join is started.

After doing some studies on MySQL, I realised that MySQL is preferrable to scan the whole table because in general this gives a better performance. However, in this case the query is binding 4 million rows to another 6 million rows. This can take a lot of processing.

Experiment 2

The next experiment is to make sure MySQL resolves the query using T2.Time first and with the same index scheme as in experiment 1, E1. There are three ways to enforce that:

  1. Covering index – index all the T2 fields appearing in the SELECT statement
  2. LIMIT – query ends with LIMIT
  3. FORCE or USE – force or hint the MySQL to use a particular index

For method 1, in order to use the T2.Time first, we have to create an index (Time, Id, C). There are a few drawbacks:

  • Adding overhead to the INSERT statement
  • Impractical when columns are changed and maintaining the index
  • Impractical if the table has many fields

For method 2, it is out of the question as we need to retrieve all the results

In our case, method 3 compromises our product the least. It only requires the index name within the FROM clause, as long as the conditions inside the WHERE clause are not likely to be changed.

Using EXPLAIN on

SELECT T1.A, T1.B, SUM(T2.C) FROM T1, T2 FORCE INDEX (Time) WHERE (T1.Id = T2.Id) AND (T2.Time >= X AND T2.Time <>

it shows

test2

As you can see the FORCE INDEX changes the order of how MySQL resolves the query. This should actually speed up the query significantly in a number of ways

  • Time is indexed in ascending order using B-Tree, so MySQL should quickly locate the entries within the given range
  • MySQL only needs to perform binding with T1 and T2 on a smaller set of entries
  • this also results in MySQL spending less time looking up the data pages (where non-indexed data is stored) once the WHERE is evaluated

We named this scheme as E2. The query runs 3 times starting with FLUSH TABLES each time.

E1E2
Test 17 mins 35 secs28 secs
Test 26 mins 14 secs27 secs
Test 310 mins 56 secs27 secs
Average8 mins 15 secs27.33 secs

As you can see, this is a huge improvement on performance. However the speedup is entirely reliant on the number of entries covered by the given range. So, the next interesting question will be if I enlarge the range query to cover the entire table in T2 (we name this result as E2-Full), would it take as long as E1?

E1E2-Full
Test 17 mins 35 secs2 mins 3 secs
Test 26 mins 14 secs1 min 59 secs
Test 310 mins 56 secs1 min 59 secs
Average8 mins 15 secs2 mins .33 secs

The answer is no. Although on the surface both queries seem to process all the entries, the order in which MySQL processes the data makes a significant difference.

In E2-Full, since it processes all the queries first on the Time index (ascending order), the time range condition is resolved instantly. Then all the rows in T2 are joined with T1.Id which is indexed. In contrast, with E1 the whole T1 table is loaded up first, then joins both tables based on Id value. The extra overhead comes from processing the range comparison for each of the joined entry.

In the next part, we will investigate whether we can speedup the query furthermore.