検索
ホームページデータベースmysql チュートリアルmysqlデータベースの最適化まとめ

mysqlデータベースの最適化まとめ

Mar 09, 2018 am 10:56 AM
mysql要約するデータベース

PHP 開発で mysql データベースを使用することは、すべての PHP プログラマーの習慣になっているようです。PHP で mysql をより高速かつ便利に動作させたい場合は、mysql データベースの最適化も面接でよく質問されます。一緒に見てみましょう!

1. MySQL クエリ キャッシュを最適化する

MySQL サーバーでクエリを実行する場合、高速クエリ キャッシュを有効にすることができます。データベース エンジンにバックグラウンドで静かに処理させることは、パフォーマンスを向上させる最も効果的な方法の 1 つです。同じクエリが複数回実行される場合、結果がキャッシュから取得されると非常に高速になります。
しかし、主な問題は、それがあまりにも簡単に隠蔽されてしまうため、私たちプログラマーのほとんどがそれを無視していることです。一部の処理タスクでは、実際にクエリ キャッシュが機能しなくなる可能性があります。

// query cache does NOT work
$r = mysql_query("SELECT username FROM user WHERE signup_date >= CURDATE()");
 // query cache works!
 $today = date("Y-m-d");
$r = mysql_query("SELECT username FROM user WHERE signup_date >= '$today'");
 // query cache does NOT work
$r = mysql_query("SELECT username FROM user WHERE signup_date >= CURDATE()");
 // query cache works!
$today = date("Y-m-d");
$r = mysql_query("SELECT username FROM user WHERE signup_date >= '$today'");

2. EXPLAIN を使用して SELECT クエリを明確にする

EXPLAIN キーワードを使用すると、MySQL が実行しているクエリ操作の種類を理解できるようになります。ボトルネックを見つけて、クエリまたはテーブル構造のどこで問題が発生しているかを示すのに役立ちます。
EXPLAIN クエリの結果から、どのインデックスが参照されているか、テーブルがどのようにスキャンされ、並べ替えられているかなどがわかります。
SELECT クエリ (結合を含むより複雑なクエリが望ましい) を実装し、そこにキーワードの説明を追加します。これにより、テーブル内の結果が表示されます。たとえば、結合を実行するときにインデックスに列を追加するのを忘れた場合、EXPLAIN は問題の発見に役立ちます。

group_id フィールドにインデックスを追加した後

3. LIMIT 1 を使用して一意の行を取得します

場合によっては、テーブルにクエリを実行する必要があることがわかっている場合があります。 1行を見てください。非常にユニークなレコードを探している場合もあれば、WHERE 句を満たす存在するレコードの数を確認しているだけである場合もあります。
この場合、LIMIT 1 を追加するとクエリがより効率的になります。このようにして、データベース エンジンはテーブルまたはインデックス全体をスキャンするのではなく、1 つだけを見つけた後にスキャンを停止します。

 // do I have any users from Alabama?   
 // what NOT to do:   
 $r = mysql_query("SELECT * FROM user WHERE state = 'Alabama'");   
 if (mysql_num_rows($r) > 0) {   
     // ...   
 }     
 // much better:   
 $r = mysql_query("SELECT 1 FROM user WHERE state = 'Alabama' LIMIT 1");   
 if (mysql_num_rows($r) > 0) {   
     // ...   
 }

4. インデックス内の検索フィールド

インデックスは主キーや一意のキーだけではありません。テーブル内の任意の列を検索する場合は、常にインデックスを指定する必要があります。

5. 結合インデックスが同じタイプであることを確認します

