1. 总体概述
1.1 三个总结
网上看到三个总结过的图片,很好,先分享如下:
1.2 mysql常用优化技术
对mysql优化是一个综合性的技术,主要包括
- 表的设计合理化(符合3NF)
-
- 三范式:
第一范式(1NF):符合1NF的关系中的每个属性都不可再分;—>1NF是所有关系型数据库的最基本要求.
第二范式:(2NF):2NF在1NF的基础之上,消除了非主属性对于码的部分函数依赖;—>表中的记录是唯一的, 就满足2NF, 通常我们设计一个主键来实现
第三范式(3NF):3NF在2NF的基础之上,消除了非主属性对于码的传递函数依赖;—>即表中不要有冗余数据, 就是说,表的信息,如果能够被推导出来,就不应该单独的设计一个字段来存放.
冗余保存:反三范式的数据库设计方式. 比如机票的订单中保存了机票的基本全部信息(在有行程PNR乘机人单独表的同时)
- 三范式:
- 添加适当索引(index) [四种: 普通索引、主键索引、唯一索引unique、全文索引]
- 分表技术(水平分割、垂直分割)
- 读(select)写[写: update/delete/insert]分离
- 存储过程 [模块化编程,可以提高速度]
- 对mysql配置优化 [配置最大并发数my.ini(max_connections,一般1000,太多占内存), 调整缓存大小,]
- mysql服务器硬件升级
- 定时的去清除不需要的数据,定时进行碎片整理(MyISAM)
1.3 常用技巧
1.3.1 查看慢sql
1.3.1.1 查看mysql运行状态
了解mysql运行状态如何查询(如当前mysql运行的时间/一共执行了多少次select/update/delete… / 当前连接)
show status 查看mysql的配置参数.
show status like ‘uptime’ ; //查询当前mysql启动时间.
show stauts like ‘com_select’ //多少次查询
show stauts like ‘com_insert’ //多少次插入
show stauts like ‘com_update’ //多少次修改
show stauts like ‘com_delete’ //多少次删除
show status like ‘connections’; //连接数目
//统计信息默默认是session会话,指取出当前窗口的执行,可以通过[session|global]修改;global:指所有(从mysql启动到现在)
show [session|global] status like ....
1.3.1.2 定位慢查询
long_query_time: 慢查询界定时间长度
slow_queries: 慢查询的次数
datadir: 记录慢查询的信息的路径
//配置参数-可以显示当前慢查询判定时间(查询时间大于多少才算慢,默认10秒)
show variables like ‘long_query_time’ ;
//配置参数-以修改慢查询判定时间 (单位秒)
set long_query_time=1 ;
show status like ‘slow_queries’; //显示慢查询次数,包括查询和插入
//记录慢查询的日志(默认不启用,以下启动方式会启用记录慢查询的日志)
bin\mysqld.exe - -safe-mode - -slow-query-log [mysql5.5 可以在my.ini指定]
bin\mysqld.exe –log-slow-queries=d:/abc.log [低版本mysql5.0可以在my.ini指定]
日志记录的路径: datadir
# Path to the database root
datadir=C:/ProgramData/MySQL/MySQL Server 5.7\Data
那就是如果有sql执行时间超过long_query_time时间的,然后该sql信息会保存到datadir目录下.
2. 系统变量
2.1 常用
//列出mysql参数
show status;
//重点关注如下的参数
Queries; //dangq发生了几次查询
Threads_connected; //几个线程连接
Threads_running; //当前几个线程工作中
2.2 show status
2.2.1 show status 查看结果
mysql> show status;
+-----------------------------------------------+--------------------------------------------------+
| Variable_name | Value |
+-----------------------------------------------+--------------------------------------------------+
| Aborted_clients | 4 |
| Aborted_connects | 4 |
| Binlog_cache_disk_use | 0 |
| Binlog_cache_use | 0 |
| Binlog_stmt_cache_disk_use | 0 |
| Binlog_stmt_cache_use | 0 |
| Bytes_received | 240 |
| Bytes_sent | 181 |
| Com_admin_commands | 0 |
| Com_assign_to_keycache | 0 |
| Com_alter_db | 0 |
| Com_alter_db_upgrade | 0 |
| Com_alter_event | 0 |
| Com_alter_function | 0 |
| Com_alter_instance | 0 |
| Com_alter_procedure | 0 |
| Com_alter_server | 0 |
| Com_alter_table | 0 |
| Com_alter_tablespace | 0 |
| Com_alter_user | 0 |
| Com_analyze | 0 |
| Com_begin | 0 |
| Com_binlog | 0 |
| Com_call_procedure | 0 |
| Com_change_db | 0 |
| Com_change_master | 0 |
| Com_change_repl_filter | 0 |
| Com_check | 0 |
| Com_checksum | 0 |
| Com_commit | 0 |
| Com_create_db | 0 |
| Com_create_event | 0 |
| Com_create_function | 0 |
| Com_create_index | 0 |
| Com_create_procedure | 0 |
| Com_create_server | 0 |
| Com_create_table | 0 |
| Com_create_trigger | 0 |
| Com_create_udf | 0 |
| Com_create_user | 0 |
| Com_create_view | 0 |
| Com_dealloc_sql | 0 |
| Com_delete | 0 |
| Com_delete_multi | 0 |
| Com_do | 0 |
| Com_drop_db | 0 |
| Com_drop_event | 0 |
| Com_drop_function | 0 |
| Com_drop_index | 0 |
| Com_drop_procedure | 0 |
| Com_drop_server | 0 |
| Com_drop_table | 0 |
| Com_drop_trigger | 0 |
| Com_drop_user | 0 |
| Com_drop_view | 0 |
| Com_empty_query | 0 |
| Com_execute_sql | 0 |
| Com_explain_other | 0 |
| Com_flush | 0 |
| Com_get_diagnostics | 0 |
| Com_grant | 0 |
| Com_ha_close | 0 |
| Com_ha_open | 0 |
| Com_ha_read | 0 |
| Com_help | 0 |
| Com_insert | 0 |
| Com_insert_select | 0 |
| Com_install_plugin | 0 |
| Com_kill | 0 |
| Com_load | 0 |
| Com_lock_tables | 0 |
| Com_optimize | 0 |
| Com_preload_keys | 0 |
| Com_prepare_sql | 0 |
| Com_purge | 0 |
| Com_purge_before_date | 0 |
| Com_release_savepoint | 0 |
| Com_rename_table | 0 |
| Com_rename_user | 0 |
| Com_repair | 0 |
| Com_replace | 0 |
| Com_replace_select | 0 |
| Com_reset | 0 |
| Com_resignal | 0 |
| Com_revoke | 0 |
| Com_revoke_all | 0 |
| Com_rollback | 0 |
| Com_rollback_to_savepoint | 0 |
| Com_savepoint | 0 |
| Com_select | 1 |
| Com_set_option | 0 |
| Com_signal | 0 |
| Com_show_binlog_events | 0 |
| Com_show_binlogs | 0 |
| Com_show_charsets | 0 |
| Com_show_collations | 0 |
| Com_show_create_db | 0 |
| Com_show_create_event | 0 |
| Com_show_create_func | 0 |
| Com_show_create_proc | 0 |
| Com_show_create_table | 0 |
| Com_show_create_trigger | 0 |
| Com_show_databases | 0 |
| Com_show_engine_logs | 0 |
| Com_show_engine_mutex | 0 |
| Com_show_engine_status | 0 |
| Com_show_events | 0 |
| Com_show_errors | 0 |
| Com_show_fields | 0 |
| Com_show_function_code | 0 |
| Com_show_function_status | 0 |
| Com_show_grants | 0 |
| Com_show_keys | 0 |
| Com_show_master_status | 0 |
| Com_show_open_tables | 0 |
| Com_show_plugins | 0 |
| Com_show_privileges | 0 |
| Com_show_procedure_code | 0 |
| Com_show_procedure_status | 0 |
| Com_show_processlist | 0 |
| Com_show_profile | 0 |
| Com_show_profiles | 0 |
| Com_show_relaylog_events | 0 |
| Com_show_slave_hosts | 0 |
| Com_show_slave_status | 0 |
| Com_show_status | 1 |
| Com_show_storage_engines | 0 |
| Com_show_table_status | 0 |
| Com_show_tables | 0 |
| Com_show_triggers | 0 |
| Com_show_variables | 0 |
| Com_show_warnings | 0 |
| Com_show_create_user | 0 |
| Com_shutdown | 0 |
| Com_slave_start | 0 |
| Com_slave_stop | 0 |
| Com_group_replication_start | 0 |
| Com_group_replication_stop | 0 |
| Com_stmt_execute | 0 |
| Com_stmt_close | 0 |
| Com_stmt_fetch | 0 |
| Com_stmt_prepare | 0 |
| Com_stmt_reset | 0 |
| Com_stmt_send_long_data | 0 |
| Com_truncate | 0 |
| Com_uninstall_plugin | 0 |
| Com_unlock_tables | 0 |
| Com_update | 0 |
| Com_update_multi | 0 |
| Com_xa_commit | 0 |
| Com_xa_end | 0 |
| Com_xa_prepare | 0 |
| Com_xa_recover | 0 |
| Com_xa_rollback | 0 |
| Com_xa_start | 0 |
| Com_stmt_reprepare | 0 |
| Compression | OFF |
| Connection_errors_accept | 0 |
| Connection_errors_internal | 0 |
| Connection_errors_max_connections | 0 |
| Connection_errors_peer_address | 0 |
| Connection_errors_select | 0 |
| Connection_errors_tcpwrap | 0 |
| Connections | 23 |
| Created_tmp_disk_tables | 0 |
| Created_tmp_files | 8 |
| Created_tmp_tables | 0 |
| Delayed_errors | 0 |
| Delayed_insert_threads | 0 |
| Delayed_writes | 0 |
| Flush_commands | 1 |
| Handler_commit | 0 |
| Handler_delete | 0 |
| Handler_discover | 0 |
| Handler_external_lock | 0 |
| Handler_mrr_init | 0 |
| Handler_prepare | 0 |
| Handler_read_first | 0 |
| Handler_read_key | 0 |
| Handler_read_last | 0 |
| Handler_read_next | 0 |
| Handler_read_prev | 0 |
| Handler_read_rnd | 0 |
| Handler_read_rnd_next | 0 |
| Handler_rollback | 0 |
| Handler_savepoint | 0 |
| Handler_savepoint_rollback | 0 |
| Handler_update | 0 |
| Handler_write | 0 |
| Innodb_buffer_pool_dump_status | Dumping of buffer pool not started |
| Innodb_buffer_pool_load_status | Buffer pool(s) load completed at 171122 17:27:29 |
| Innodb_buffer_pool_resize_status | |
| Innodb_buffer_pool_pages_data | 934 |
| Innodb_buffer_pool_bytes_data | 15302656 |
| Innodb_buffer_pool_pages_dirty | 0 |
| Innodb_buffer_pool_bytes_dirty | 0 |
| Innodb_buffer_pool_pages_flushed | 134 |
| Innodb_buffer_pool_pages_free | 7256 |
| Innodb_buffer_pool_pages_misc | 1 |
| Innodb_buffer_pool_pages_total | 8191 |
| Innodb_buffer_pool_read_ahead_rnd | 0 |
| Innodb_buffer_pool_read_ahead | 0 |
| Innodb_buffer_pool_read_ahead_evicted | 0 |
| Innodb_buffer_pool_read_requests | 11893 |
| Innodb_buffer_pool_reads | 883 |
| Innodb_buffer_pool_wait_free | 0 |
| Innodb_buffer_pool_write_requests | 3542 |
| Innodb_data_fsyncs | 7 |
| Innodb_data_pending_fsyncs | 0 |
| Innodb_data_pending_reads | 0 |
| Innodb_data_pending_writes | 0 |
| Innodb_data_read | 14537216 |
| Innodb_data_reads | 953 |
| Innodb_data_writes | 151 |
| Innodb_data_written | 2230272 |
| Innodb_dblwr_pages_written | 2 |
| Innodb_dblwr_writes | 1 |
| Innodb_log_waits | 0 |
| Innodb_log_write_requests | 0 |
| Innodb_log_writes | 2 |
| Innodb_os_log_fsyncs | 4 |
| Innodb_os_log_pending_fsyncs | 0 |
| Innodb_os_log_pending_writes | 0 |
| Innodb_os_log_written | 1024 |
| Innodb_page_size | 16384 |
| Innodb_pages_created | 52 |
| Innodb_pages_read | 882 |
| Innodb_pages_written | 134 |
| Innodb_row_lock_current_waits | 0 |
| Innodb_row_lock_time | 0 |
| Innodb_row_lock_time_avg | 0 |
| Innodb_row_lock_time_max | 0 |
| Innodb_row_lock_waits | 0 |
| Innodb_rows_deleted | 0 |
| Innodb_rows_inserted | 1069 |
| Innodb_rows_read | 3573 |
| Innodb_rows_updated | 0 |
| Innodb_num_open_files | 62 |
| Innodb_truncated_status_writes | 0 |
| Innodb_available_undo_logs | 128 |
| Key_blocks_not_flushed | 0 |
| Key_blocks_unused | 6693 |
| Key_blocks_used | 5 |
| Key_read_requests | 210 |
| Key_reads | 5 |
| Key_write_requests | 0 |
| Key_writes | 0 |
| Last_query_cost | 0.000000 |
| Last_query_partial_plans | 0 |
| Locked_connects | 0 |
| Max_execution_time_exceeded | 0 |
| Max_execution_time_set | 0 |
| Max_execution_time_set_failed | 0 |
| Max_used_connections | 3 |
| Max_used_connections_time | 2017-11-22 17:28:55 |
| Not_flushed_delayed_rows | 0 |
| Ongoing_anonymous_transaction_count | 0 |
| Open_files | 40 |
| Open_streams | 0 |
| Open_table_definitions | 258 |
| Open_tables | 392 |
| Opened_files | 1517 |
| Opened_table_definitions | 0 |
| Opened_tables | 0 |
| Performance_schema_accounts_lost | 0 |
| Performance_schema_cond_classes_lost | 0 |
| Performance_schema_cond_instances_lost | 0 |
| Performance_schema_digest_lost | 0 |
| Performance_schema_file_classes_lost | 0 |
| Performance_schema_file_handles_lost | 0 |
| Performance_schema_file_instances_lost | 0 |
| Performance_schema_hosts_lost | 0 |
| Performance_schema_index_stat_lost | 0 |
| Performance_schema_locker_lost | 0 |
| Performance_schema_memory_classes_lost | 0 |
| Performance_schema_metadata_lock_lost | 0 |
| Performance_schema_mutex_classes_lost | 0 |
| Performance_schema_mutex_instances_lost | 0 |
| Performance_schema_nested_statement_lost | 0 |
| Performance_schema_prepared_statements_lost | 0 |
| Performance_schema_program_lost | 0 |
| Performance_schema_rwlock_classes_lost | 0 |
| Performance_schema_rwlock_instances_lost | 0 |
| Performance_schema_session_connect_attrs_lost | 0 |
| Performance_schema_socket_classes_lost | 0 |
| Performance_schema_socket_instances_lost | 0 |
| Performance_schema_stage_classes_lost | 0 |
| Performance_schema_statement_classes_lost | 0 |
| Performance_schema_table_handles_lost | 0 |
| Performance_schema_table_instances_lost | 0 |
| Performance_schema_table_lock_stat_lost | 0 |
| Performance_schema_thread_classes_lost | 0 |
| Performance_schema_thread_instances_lost | 0 |
| Performance_schema_users_lost | 0 |
| Prepared_stmt_count | 0 |
| Qcache_free_blocks | 1 |
| Qcache_free_memory | 1031832 |
| Qcache_hits | 0 |
| Qcache_inserts | 0 |
| Qcache_lowmem_prunes | 0 |
| Qcache_not_cached | 68 |
| Qcache_queries_in_cache | 0 |
| Qcache_total_blocks | 1 |
| Queries | 947 |
| Questions | 2 |
| Select_full_join | 0 |
| Select_full_range_join | 0 |
| Select_range | 0 |
| Select_range_check | 0 |
| Select_scan | 0 |
| Slave_open_temp_tables | 0 |
| Slow_launch_threads | 0 |
| Slow_queries | 0 |
| Sort_merge_passes | 0 |
| Sort_range | 0 |
| Sort_rows | 0 |
| Sort_scan | 0 |
| Ssl_accept_renegotiates | 0 |
| Ssl_accepts | 0 |
| Ssl_callback_cache_hits | 0 |
| Ssl_cipher | |
| Ssl_cipher_list | |
| Ssl_client_connects | 0 |
| Ssl_connect_renegotiates | 0 |
| Ssl_ctx_verify_depth | 0 |
| Ssl_ctx_verify_mode | 0 |
| Ssl_default_timeout | 0 |
| Ssl_finished_accepts | 0 |
| Ssl_finished_connects | 0 |
| Ssl_server_not_after | |
| Ssl_server_not_before | |
| Ssl_session_cache_hits | 0 |
| Ssl_session_cache_misses | 0 |
| Ssl_session_cache_mode | NONE |
| Ssl_session_cache_overflows | 0 |
| Ssl_session_cache_size | 0 |
| Ssl_session_cache_timeouts | 0 |
| Ssl_sessions_reused | 0 |
| Ssl_used_session_cache_entries | 0 |
| Ssl_verify_depth | 0 |
| Ssl_verify_mode | 0 |
| Ssl_version | |
| Table_locks_immediate | 212 |
| Table_locks_waited | 0 |
| Table_open_cache_hits | 0 |
| Table_open_cache_misses | 0 |
| Table_open_cache_overflows | 0 |
| Tc_log_max_pages_used | 0 |
| Tc_log_page_size | 0 |
| Tc_log_page_waits | 0 |
| Threads_cached | 0 |
| Threads_connected | 3 |
| Threads_created | 3 |
| Threads_running | 1 |
| Uptime | 434003 |
| Uptime_since_flush_status | 434003 |
+-----------------------------------------------+--------------------------------------------------+
356 rows in set (0.00 sec)
mysql>
2.2.2 show status 部分详解
Aborted_clients 由于客户没有正确关闭连接已经死掉,已经放弃的连接数量。
Aborted_connects 尝试已经失败的MySQL服务器的连接的次数。
Connections 试图连接MySQL服务器的次数。
Created_tmp_tables 当执行语句时,已经被创造了的隐含临时表的数量。
Delayed_insert_threads 正在使用的延迟插入处理器线程的数量。
Delayed_writes 用INSERT DELAYED写入的行数。
Delayed_errors 用INSERT DELAYED写入的发生某些错误(可能重复键值)的行数。
Flush_commands 执行FLUSH命令的次数。
Handler_delete 请求从一张表中删除行的次数。
Handler_read_first 请求读入表中第一行的次数。
Handler_read_key 请求数字基于键读行。
Handler_read_next 请求读入基于一个键的一行的次数。
Handler_read_rnd 请求读入基于一个固定位置的一行的次数。
Handler_update 请求更新表中一行的次数。
Handler_write 请求向表中插入一行的次数。
Key_blocks_used 用于关键字缓存的块的数量。
Key_read_requests 请求从缓存读入一个键值的次数。
Key_reads 从磁盘物理读入一个键值的次数。
Key_write_requests 请求将一个关键字块写入缓存次数。
Key_writes 将一个键值块物理写入磁盘的次数。
Max_used_connections 同时使用的连接的最大数目。
Not_flushed_key_blocks 在键缓存中已经改变但是还没被清空到磁盘上的键块。
Not_flushed_delayed_rows 在INSERT DELAY队列中等待写入的行的数量。
Open_tables 打开表的数量。
Open_files 打开文件的数量。
Open_streams 打开流的数量(主要用于日志记载)
Opened_tables 已经打开的表的数量。
Questions 发往服务器的查询的数量。
Slow_queries 要花超过long_query_time时间的查询数量。
Threads_connected 当前打开的连接的数量。
Threads_running 不在睡眠的线程数量。
Uptime 服务器工作了多少秒。
3. processlist和profiles
3.1 show processlist
3.1.1 显示msyql正在运行线程
mysql> show processlist\G
*************************** 1. row ***************************
Id: 2
User: root
Host: localhost:54495
db: NULL
Command: Query
Time: 0
State: starting
Info: show processlist
1 row in set (0.00 sec)
mysql>
3.1.2 定义及注意
如果是root帐号,你能看到所有用户的当前连接。如果是其它普通帐号,只能看到自己占用的连接。
show processlist;只列出前100条,如果想全列出请使用show full processlist;
本语句报告TCP/IP连接的主机名称(采用host_name:client_port格式),以方便地判定哪个客户端正在做什么。
如果您得到“too many connections”错误信息,并且想要了解正在发生的情况,本语句是非常有用的。MySQL保留一个额外的连接,让拥有SUPER权限的 账户使用,以确保管理员能够随时连接和检查系统(假设您没有把此权限给予所有的用户)。
3.1.3 processlist的State类型
Checking table 正在检查数据表(这是自动的)。
Closing tables 正在将表中修改的数据刷新到磁盘中,同时正在关闭已经用完的表。这是一个很快的操作,如果不是这样的话,就应该确认磁盘空间是否已经满了或者磁盘是否正处于重负中。
Connect Out 复制从服务器正在连接主服务器。
Copying to tmp table on disk 由于临时结果集大于tmp_table_size,正在将临时表从内存存储转为磁盘存储以此节省内存。
Creating tmp table 正在创建临时表以存放部分查询结果。
deleting from main table 服务器正在执行多表删除中的第一部分,刚删除第一个表。
deleting from reference tables 服务器正在执行多表删除中的第二部分,正在删除其他表的记录。
Flushing tables 正在执行FLUSH TABLES,等待其他线程关闭数据表。
Killed 发送了一个kill请求给某线程,那么这个线程将会检查kill标志位,同时会放弃下一个kill请求。MySQL会在每次的主循环中检查kill标志位,不过有些情况下该线程可能会过一小段才能死掉。如果该线程程被其他线程锁住了,那么kill请求会在锁释放时马上生效。
***Locked 被其他查询锁住了。
Sending data 正在处理SELECT查询的记录,同时正在把结果发送给客户端。
Sorting for group 正在为GROUP BY做排序。
Sorting for order 正在为ORDER BY做排序。
Opening tables 这个过程应该会很快,除非受到其他因素的干扰。例如,在执ALTER TABLE或LOCK TABLE语句行完以前,数据表无法被其他线程打开。正尝试打开一个表。
Removing duplicates 正在执行一个SELECT DISTINCT方式的查询,但是MySQL无法在前一个阶段优化掉那些重复的记录。因此,MySQL需要再次去掉重复的记录,然后再把结果发送给客户端。
Reopen table 获得了对一个表的锁,但是必须在表结构修改之后才能获得这个锁。已经释放锁,关闭数据表,正尝试重新打开数据表。
Repair by sorting 修复指令正在排序以创建索引。
Repair with keycache 修复指令正在利用索引缓存一个一个地创建新索引。它会比Repair by sorting慢些。
Searching rows for update 正在讲符合条件的记录找出来以备更新。它必须在UPDATE要修改相关的记录之前就完成了。
Sleeping 正在等待客户端发送新请求.
System lock
正在等待取得一个外部的系统锁。如果当前没有运行多个mysqld服务器同时请求同一个表,那么可以通过增加–skip-external-locking参数来禁止外部系统锁。
Upgrading lock INSERT DELAYED正在尝试取得一个锁表以插入新记录。
Updating 正在搜索匹配的记录,并且修改它们。
User Lock 正在等待GET_LOCK()。
Waiting for tables 该线程得到通知,数据表结构已经被修改了,需要重新打开数据表以取得新的结构。然后,为了能的重新打开数据表,必须等到所有其他线程关闭这个表。以下几种情况下会产生这个通知:FLUSH TABLES tbl_name, ALTER TABLE, RENAME TABLE, REPAIR TABLE, ANALYZE TABLE,或OPTIMIZE TABLE。
waiting for handler insert INSERT DELAYED已经处理完了所有待处理的插入操作,正在等待新的请求。
大部分状态对应很快的操作,只要有一个线程保持同一个状态好几秒钟,那么可能是有问题发生了,需要检查一下。
还有其他的状态没在上面中列出来,不过它们大部分只是在查看服务器是否有存在错误是才用得着。
3.1.4 周期查看执行线程
脚本内容
#!/bin/bash
while true
do
mysql -uroot -e 'show processlist\G'|grep State:|uniq -c|sort -rn
echo '---'
sleep 1
Done
3.1.5 注意状态可疑危险
converting HEAP to MyISAM 查询结果太大时,把结果放在磁盘
create tmp table 创建临时表(如group时储存中间结果)
Copying to tmp table on disk 把内存临时表复制到磁盘
locked 被其他查询锁住
logging slow query 记录慢查询
3.1.6 可疑状态解析
3.1.6.1 什么情况下产生临时表(create tmp table)?
- 1: group by 的列和order by 的列不同时, 2表边查时,取A表的内容,group/order by另外表的列
- 2: distinct 和 order by 一起使用时
- 3: 开启了 SQL_SMALL_RESULT 选项
3.1.6.2 什么情况下临时表写到磁盘上(converting HEAP to MyISAM)?
-
1 ) sql有问题,取出的结果或中间结果过大,内存临时表放不下:
-
- 1.1) 取出的列含有text/blob类型时 —内存表储存不了text/blob类型
-
- 1.2) 在group by 或distinct的列中存在>512字节的string列
-
- 1.3) select 中含有>512字节的string列,同时又使用了union或union all语句
-
2 ) 服务器配置的临时表内存参数过小.
tmp_table_size 和 max_heap_table_size
3.2 profiles
3.2.1 常用命令
show variables like '%profiling%'; //查看配置是否开启
show profiles; //显示profile分析记录
set profiling =1; //设置开启profiling
show profiles for query 2; //对上个语句(show profiles的结果中的第二条)看查询过程
//注意: 需要开启profiling=on才记录执行的sql的分析记录
3.2.2 操作命令举例
mysql> show variables like 'profil%';
+------------------------+-------+
| Variable_name | Value |
+------------------------+-------+
| profiling | OFF |profile开关
| profiling_history_size | 15 |profile缓存条数
+------------------------+-------+
2 rows in set, 1 warning (0.00 sec)
mysql> use bobshutetest;
Database changed
//查看profile分析记录(当前关闭)
mysql> show profiles;
Empty set, 1 warning (0.00 sec)
//开启profiles分析工具
mysql> set profiling =1;
Query OK, 0 rows affected, 1 warning (0.00 sec)
//执行一个sql-显示表
mysql> show tables;
+------------------------+
| Tables_in_bobshutetest |
+------------------------+
| test_table |
| viewa |
+------------------------+
2 rows in set (0.00 sec)
//执行一个sql,查询表中书
mysql> select * from test_table;
+----+------+-----------+
| id | name | score_new |
+----+------+-----------+
| 2 | b | 00022 |
| 3 | a | 444444444 |
| 4 | b | 00022 |
| 5 | a | 444444444 |
| 6 | b | 00022 |
+----+------+-----------+
5 rows in set (0.00 sec)
//再次查看profile分析记录(当前开启)
mysql> show profiles;
+----------+------------+--------------------------+
| Query_ID | Duration | Query |
+----------+------------+--------------------------+
| 1 | 0.00069125 | show tables |
| 2 | 0.00754650 | select * from test_table |
+----------+------------+--------------------------+
2 rows in set, 1 warning (0.00 sec)
//查看profile分析中的第二条
mysql> show profile for query 2;
+----------------------+----------+
| Status | Duration |
+----------------------+----------+
| starting | 0.000052 |
| checking permissions | 0.000004 |
| Opening tables | 0.007345 |
| init | 0.000018 |
| System lock | 0.000006 |
| optimizing | 0.000003 |
| statistics | 0.000008 |
| preparing | 0.000007 |
| executing | 0.000002 |
| Sending data | 0.000037 |
| end | 0.000002 |
| query end | 0.000004 |
| closing tables | 0.000004 |
| freeing items | 0.000050 |
| cleaning up | 0.000009 |
+----------------------+----------+
15 rows in set, 1 warning (0.00 sec)
4. explian
4.1 开启慢查询
//列出查询先关配置
mysql> show variables like '%quer%';
+----------------------------------------+-------------------+
| Variable_name | Value |
+----------------------------------------+-------------------+
| binlog_rows_query_log_events | OFF |
| ft_query_expansion_limit | 20 |
| have_query_cache | YES |
| log_queries_not_using_indexes | OFF |
| log_throttle_queries_not_using_indexes | 0 |
| long_query_time | 10.000000 |重点
| query_alloc_block_size | 8192 |
| query_cache_limit | 1048576 |
| query_cache_min_res_unit | 4096 |
| query_cache_size | 0 |
| query_cache_type | OFF |
| query_cache_wlock_invalidate | OFF |
| query_prealloc_size | 8192 |
| slow_query_log | ON |重点
| slow_query_log_file | SHUBB-PC-slow.log |重点
+----------------------------------------+-------------------+
15 rows in set, 1 warning (0.01 sec)
explain 显示了mysql如何使用索引来处理select语句以及连接表。可以帮助选择更好的索引和写出更优化的查询语句
explain 可以帮助我们在不真正执行某个sql语句时,就执行mysql怎样执行,这样利用我们去分析sql指令.
4.1 explain详解
explain能详细告诉你 Mysql 打算如何、执行你这个Query.
- id SELECT识别符。这是SELECT的查询序列号
- select_type 查询类型
- SIMPLE: 简单模式,优化到常量/系统级别,很快,不包含子查询
- PRIMARY :子查询中最外层查询(含有子查询或派生查询 )
- SUBQUERY : 子查询内层第一个SELECT,结果不依赖于外部查询(非from查询)
- DEPENDENT SUBQUERY:子查询内层第一个SELECT,依赖于外部查询
- derived :派生, from子查询
- UNION :UNION语句中第二个SELECT开始后面所有SELECT,
- UNION RESULT : UNION 中合并结果
-
table 表名
- 实际的表名: 如select * from t1;
- 表的别名: 如 select * from t2 as tmp;
- derived: 如from型子查询时
- null: 直接计算得结果,不用走表
-
type 对表访问方式,是指查询的方式, 非常重要,是分析”查数据过程”的重要依据
- ALL 完整的表扫描 通常不好. 如:SELECT * FROM emp \G
- index 比all性能稍好一点,也不够好.
通俗的说: all 扫描所有的数据行,相当于data-all,index 扫描所有的索引节点,相当于index-all - range :意思是查询时,能根据索引做范围的扫描,比如> <或 between.
- ref:const : 意思是指 通过索引列,可以直接引用到某些数据行
- eq_ref: 直接定位到某一行.
- system 表仅有一行(=系统表)。这是const联接类型的一个特例,最高级
SELECT * FROM (SELECT * FROM emp WHERE empno = 3) a ; - const :常数,效率最高, 表最多有一个匹配行,最高级
- null : 个表达式,不经过表时, 出现NULL.最高级
-
Possible_keys 该查询可以利用的索引,如果没有任何索引显示null
注意: 系统估计可能用的几个索引,但最终,只能用1个. -
Key Mysql 从 Possible_keys 所选择使用索引,最终用的索引.
-
Key_len 被选中使用索引的索引长度
一般按照主键来查询时,易出现const,system,或者直接查询某个表达式,不经过表时, 出现NULL.
```
key_len 大小的计算规则是:
一般地,key_len 等于索引列类型字节长度,例如int类型为4 bytes,bigint为8 bytes;
如果是字符串类型,还需要同时考虑字符集因素,例如:CHAR(30) UTF8则key_len至少是90 bytes;
若该列类型定义时允许NULL,其key_len还需要再加 1 bytes;
若该列类型为变长类型,例如 VARCHAR(TEXT\BLOB不允许整列创建索引,如果创建部分索引也被视为动态列类型),其key_len还需要再加 2 bytes;
例1:
`name` char(20) NOT NULL DEFAULT '',char(20),且非空。
首先我的表用的utf8字符集,那么大家都知道utf8字符集占用3个字节,那么我又定义char(20),
key_len=20*3=60
例2:
`name` char(20) NOT NULL DEFAULT ''; name char(20),且非空。
`name1` char(20) DEFAULT NULL; name1,char(20),且非空。
联合索引: add key idx_key_name_name1 (name,name1);
key_len=20 * 3 + (20 * 3 +1)=121(name1字段的定义为DEFAULT NULL,所以MySQL需要1个字节来标识NULL)
varchr(10)变长字段且允许NULL = 10 * ( character set:utf8=3,gbk=2,latin1=1)+1(NULL)+2(变长字段)
varchr(10)变长字段且不允许NULL = 10 *( character set:utf8=3,gbk=2,latin1=1)+2(变长字段)
char(10)固定字段且允许NULL = 10 * ( character set:utf8=3,gbk=2,latin1=1)+1(NULL)
char(10)固定字段且不允许NULL = 10 * ( character set:utf8=3,gbk=2,latin1=1)
```
- ref : 连接查询时,前表与后表的引用关系.分析的是多表的时候.
- rows 显示MySQL认为它执行查询时必须检查的行数(估算出结果集行数)
- extra 查询细节信息
- using index 是指用到了索引覆盖,效率非常高
- Using where 不用读取表中所有信息,仅通过索引就可以获取所需数据;
- No tables
- Query语句中使用FROM DUAL 或不含任何FROM子句
- Using filesort 当Query中包含 ORDER BY 操作,而且无法利用索引完成排序.不好.
- Impossible WHERE noticed after reading const tables: MYSQL Query Optimizer
通过收集统计信息不可能存在结果 - Using temporary 某些操作必须使用临时表,常见 GROUP BY ; ORDER BY
注意
- explain一般不产生查询,偶尔也会. 注: 版本5.6 之后确实不产生查询了
- explain 不是即执行结果. 只是可能的执行过程.
- 5.6版本之前只支持select语句. 在5.6 版本之后也支持update,insert,delete.
- 对limit的解释.错误,不支持对limit的解释.
- 如果是多条查询出现多个表的执行计划,则是按照编号顺序来执行的.
举例说明
2个表一个商品类型(goods_type)表,一个商品表(goods)
select * from goods t where t.goods_type in (select t2.type_id from goods_type t2 where t2.id='1')
这样的执行过程是先查出goods中的全部的数据一条一条的将good_type拿出来(explian的type为all),然后到goods_tppe中比较(速度很慢).即先t表,然后t2表.
修改为如下:
select * from goods t inner join (select t2.type_id from goods_type t2 where t2.id='1') as t3 on t.goods_type = t3.type_id
执行效率提升很大.
4.2 Query优化原则
- 优化需要优化的Query
- 定位优化对象性能瓶颈
- 从Explain入手
- 永远小结果集驱动大结果集
- 尽可能在索引中完成排序
- 只取自己需要的Column
- 仅仅使用最有效的过滤条件
- 尽可能避免复杂的join和子查询
- 只一行数据使用limit 1
参考网页:
http://blog.csdn.net/zhuxineli/article/details/14455029
5. 索引基本
5.1 基本概念
索引类型:主键索引,唯一索引,全文索引,普通索引.
5.2 添加索引
5.2.1 主键索引
1)创建表的时候一起创建,primary key就是主键.
2)alter table tablename
5.2.2 普通索引
create index 索引名 on 表 (列1,列名2); 默认b_tree(binary_tree二叉树)
二叉树 效率 log2N,也就是若果检索10次,得到检索2^10=1024条数据.
create index idx_article_name on article(name);//普通索引
Alter table tb add primary key(id); //创建主键
Alter table tb change id id int(10) not null auto_increment=1;//设置主键自增长
先删除自增长在删除主键
Alter table tb change id id int(10);//删除自增长
Alter table tb drop primary key;//删除主建
5.2.3 全文索引
全文所以主要针对文字,文本,比如文章.真针对 MyISAM有效(engine=myisam).
1.在mysql中fulltext 索引只针对 myisam生效
2.mysql自己提供的fulltext针对英文生效->sphi nx (coreseek) 技术处理中文
3.使用方法是 match(字段名…) against(‘关键字’)
4.全文索引一个 叫 停止词, 因为在一个文本中,创建索引是一个无穷大的数,因此,对一些常用词和字符,就不会创建,这些词,称为停止词(如单个字符a等).
FULLTEXT KEY `title` (`title`,`content`) //全文索引
select * from articles where body like ‘%mysql%’;// 【不会使用到全文索引】
select * from articles where match(title,body) against(‘database’);// 【正确使用全文索引】
select match(title,body) against(‘database’) from articles; //查看全文匹配的匹配度(database是索引的词)
5.2.4 唯一索引
当表的某列被指定为unique约束时,这列就是一个唯一索引.
create table tbl_name(id int primary key auto_increment , name varchar(32) unique); //创建表唯一索引的sql
create table tbl_name(id int primary key auto_increment, name varchar(32)); //创建唯一索引
create unique index 索引名 on 表名 (列表..);
unique字段可以为NULL,并可以有多NULL, 但是如果是具体内容(空字串’'也不行),则不能重复.
主键字段,不能为NULL,也不能重复.
cREATE TABLE `article` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(20) ,
`idcard` varchar(32) unique, /唯一索引
`title` varchar(200) DEFAULT NULL,
`content` text,
PRIMARY KEY (`id`),//主键索引
KEY `idx_article_name` (`name`), //姓名索引
KEY `idx_article_name_idcard` (`name`,`idcard`) ,//姓名身份证号码联和索引
FULLTEXT KEY `title` (`title`,`content`) //全文索引
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
5.3 查看索引
desc tbl_name 缺点是不能显示索引名称.
***show index from tbl_name\G***查看表名,\G格式命令结果
***show keys from tbl_name***查看表中的key
索引所在目录(my.cnf配置中):
datadir=C:/ProgramData/MySQL/MySQL Server 5.7\Data
5.4 删除索引
alter table 表名 drop index 索引名; //删除所以
5.5 修改索引
先删除,后重建
5.6 索引优缺点
5.6.1 索引的代价
- 占用磁盘空间
- 对dml操作有影响,变慢
5.6.2 索引的使用场景
- 较为频繁的座位查询条件字段应该创建索引.
- 唯一性差的字段不适合单独创建索引,即使频繁作为查询条件.
- 更新非常频繁的字段不适合创建索引.
- 不会出现在where子句的字段中不该创建索引.
即:
1)肯定在where条经常使用
2)该字段的内容不是唯一的几个值(sex)
c)字段内容不是频繁变化
5.6.3 索引使用原则
查询要使用索引最重要的条件是查询条件中需要使用索引。
下列几种情况下有可能使用到索引:
- 对于创建的多列索引,只要查询条件使用了最左边的列,索引一般就会被使用。
- 对于使用like的查询,查询如果是 ‘%aaa’ 不会使用到索引,‘aaa%’ 会使用到索引。
下列的表将不使用索引:
- 如果条件中有or,即使其中有条件带索引也不会使用。
- 对于多列索引,不是使用的第一部分,则不会使用索引。
- like查询是以%开头,不能用%开头,可以在中间或者结尾.如果一定要用,则请用全文索引(支持中文的:sphinx)
- 如果列类型是字符串,那一定要在条件中将数据使用引号引用起来。否则不使用索引。(添加时,字符串必须’’)
- 如果mysql估计使用全表扫描要比使用索引快,则不使用索引。
5.6.4 查看索引使用情况
//这个值也高,说明索引的使用率越高
show status like 'handler_read_%';
结果如下:
+-----------------------+-------+
| Variable_name | Value |
+-----------------------+-------+
| Handler_read_first | 1 |
| Handler_read_key | 1 |
| Handler_read_last | 0 |
| Handler_read_next | 0 |
| Handler_read_prev | 0 |
| Handler_read_rnd | 0 |
| Handler_read_rnd_next | 21 |
+-----------------------+-------+
7 rows in set (0.01 sec)
首先7个计数器我们应该分为两部分:
5.6.4.1 对索引读的计数器:前面的5个都是对索引读情况的计数器,
Handler_read_first:是指读索引的第一项(的次数);
Handler_read_key:是指读索引的某一项(的次数);
Handler_read_next:是指读索引的下一项(的次数);
Handler_read_last:是指读索引的最后第一项(的次数);
Handler_read_prev:是指读索引的前一项(的次数);
5者应该有四种组合:
- Handler_read_first 和 Handler_read_next 组合应该是索引覆盖扫描
- Handler_read_key 基于索引取值
- Handler_read_key 和 Handler_read_next 组合应该是索引范围扫描
- Handler_read_last 和 Handler_read_prev 组合应该是索引范围扫描(orde by desc)
5.6.4.2 对数据文件的计数器:后面的2个都是对数据文件读情况的计数器,
Handler_read_rnd:
The number of requests to read a row based on a fixed position. This value is high if you are doing alot of queries that require sorting of the result. You probably have a lot of queries that require MySQL toscan entire tables or you have joins that do not use keys properly.
Handler_read_rnd_next:
The number of requests to read the next row in the data file. This value is high if you are doing a lot of
table scans. Generally this suggests that your tables are not properly indexed or that your queries are
not written to take advantage of the indexes you have.
5.6.4.3 索引使用情况总结
**handler_read_key:**这个值越高越好,越高表示使用索引查询到的次数(使用索引读很高)。
**handler_read_rnd_next和(Handler_read_rnd)**是越低越好,如果很高,这个值越高,说明查询低效.应该进行索引相关的调优。
其他的计数器,要具体情况具体分析
6. 索引与引擎
存储引擎 | 允许的索引类型 |
---|---|
MyISAM | BTREE |
InnoDB | BTREE |
MEMORY/HEAP | HASH,BTREE |
6.1 索引类型
6.1.1 B-tree索引
注: 名叫btree索引,大的方面看,都用的平衡树,但具体的实现上, 各引擎稍有不同,
比如,严格的说,NDB引擎,使用的是T-tree
Myisam,innodb中,默认用B-tree索引
但抽象一下—B-tree系统,可理解为”排好序的快速查找结构”.
6.1.2 hash索引
在memory表里,默认是hash索引, hash的理论查询时间复杂度为O(1).O(1)的意思是一次计算到指定位置.
6.1.3 常见问题
1. 问: 既然hash的查找如此高效,为什么不都用hash索引?
答:
1:hash函数计算后的结果,是随机的,如果是在磁盘上放置数据,比主键为id为例, 那么随着id的增长, id对应的行,在磁盘上随机放置.可以理解为计算位置快,但是找到位置慢.
2: 不法对范围查询进行优化.
3: 无法利用前缀索引. 比如 在btree中, field列的值“hellopworld”,并加索引,查询 xx=helloword,自然可以利用索引, xx=hello,也可以利用索引. (左前缀索引),因为hash(‘helloword’),和hash(‘hello’),两者的关系仍为随机.
4: 排序也无法优化.
5: 必须回行.就是说 通过索引拿到数据位置,必须回到表中取数据
2: btree索引的常见误区
2.1 在where条件常用的列上都加上索引
例: where cat_id=3 and price>100 ; //本意为查询第3个栏目,100元以上的商品
误: cat_id上,和, price上都加上索引.
错: 只能用上cat_id或Price索引,因为是独立的索引,同时只能用上1个.
2.2 在多列上建立索引后,查询哪个列,索引都将发挥作用
误: 多列索引上,索引发挥作用,需要满足左前缀要求.
以 index(a,b,c) 为例,
语句 索引是否发挥作用
Where a=3 是,只使用了a列
Where a=3 and b=5 是,使用了a,b列
Where a=3 and b=5 and c=4 是,使用了abc
Where b=3 / where c=4 否
Where a=3 and c=4 a列能发挥索引,c不能
Where a=3 and b>10 and c=7 A能利用,b能利用, C不能利用
同上,where a=3 and b like ‘xxxx%’ and c=7 A能用,B能用,C不能用
6.1.4 案例解析
假设某个表有一个联合索引(c1,c2,c3,c4)一下——只能使用该联合索引的c1,c2,c3部分
A where c1=x and c2=x and c4>x and c3=x
B where c1=x and c2=x and c4=x order by c3
C where c1=x and c4= x group by c3,c2
D where c1=x and c5=x order by c2,c3
E where c1=x and c2=x and c5=? order by c2,c3
A:调整c4,c3位置,用了c1,c2,c3,c4
B:c1 ,c2索引用上了,在c2用到索引的基础上,c3是排好序的,因此不用额外排序(不用Using filesort)
C:只用到c1索引,因为group by c3,c2的顺序无法利用c2,c3索引
分组统计要先按分组字段(Extra:Using temporary;),有序排列(Extra:Using filesort),
此处C会有(Extra: Using temporary;Using filesort;)
[但是如果本题改成group by c2,c3则因为c2,c3的索引,不会出现Extra: Using temporary;Using filesort;]
D:c1,然后c2,c3排序,然后在之外的c5,所以用上c1索引,c2,c3排上用场
E:c1,c2
注意如果查询条件是常量,然后根据常量排序,那么常量不用考虑索引问题.
6.2 引擎索引介绍
MyISAM的次索引和主索引 都指向物理行
InnoDB的主索引 数据和主键在一起附近.
InnoDB的次索引 指向对主键的引用,数据和主键在一起附近.
MySQL中的B-Tree索引的物理文件大多都是以BalanceTree的结构来存储的,也就是所有实际需要的数据都存放于Tree的Leaf Node(叶子节点) ,而且到任何一个Leaf Node 的最短路径的长度都是完全相同的,所以我们大家都称之为 B-Tree 索引。
当然,可能各种数据库(或 MySQL 的各种存储引擎)在存放自己的 B-Tree 索引的时候会对存储结构稍作改造。如 Innodb 存储引擎的 B-Tree 索引实际使用的存储结构实际上是 B+Tree,也就是在 B-Tree 数据结构的基础上做了很小的改造,在每一个Leaf Node 上面出了存放索引键的相关信息之外,还存储了指向与该 Leaf Node 相邻的后一个 LeafNode 的指针信息(增加了顺序访问指针),这主要是为了加快检索多个相邻 Leaf Node 的效率考虑。
参考文章:MyISAM和InnoDB的索引实现
6.2.1 MyISAM的B-Tree索引
MyISAM索引文件和数据文件是分离的,索引文件仅保存数据记录的地址。
6.2.1.1 主键索引(Primary key)
MyISAM引擎使用B+Tree作为索引结构,叶节点的data域存放的是数据记录的地址。下图是MyISAM主键索引的原理图:
6.2.1.2 辅助索引(Secondary key)
在MyISAM中,主索引和辅助索引(Secondarykey)在结构上没有任何区别,只是主索引要求key是唯一的,
而辅助索引的key可以重复。如果我们在Col2上建立一个辅助索引,则此索引的结构如下图所示:
6.2.1.3 MyISAM索引总结
同样也是一颗B+Tree,data域保存数据记录的地址。因此,MyISAM中索引检索的算法为首先按照B+Tree搜索算法搜索索引,如果指定的Key存在,则取出其data域的值,然后以data域的值为地址,读取相应数据记录。
MyISAM的索引方式也叫做“非聚集”的,之所以这么称呼是为了与InnoDB的聚集索引区分。
6.2.2 InnoDb的聚簇索引(基于B+Tree索引)
6.2.2.1 聚簇索引简介
1: 主键索引 既存储索引值,又在叶子中存储行的数据
2: 如果没有主键, 则会Unique key做主键
3: 如果没有unique,则系统生成一个内部的rowid做主键.
4: 像innodb中,主键的索引结构中,既存储了主键值,又存储了行数据,这种结构称为”聚簇索引”. 索引哟!
聚簇索引
优势: 根据主键查询条目比较少时,不用回行(数据就在主键节点下)
劣势: 如果碰到不规则数据插入时,造成频繁的页分裂.
6.2.2.2 InnoDB聚簇主键索引
MyISAM索引文件和数据文件是分离的,索引文件仅保存数据记录的地址。而在InnoDB中,表数据文件本身就是按B+Tree组织的一个索引结构,这棵树的叶节点data域保存了完整的数据记录。这个索引的key是数据表的主键,因此InnoDB表数据文件本身就是主索引.
是InnoDB主索引(同时也是数据文件)的示意图,可以看到叶节点包含了完整的数据记录。这种索引叫做聚集索引。因为InnoDB的数据文件本身要按主键聚集,所以InnoDB要求表必须有主键(MyISAM可以没有),如果没有显式指定,则MySQL系统会自动选择一个可以唯一标识数据记录的列作为主键,如果不存在这种列,则MySQL自动为InnoDB表生成一个隐含字段作为主键,这个字段长度为6个字节,类型为长整形。
6.2.2.3 InnoDB辅助索引
InnoDB的所有辅助索引都引用主键作为data域。例如,下图为定义在Col3上的一个辅助索引:
6.2.2.4 InnoDB索引总结
InnoDB 表是基于聚簇索引建立的。因此InnoDB的索引能提供一种非常快速的主键查找性能。不过,它的辅助索引(Secondary Index, 也就是非主键索引)也会包含主键列,所以,如果主键定义的比较大,其他索引也将很大。如果想在表上定义 、很多索引,则争取尽量把主键定义得小一些。InnoDB 不会压缩索引。
文字符的ASCII码作为比较准则。聚集索引这种实现方式使得按主键的搜索十分高效,但是辅助索引搜索需要检索两遍索引:首先检索辅助索引获得主键,然后用主键到主索引中检索获得记录。
不同存储引擎的索引实现方式对于正确使用和优化索引都非常有帮助,例如知道了InnoDB的索引实现后,就很容易明白1、为什么不建议使用过长的字段作为主键,因为所有辅助索引都引用主索引,过长的主索引会令辅助索引变得过大。再例如,2、用非单调的字段作为主键在InnoDB中不是个好主意,因为InnoDB数据文件本身是一颗B+Tree,非单调的主键会造成在插入新记录时数据文件为了维持B+Tree的特性而频繁的分裂调整,十分低效,而使用自增字段作为主键则是一个很好的选择。
6.2.3 MyISAM索引和InnoDb聚簇索引优缺点对比
6.2.3.1 MyISAM索引和InnoDb聚簇索引
一是主索引的区别,InnoDB的数据文件本身就是索引文件。而MyISAM的索引和数据是分开的。
二是辅助索引的区别:InnoDB的辅助索引data域存储相应记录主键的值而不是地址。而MyISAM的辅助索引和主索引没有多大区别
6.2.3.2 节点分裂
对于MyISAM而言,节点存储对物理上地址,内容较小,又缓存在内存里,分裂速度很快.
对于innodb,节点下存储了行数居,分裂的时候还要移动行数据.
聚簇索引的主键值,应尽量是连续增长的值,而不是要是随机值, (不要用随机字符串或UUID)否则会造成大量的页分裂与页移动.
高性能索引策略:
对于innodb而言,因为节点下有数据文件,因此节点的分裂将会比较慢. 对于innodb的主键,尽量用整型,而且是递增的整型.
如果是无规律的数据,将会产生的页的分裂,影响速度.
6.2.3.3 案例分析
select * from table order by id ;//慢
select * from table order by id,name; //快
里面包含有varchar(5000)这样的大数据列
因为innodb的聚簇索引:
1.是聚簇索引,导致沿着ID排序时,要跨过好多下文件快
2.有比较长的列,导致快比较多.
3.而非主键索引,而没有数据行,占用空间小,可以在内容中存储. 所以快
4.如果是Myisam则不会存在这个问题.
6.3 索引覆盖
索引覆盖是指 如果查询的列恰好是索引的一部分,那么查询只需要在索引文件上进行,不需要回行到磁盘再找数据.
这种查询速度非常快,称为”索引覆盖”
MyISAM: 根据索引找到所在节点,然后根据节点存储的磁盘编号到硬盘上回行查询数据具体内容.
但是如果根据索引查询的值就是建立索引的列.
举例:
正常查询:
表table 有字段name,给name建立索引,然后select * from talbe t where t.name=‘xxx’;
此时,先根据name的索引查找节点name(存有name的值及数据在硬盘上的数据磁盘编号),然后找到数据在硬盘上的位置,
然后在回行在磁盘查找数据.
索引覆盖:
但是如果查询select * from talbe t where t.name=‘xxx’;则索引上直接有name的结果值,
explain中会看到Extra:Using index;
6.4 案例
select * from table t limit 10000,10;//是从0取10000,然后从10000开始再取10条(默认没有查询条件时).
limit 及翻页优化
limit offset,N, 当offset非常大时, 效率极低,
原因是mysql并不是跳过offset行,然后单取N行,而是取offset+N行,返回放弃前offset行,返回N行.
效率较低,当offset越大时,效率越低
优化办法:
1: 从业务上去解决
办法: 不允许翻过100页
以百度为例,一般翻页到70页左右.
2. 不用offset,用条件查询.
不用select * from table t limit 10000,0而是用select * from table t where t.id>50000 limit 10;
3.非要物理删除,还要用offset精确查询,还不限制用户分页,怎么办?
分析: 优化思路是 不查,少查,查索引,少取.
我们现在必须要查,则只查索引,不查数据,得到id.
再用id去查具体条目. 这种技巧就是延迟索引.
mysql> select id,name from lx_com inner join (select id from lx_com limit 5000000,10) as tmp using(id);//用的 同一个表
7 索引技巧总结
7.1 伪Hash
- 伪hash-转存长字符串 crc32(char),crc32之后是一个数字;然后为该结果建立索引,索引的key_len会短很多.
一般建议在代码中hash之后再存入数据库中. 查询的时候先hash之后在到数据库中查询.
7.2 索引与排序
排序可能发生2种情况:
1: 对于覆盖索引,直接在索引上查询时,就是有顺序的, using index
2: 先取出数据,形成临时表做filesort(文件排序,但文件可能在磁盘上,也可能在内存中)
比如: goods商品表, (cat_id,shop_price)组成联合索引,
where cat_id=N order by shop_price ,可以利用索引来排序,
select goods_id,cat_id,shop_price from goods order by shop_price;
// using where,按照shop_price索引取出的结果,本身就是有序的.
select goods_id,cat_id,shop_price from goods order by click_count;
// using filesort 用到了文件排序,即取出的结果再次排序
7.3 重复索引与冗余索引
重复索引: 是指 在同1个列(如age), 或者 顺序相同的几个列(age,school), 建立了多个索引,
称为重复索引, 重复索引没有任何帮助,只会增大索引文件,拖慢更新速度, 去掉.
冗余索引:
冗余索引是指2个索引所覆盖的列有重叠, 称为冗余索引
比如 x,m,列 , 加索引 index x(x), index xm(x,m)
x,xm索引, 两者的x列重叠了, 这种情况,称为冗余索引.
甚至可以把 index mx(m,x) 索引也建立, mx, xm 也不是重复的,因为列的顺序不一样.
7.4 索引碎片与维护
在长期的数据更改过程中, 索引文件和数据文件,都将产生空洞,形成碎片.
我们可以通过一个nop操作(不产生对数据实质影响的操作), 来修改表.
比如: 表的引擎为innodb , 可以 alter table xxx engine innodb
optimize table 表名 ,也可以修复.
注意: 修复表的数据及索引碎片,就会把所有的数据文件重新整理一遍,使之对齐.
这个过程,如果表的行数比较大,也是非常耗费资源的操作.
所以,不能频繁的修复.
如果表的Update操作很频率,可以按周/月,来修复.
如果不频繁,可以更长的周期来做修复.
7.5 max,min优化技巧
select max(id) from table;//使用常量参数,很快.
select id from table use index(primary) where type = ‘100’; //强制使用主键索引
7.6 count 优化小技巧
select count() from table t where t.id < 100;
修改为如下:
select count() from table t - select count(*) from table t where t.id < 100;
7.7 强制索引
SELECT * FROM TABLE1 FORCE INDEX (FIELD1) …
7.8 理想的索引
1:查询频繁 2:区分度高 3:长度小 4: 尽量能覆盖常用查询字段.
其中区分度高和长度小是矛盾的.
确定从前确定字符重复率
select * from count(1) from dict; //16000
select * from count(distinct left(word,1)) from dict; //区分度 0.04
select * from count(distinct left(word,2)) from dict; //0.6
select * from count(distinct left(word,3)) from dict; //0.95
select * from count(distinct left(word,4)) from dict; //0.98
select * from count(distinct left(word,5)) from dict; //0.99
select * from count(distinct left(word,5)) from dict; //0.995
取 0.99即可
8. Mysql其它优化技巧
8.1 表的优化与列类型选择
8.1.1 字段类型优先级
整型 > date,time > enum,char>varchar > blob
列的特点分析:
整型: 定长,没有国家/地区之分,没有字符集的差异
time: 定长,运算快,节省空间. 考虑时区,写sql时不方便 where > ‘2005-10-12’;
enum: 能起来约束值的目的, 内部用整型来存储,但与char联查时,内部要经历串与值的转化
Char: 定长, 考虑字符集和(排序)校对集
varchar: 不定长 要考虑字符集的转换与 排序时的校对集,速度慢.
text/Blob: 无法使用内存临时表
8.1.2: 够用就行,不要慷慨 (如smallint,varchar(N))
原因: 大的字段浪费内存,影响速度,
以年龄为例 tinyint unsigned not null ,可以存储255岁,足够. 用int浪费了3个字节
以varchar(10) ,varchar(300)存储的内容相同, 但在表联查时,varchar(300)要花更多内存.
8.1.3: 尽量避免用NULL()
原因: NULL不利于索引,null也不便于查询, 要用特殊的字节来标注.
在磁盘上占据的空间其实更大.
一个字符在用explain会发现一个字符的key_len为3,而null的key_len为4(为了null多保存一个字节).
8.1.4 Enum列的说明
1: enum列在内部是用整型来储存的
2: enum列与enum列相关联速度最快
3: enum列比varchar的弱势—在碰到与char关联时,要转化. 要花时间.
4: 优势在于,当char非常长时,enum依然是整型固定长度. 当查询的数据量越大时,enum的优势越明显.
5: enum与char/varchar关联 ,因为要转化,速度要比enum->enum,char->char要慢,但有时也这样用—就是在数据量特别大时,可以节省IO.
原因–无论enum(‘manmaman’,’womanwomanwoman’) 枚举的字符多长,内部都是用整型表示, 在内存中产生的数据大小不变,而char型,却在内存中产生的数据越来越多.
总结: enum 和enum类型关联速度比较快
8.2 大批量插入数据(MySql管理员)
对于MyISAM:
禁用索引,否则会每一条都创建索引.应该全部创建完之后在创建索引.
alter table table_name disable keys;//禁用索引.
loading data //insert语句;
alter table table_name enable keys;
对于Innodb:
1.将要导入的数据按照主键排序
2.set unique_checks=0,关闭唯一性校验,否则会妹添加一条就会检查一下.(而实际导入源数据已经是唯一的原表导入的.)
3.set autocommit=0,关闭
8.3 优化group by 语句
在使用group by 分组查询时,默认分组后,还会排序,可能会降低速度.
默认情况,MySQL对所有的group by col1,col2进行排序。这与在查询中指定order by col1,col2类似。
如果查询中包括groupby但用户想要避免排序结果的消耗,则可以使用order by null禁止排序
user表中有 select name,sex from user
select count(1),sex from user group by sex; //此时会先找出某一中sex的书,然后排序统计
explaine select * from dept group by dname; =>这时显示 extra: using filesort 说明会进行排序
explaine select * from dept group by dname order by null =>这时不含有显示 extra: using filesort 说明不会进行排序
8.4 有些情况下,可以使用连接来替代子查询
因为使用join,MySQL不需要在内存中创建临时表。
select * from dept, emp where dept.deptno=emp.deptno; [简单处理方式]
select * from dept left join emp on dept.deptno=emp.deptno; [左外连接,更ok!]
8.5 不要浪费存储类型
在精度要求高的应用中,建议使用定点数来存储数值,以保证结果的准确性。deciaml 不要用float
日期类型,tinyint,int 等同理.节省存储空间.
tinyint < smallint < mediumint < int < bigint
8.6 MyISAM碎片整理
对于存储引擎是MyISAM的数据库,如果经常做删除和修改记录的操作,要定时执行optimize table table_name;功能对表进行碎片整理。delete数据之后,tbl_name.MYD大小不变. 需要碎片整理.
optimize table table_name
show database;
8.7 水平分割数据库
根据hash或者取模来分拆到不同的数据库中.
8.8 垂直分割
把某个表的某些字段,这些字段,在查询时并不是经常关心的数据,但是数据量很大,可以把这些字段单独放到另外一张表中.从而提高常用查询表的效率.
(有些表记录数并不多,可能也就2、3万条,但是字段却很长,表占用空间很大,检索表时需要执行大量I/O,严重降低了性能。这个时候需要把大的字段拆分到另一个表,并且该表与原表是一对一的关系。 (JOIN))
8.9 文件和图片存在文件服务器
文件和图片保存在专门独立的服务器保存,数据库表中只保存文件或图片的路径.
8.10 数据库参数配置
当然调整参数还是要看状态,用show status语句可以看到当前状态,以决定改调整哪些参数.常用如下:
8.10.1 内存配置
最重要的参数就是内存,分为innodb和myisam
- innodb引擎,所以下面两个参数调的很大
innodb_additional_mem_pool_size = 64M
innodb_buffer_pool_size =1G
- 对于myisam,需要调整***key_buffer_size***
8.10.2 my.cnf
如:
在my.ini修改端口(por=3306t),默认存储引擎
最大连接数(max_connections=100,默认100,一般最大并发到2000)
查询缓存大小(query_cache_size=15M)
8.11 合理的硬件资源和操作系统
如果你的机器内存超过4G,那么毋庸置疑应当采用64位操作系统和64位mysql 5.5.19 .
8.12 读写分离
读写分离,如果数据库压力很大,一台机器支撑不了,那么可以用mysql复制实现多台机器同步,将数据库的压力分散。
根据业务情况,如果插入,修改不频繁,查询频繁. 可以master为插入,修改库,slave为查询库.master数据同步到slave.
mysqlproxy来分发到符合本条数据的从库.
amoeba:以MySQL为底层数据存储,并对应用提供MySQL协议接口的proxy.sina出品.
9. 其它
9.1 观察周期性变化
通过脚本统计周期性观察以上值.确认是否为周期性压力变化.
9.2 mysql压力测试工具
tpcc,mysqlslap,Sysbench(最常用,实用)
文中内容根据网络内容总结.