アプリケーションに複数の結合クエリが含まれている場合は、リンクする列が両方のテーブルでインデックス付けされていることを確認する必要があります。これは、MySQL が内部結合操作を最適化する方法に影響します。
さらに、追加された列は同じタイプである必要があります。たとえば、DECIMAL カラムを結合し、同時に別のテーブルの int カラムを結合する場合、MySQL は少なくとも 1 つのインジケータを使用できなくなります。文字エンコーディングも文字列型と同じである必要があります。

 // looking for companies in my state   
 $r = mysql_query("SELECT company_name FROM users  
     LEFT JOIN companies ON (users.state = companies.state)  
     WHERE users.id = $user_id");   

 // both state columns should be indexed   
 // and they both should be the same type and character encoding   
 // or MySQL might do full table scans

6. BY RAND() コマンドを使用しないでください

这是一个令很多新手程序员会掉进去的陷阱。你可能不知不觉中制造了一个可怕的平静。这个陷阱在你是用BY RAND()命令时就开始创建了。
如果您真的需要随机显示你的结果,有很多更好的途径去实现。诚然这需要写更多的代码,但是能避免性能瓶颈的出现。问题在于,MySQL可能会为表中每一个独立的行执行BY RAND()命令(这会消耗处理器的处理能力),然后给你仅仅返回一行。

 // what NOT to do:   
 $r = mysql_query("SELECT username FROM user ORDER BY RAND() LIMIT 1");   
 // much better:   
 $r = mysql_query("SELECT count(*) FROM user");   
 $d = mysql_fetch_row($r);   
 $rand = mt_rand(0,$d[0] - 1);   
 $r = mysql_query("SELECT username FROM user LIMIT $rand, 1");

7. 尽量避免SELECT *命令

从表中读取越多的数据,查询会变得更慢。他增加了磁盘需要操作的时间,还是在数据库服务器与WEB服务器是独立分开的情况下。你将会经历非常漫长的网络延迟,仅仅是因为数据不必要的在服务器之间传输。
始终指定你需要的列,这是一个非常良好的习惯。

 // not preferred   
 $r = mysql_query("SELECT * FROM user WHERE user_id = 1");   
 $d = mysql_fetch_assoc($r);   
 echo "Welcome {$d['username']}";   
 // better:   
 $r = mysql_query("SELECT username FROM user WHERE user_id = 1");   
 $d = mysql_fetch_assoc($r);   
 echo "Welcome {$d['username']}";    
 // the differences are more significant with bigger result sets

8. 从PROCEDURE ANALYSE()中获得建议

PROCEDURE ANALYSE()可让MySQL的柱结构分析和表中的实际数据来给你一些建议。如果你的表中已经存在实际数据了,能为你的重大决策服务。

9. 准备好的语句

准备好的语句,可以从性能优化和安全两方面对大家有所帮助。
准备好的语句在过滤已经绑定的变量默认情况下,能给应用程序以有效的保护,防止SQL注入攻击。当然你也可以手动过滤,不过由于大多数程序员健忘的性格,很难达到效果。

 // create a prepared statement   
 if ($stmt = $mysqli->prepare("SELECT username FROM user WHERE state=?")) {    
     // bind parameters   
     $stmt->bind_param("s", $state);    
     // execute   
     $stmt->execute();    
     // bind result variables   
     $stmt->bind_result($username);     
     // fetch value   
     $stmt->fetch();    
     printf("%s is from %s\n", $username, $state);     
     $stmt->close();   
 }

10. 将IP地址存储为无符号整型

许多程序员在创建一个VARCHAR(15)时并没有意识到他们可以将IP地址以整数形式来存储。当你有一个INT类型时,你只占用4个字节的空间,这是一个固定大小的领域。
你必须确定你所操作的列是一个UNSIGNED INT类型的,因为IP地址将使用32位unsigned integer。

$r = "UPDATE users SET ip = INET_ATON('{$_SERVER['REMOTE_ADDR']}') WHERE user_id = $user_id";

11.永远为每张表设置一个ID

我们应该为数据库里的每张表都设置一个ID做为其主键,而且最好的是一个INT型的(推荐使用UNSIGNED),并设置上自动增加的AUTO_INCREMENT标志。
就算是你users表有一个主键叫“email”的字段,你也别让它成为主键。使用VARCHAR类型来当主键会使用得性能下降。另外,在你的程序中,你应该使用表的ID来构造你的数据结构。
而且,在MySQL数据引擎下,还有一些操作需要使用主键,在这些情况下,主键的性能和设置变得非常重要,比如,集群,分区……
在这里,只有一个情况是例外,那就是“关联表”的“外键”,也就是说,这个表的主键,通过若干个别的表的主键构成。我们把这个情况叫做“外键”。比如:有一个“学生表”有学生的ID,有一个“课程表”有课程ID,那么,“成绩表”就是“关联表”了,其关联了学生表和课程表,在成绩表中,学生ID和课程ID叫“外键”其共同组成主键。

12. VARCHAR の代わりに ENUM を使用します

ENUM 型は非常に高速でコンパクトです。実際には、TINYINT が保存されますが、文字列として表示されます。このようにして、このフィールドを使用して選択リストを作成するのが非常に完璧になります。
「性別」、「国」、「民族」、「ステータス」、「部門」などのフィールドがあり、これらのフィールドの値が制限され固定されていることがわかっている場合は、ENUM を使用する必要があります。 VARCHAR の代わりに。
MySQL には、テーブル構造を再編成する方法を示す「提案」 (項目 10 を参照) もあります。 VARCHAR フィールドがある場合、この提案はそれを ENUM 型に変更するように指示します。 PROCEDURE ANALYSE() を使用すると、関連する提案を取得できます。

13. PROCEDURE ANALYSE() から提案を取得する p プログラマー ステーション

PROCEDURE ANALYSE() は、MySQL を使用してフィールドとその実際のデータを分析し、いくつかの有用な提案を提供します。いくつかの大きな決定を下すには基礎としてデータが必要であるため、これらの提案はテーブルに実際のデータがある場合にのみ役に立ちます。
たとえば、主キーとして INT フィールドを作成したが、データがあまりない場合、PROCEDURE ANALYSE() はこのフィールドのタイプを MEDIUMINT に変更することを推奨します。または、VARCHAR フィールドを使用している場合は、データが少ないため、ENUM に変更するよう提案される場合があります。これらの提案はすべて、十分なデータがなく、意思決定が十分に正確ではないために可能になります。
phpmyadmin では、テーブルを表示するときに [テーブル構造の提案] をクリックしてこれらの提案を表示できます。これらの提案は、テーブル内のデータが増えた場合にのみ変更されることに注意してください。正確な。最終決定を下すのは自分であることを常に忘れないでください

14. 可能な限り NOT NULL php プログラマ ステーションを使用してください
NULL 値を使用する特別な理由がない限り、常に次のようにする必要があります。フィールドは NULL にしないでください。これは少し物議を醸すかもしれませんが、読み続けてください。

まず、「Empty」と「NULL」(INT の場合、0 と NULL) の違いは何なのかを自問してください。両者に違いがないと思われる場合は、NULL を使用しないでください。 (ご存知ですか? Oracle では、NULL と空の文字列は同じです!)

NULL にはスペースが必要ないと考えないでください。NULL には追加のスペースが必要であり、比較を実行するとプログラムがより複雑になります。もちろん、これは NULL を使用できないという意味ではありません。現実は非常に複雑なので、NULL 値を使用する必要がある状況が依然として存在します。
以下は MySQL 自身のドキュメントからの抜粋です:

15. プリペアド ステートメント

プリペアド ステートメントは、バックグラウンドで実行される SQL ステートメントのコレクションに似ています。パフォーマンスの問題であれ、セキュリティの問題であれ、プリペアドステートメントの使用から解放されます。

プリペアド ステートメントはバインドした一部の変数をチェックできるため、プログラムを「SQL インジェクション」攻撃から保護できます。もちろん、変数を手動でチェックすることもできますが、手動によるチェックは問題が発生しやすく、プログラマによって忘れられることがよくあります。何らかのフレームワークやORMを使用すると、この問題は改善されるでしょう。 パフォーマンスの面では、同じクエリが複数回使用される場合に、パフォーマンスが大幅に向上します。これらのプリペアド ステートメントにはいくつかのパラメータを定義できますが、MySQL はそれらを 1 回だけ解析します。 最新バージョンの MySQL はプリペアド ステートメントの送信時にバイナリ形式を使用しますが、これによりネットワーク送信が非常に効率的になります。
もちろん、プリペアド ステートメントはクエリ キャッシュをサポートしていないため、プリペアド ステートメントの使用を避ける必要がある場合もあります。ただし、バージョン5.1以降でサポートされるとのこと。 PHP でプリペアド ステートメントを使用するには、そのマニュアルを確認するか、mysqli 拡張機能を確認するか、PDO.


16 などのデータベース抽象化レイヤーを使用します。

通常の状況では、スクリプト内で SQL ステートメントを実行すると、SQL ステートメントが返されなくなるまでプログラムはそこで停止し、その後、プログラムは実行を続けます。バッファリングされていないクエリを使用して、この動作を変更できます。
この問題については、PHP ドキュメントに非常に詳しい説明があります: mysql_unbuffered_query() 関数:
上記の文の翻訳は、mysql_unbuffered_query() が SQL ステートメントを MySQL に送信し、mysql_query() とは異なることを意味します。自動フェッチとキャッシュされた結果の場合。これにより、特に多数の結果を生成するクエリの場合に、かなりのメモリが節約され、すべての結果が返されるまで待つ必要がなく、データの最初の行を返すだけで開始できます。クエリ結果はすぐに表示されます。
ただし、これにはいくつかの制限があります。すべての行を読み取るか、次のクエリの前に mysql_free_result() を呼び出して結果をクリアする必要があるためです。また、mysql_num_rows() または mysql_data_seek() は機能しません。したがって、バッファなしクエリを使用するかどうかを慎重に検討する必要があります。

17. IP アドレスを UNSIGNED INT として保存します

多くのプログラマは、IP を整数 IP の代わりに文字列形式で保存するために VARCHAR(15) フィールドを作成します。整数を使用して格納する場合、必要なバイト数は 4 バイトだけであり、固定長のフィールドを持つことができます。さらに、これは特に、ip1 と ip2 の間の IP のような WHERE 条件を使用する必要がある場合に、クエリに利点をもたらします。
IP アドレスは 32 ビットの符号なし整数全体を使用するため、UNSIGNED INT を使用する必要があります。
クエリでは、INET_ATON() を使用して文字列 IP を整数に変換し、INET_NTOA() を使用して整数を文字列 IP に変換できます。 PHP には、ip2long() および long2ip() という関数もあります。

18. 固定長テーブルは高速になります

テーブル内のすべてのフィールドが「固定長」の場合、テーブル全体が「静的」または「固定長」とみなされます。 たとえば、テーブルには VARCHAR、TEXT、BLOB 型のフィールドはありません。これらのフィールドのいずれかを含めている限り、テーブルは「固定長の静的テーブル」ではなくなり、MySQL エンジンは別の方法でテーブルを処理します。
固定長テーブルは MySQL の検索を高速化するため、パフォーマンスが向上します。これらの固定長により次のデータのオフセットの計算が容易になるため、当然読み取りも高速になります。また、フィールドが固定長でない場合、次のフィールドを検索するたびに、プログラムは主キーを検索する必要があります。
また、固定長テーブルはキャッシュと再構築が簡単です。ただし、唯一の副作用は、固定長フィールドは使用するかどうかに関係なく非常に多くのスペースを必要とするため、固定長フィールドによってスペースが浪費されることです。 PHP プログラマー ステーション
「垂直分割」テクノロジ (次の項目を参照) を使用すると、テーブルを 2 つに分割し、1 つは固定長、もう 1 つは可変長にすることができます。

19. 垂直分割

「垂直分割」は、データベース内のテーブルを列ごとに複数のテーブルに変換する方法で、テーブルの複雑さとフィールドの数を削減し、最適化を実現します。の目標。 (私は以前銀行でプロジェクトを行っていたのですが、100 を超えるフィールドがあるテーブルを見ました。これは怖かったです)
例 1: Users テーブルに自宅の住所であるフィールドがあります。このフィールドは、When you と比較するとオプションです。 データベースを操作する場合、個人情報を除き、このフィールドを頻繁に読み取ったり書き換えたりする必要はありません。では、別のテーブルに入れてみてはいかがでしょうか? これにより、テーブルが改善されます

関連する推奨事項:

MySQL 最適化の概要 - クエリの総数

以上がmysqlデータベースの最適化まとめの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
MySQL:世界で最も人気のあるデータベースの紹介MySQL:世界で最も人気のあるデータベースの紹介Apr 12, 2025 am 12:18 AM

MySQLはオープンソースのリレーショナルデータベース管理システムであり、主にデータを迅速かつ確実に保存および取得するために使用されます。その実用的な原則には、クライアントリクエスト、クエリ解像度、クエリの実行、返品結果が含まれます。使用法の例には、テーブルの作成、データの挿入とクエリ、および参加操作などの高度な機能が含まれます。一般的なエラーには、SQL構文、データ型、およびアクセス許可、および最適化の提案には、インデックスの使用、最適化されたクエリ、およびテーブルの分割が含まれます。

MySQLの重要性:データストレージと管理MySQLの重要性:データストレージと管理Apr 12, 2025 am 12:18 AM

MySQLは、データストレージ、管理、クエリ、セキュリティに適したオープンソースのリレーショナルデータベース管理システムです。 1.さまざまなオペレーティングシステムをサポートし、Webアプリケーションやその他のフィールドで広く使用されています。 2。クライアントサーバーアーキテクチャとさまざまなストレージエンジンを通じて、MySQLはデータを効率的に処理します。 3.基本的な使用には、データベースとテーブルの作成、挿入、クエリ、データの更新が含まれます。 4.高度な使用には、複雑なクエリとストアドプロシージャが含まれます。 5.一般的なエラーは、説明ステートメントを介してデバッグできます。 6.パフォーマンスの最適化には、インデックスの合理的な使用と最適化されたクエリステートメントが含まれます。

なぜMySQLを使用するのですか?利点と利点なぜMySQLを使用するのですか?利点と利点Apr 12, 2025 am 12:17 AM

MySQLは、そのパフォーマンス、信頼性、使いやすさ、コミュニティサポートに選択されています。 1.MYSQLは、複数のデータ型と高度なクエリ操作をサポートし、効率的なデータストレージおよび検索機能を提供します。 2.クライアントサーバーアーキテクチャと複数のストレージエンジンを採用して、トランザクションとクエリの最適化をサポートします。 3.使いやすく、さまざまなオペレーティングシステムとプログラミング言語をサポートしています。 4.強力なコミュニティサポートを提供し、豊富なリソースとソリューションを提供します。

InnoDBロックメカニズム(共有ロック、排他的ロック、意図ロック、レコードロック、ギャップロック、次のキーロック)を説明します。InnoDBロックメカニズム(共有ロック、排他的ロック、意図ロック、レコードロック、ギャップロック、次のキーロック)を説明します。Apr 12, 2025 am 12:16 AM

INNODBのロックメカニズムには、共有ロック、排他的ロック、意図ロック、レコードロック、ギャップロック、次のキーロックが含まれます。 1.共有ロックにより、トランザクションは他のトランザクションが読み取らないようにデータを読み取ることができます。 2.排他的ロックは、他のトランザクションがデータの読み取りと変更を防ぎます。 3.意図ロックは、ロック効率を最適化します。 4。ロックロックインデックスのレコードを記録します。 5。ギャップロックロックインデックス記録ギャップ。 6.次のキーロックは、データの一貫性を確保するためのレコードロックとギャップロックの組み合わせです。

貧弱なMySQLクエリパフォーマンスの一般的な原因は何ですか?貧弱なMySQLクエリパフォーマンスの一般的な原因は何ですか?Apr 12, 2025 am 12:11 AM

MySQLクエリのパフォーマンスが低いことの主な理由には、インデックスの使用、クエリオプティマイザーによる誤った実行計画の選択、不合理なテーブルデザイン、過剰なデータボリューム、ロック競争などがあります。 1.インデックスがゆっくりとクエリを引き起こし、インデックスを追加するとパフォーマンスが大幅に向上する可能性があります。 2。説明コマンドを使用してクエリ計画を分析し、オプティマイザーエラーを見つけます。 3.テーブル構造の再構築と結合条件を最適化すると、テーブルの設計上の問題が改善されます。 4.データボリュームが大きい場合、パーティション化とテーブル分割戦略が採用されます。 5.高い並行性環境では、トランザクションの最適化とロック戦略は、ロック競争を減らすことができます。

複数の単一列インデックスに対して複合インデックスをいつ使用する必要がありますか?複数の単一列インデックスに対して複合インデックスをいつ使用する必要がありますか?Apr 11, 2025 am 12:06 AM

データベースの最適化では、クエリ要件に従ってインデックス作成戦略を選択する必要があります。1。クエリに複数の列が含まれ、条件の順序が固定されている場合、複合インデックスを使用します。 2。クエリに複数の列が含まれているが、条件の順序が修正されていない場合、複数の単一列インデックスを使用します。複合インデックスは、マルチコラムクエリの最適化に適していますが、単一列インデックスは単一列クエリに適しています。

MySQLでスロークエリを識別して最適化する方法は? (スロークエリログ、Performance_schema)MySQLでスロークエリを識別して最適化する方法は? (スロークエリログ、Performance_schema)Apr 10, 2025 am 09:36 AM

MySQLスロークエリを最適化するには、slowquerylogとperformance_schemaを使用する必要があります。1。LowerQueryLogを有効にし、しきい値を設定して、スロークエリを記録します。 2。performance_schemaを使用してクエリの実行の詳細を分析し、パフォーマンスのボトルネックを見つけて最適化します。

MySQLおよびSQL:開発者にとって不可欠なスキルMySQLおよびSQL:開発者にとって不可欠なスキルApr 10, 2025 am 09:30 AM

MySQLとSQLは、開発者にとって不可欠なスキルです。 1.MYSQLはオープンソースのリレーショナルデータベース管理システムであり、SQLはデータベースの管理と操作に使用される標準言語です。 2.MYSQLは、効率的なデータストレージと検索機能を介して複数のストレージエンジンをサポートし、SQLは簡単なステートメントを通じて複雑なデータ操作を完了します。 3.使用の例には、条件によるフィルタリングやソートなどの基本的なクエリと高度なクエリが含まれます。 4.一般的なエラーには、SQLステートメントをチェックして説明コマンドを使用することで最適化できる構文エラーとパフォーマンスの問題が含まれます。 5.パフォーマンス最適化手法には、インデックスの使用、フルテーブルスキャンの回避、参加操作の最適化、コードの読み取り可能性の向上が含まれます。

See all articles

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

AI Hentai Generator

AI Hentai Generator

AIヘンタイを無料で生成します。

ホットツール

DVWA

DVWA

Damn Vulnerable Web App (DVWA) は、非常に脆弱な PHP/MySQL Web アプリケーションです。その主な目的は、セキュリティ専門家が法的環境でスキルとツールをテストするのに役立ち、Web 開発者が Web アプリケーションを保護するプロセスをより深く理解できるようにし、教師/生徒が教室環境で Web アプリケーションを教え/学習できるようにすることです。安全。 DVWA の目標は、シンプルでわかりやすいインターフェイスを通じて、さまざまな難易度で最も一般的な Web 脆弱性のいくつかを実践することです。このソフトウェアは、

MinGW - Minimalist GNU for Windows

MinGW - Minimalist GNU for Windows

このプロジェクトは osdn.net/projects/mingw に移行中です。引き続きそこでフォローしていただけます。 MinGW: GNU Compiler Collection (GCC) のネイティブ Windows ポートであり、ネイティブ Windows アプリケーションを構築するための自由に配布可能なインポート ライブラリとヘッダー ファイルであり、C99 機能をサポートする MSVC ランタイムの拡張機能が含まれています。すべての MinGW ソフトウェアは 64 ビット Windows プラットフォームで実行できます。

Safe Exam Browser

Safe Exam Browser

Safe Exam Browser は、オンライン試験を安全に受験するための安全なブラウザ環境です。このソフトウェアは、あらゆるコンピュータを安全なワークステーションに変えます。あらゆるユーティリティへのアクセスを制御し、学生が無許可のリソースを使用するのを防ぎます。

AtomエディタMac版ダウンロード

AtomエディタMac版ダウンロード

最も人気のあるオープンソースエディター

mPDF

mPDF

mPDF は、UTF-8 でエンコードされた HTML から PDF ファイルを生成できる PHP ライブラリです。オリジナルの作者である Ian Back は、Web サイトから「オンザフライ」で PDF ファイルを出力し、さまざまな言語を処理するために mPDF を作成しました。 HTML2FPDF などのオリジナルのスクリプトよりも遅く、Unicode フォントを使用すると生成されるファイルが大きくなりますが、CSS スタイルなどをサポートし、多くの機能強化が施されています。 RTL (アラビア語とヘブライ語) や CJK (中国語、日本語、韓国語) を含むほぼすべての言語をサポートします。ネストされたブロックレベル要素 (P、DIV など) をサポートします。