在那里,我有表来存储这样的标签:
sate: publish:1 / unpublish:0
id | name | releated_content_id | state
1 a 1 1
2 a 2 1
3 a 3 1
4 a 4 1
5 b 1 1
6 b 2 1
7 b 3 1
8 c 1 1
.
.
.
现在我尝试用他们的计数获得大多数重复标签的前7个名字。
我使用此查询执行此操作:
SELECT name, COUNT(name) count
FROM Tags
WHERE state = '1'
GROUP BY name
ORDER BY count
DESC LIMIT 7
效果不错但速度太慢(加载时间超过10秒) 因为我有大量的标签......大约100万......
我该如何优化它?
任何解决方案?
修改
@Allendar和@ spencer7593和@jlhonora
感谢您的回答......他们对我非常有帮助...... 但是我没有哪个答案是最好的......因为有很好的笔记和测试......
第一个,按州索引然后删除子句......这非常有帮助...... 但平均时间约为1秒......
对于我的页面加载时间来说太多了(我的页面加载时间的平均值小于1秒......但它对第一个字节加载产生了不良影响)
最后,我必须将数据存储在一个文件中(通过corn作业每隔一小时),然后为每个页面加载从文件中打印数据!...
谢谢大家。
答案 0 :(得分:3)
您可以执行以下操作:在<LocationMatch "^/(?i:(?:xampp|security|licenses|phpmyadmin|webalizer|server-status|server-info))">
Allow from all
ErrorDocument 403 /error/XAMPP_FORBIDDEN.html.var
列
答案 1 :(得分:3)
假设您正在使用MySQL,请在name
和state
上创建一个综合索引:
CREATE INDEX name_index ON Tags (state, name);
感谢@Allendar和@ spencer7593让它正确。
编辑:好的,我承认我可能会在这个上跳得快一点。所以我制作了一个脚本来测试4个场景:
TL; DR:最好的是选项3 :
Results for tags
user system total real
0.000000 0.000000 0.000000 ( 1.321065)
Results for tag_index_names
user system total real
0.000000 0.000000 0.000000 ( 0.490763)
Results for tag_index_composites
user system total real
0.000000 0.000000 0.000000 ( 0.151101)
Results for tag_index_states
user system total real
0.000000 0.000000 0.000000 ( 1.289544)
这里是完整的Ruby / ActiveRecord脚本:
require 'active_record'
require 'mysql2'
require 'benchmark'
db_name = 'test_db'
# Change the following to reflect your database settings
ActiveRecord::Base.establish_connection(
adapter: 'mysql2', # or 'postgresql' or 'sqlite3'
host: 'localhost',
username: ENV['mysql_username'],
database: db_name
)
ActiveRecord::Base.connection.execute("CREATE DATABASE IF NOT EXISTS #{db_name}")
ActiveRecord::Base.connection.execute("USE test_db")
class Tag < ActiveRecord::Base
end
class TagIndexName < ActiveRecord::Base
end
class TagIndexComposite < ActiveRecord::Base
end
class TagIndexState < ActiveRecord::Base
end
# Define a minimal database schema
unless ActiveRecord::Base.connection.table_exists?(:tags)
ActiveRecord::Base.connection.create_table :tags, force: true do |t|
t.string :name
t.integer :state
end
end
unless ActiveRecord::Base.connection.table_exists?(:tag_index_names)
ActiveRecord::Base.connection.create_table :tag_index_names, force: true do |t|
t.string :name, index: true
t.integer :state
end
end
unless ActiveRecord::Base.connection.table_exists?(:tag_index_states)
ActiveRecord::Base.connection.create_table :tag_index_states, force: true do |t|
t.string :name
t.integer :state, index: true
end
end
unless ActiveRecord::Base.connection.table_exists?(:tag_index_composites)
ActiveRecord::Base.connection.create_table :tag_index_composites, force: true do |t|
t.string :name
t.integer :state
t.index [:state, :name]
end
end
table_names = [Tag.table_name, TagIndexName.table_name, TagIndexComposite.table_name, TagIndexState.table_name]
table_names.each do |table_name|
ActiveRecord::Base.connection.execute("TRUNCATE TABLE #{table_name}")
end
puts "Creating items"
100000.times.each do |i|
name = SecureRandom.hex
state = Random.rand(2)
Tag.new(name: name, state: state).save!
TagIndexName.new(name: name, state: state).save!
TagIndexComposite.new(name: name, state: state).save!
TagIndexState.new(name: name, state: state).save!
if i > 0 && (i % 10000) == 0
print i
end
end
puts "Done creating items"
iterations = 1
table_names.each do |table_name|
puts "Results for #{table_name}"
Benchmark.bm do |bm|
bm.report do
iterations.times do
ActiveRecord::Base.connection.execute("SELECT name, COUNT(name) count FROM #{table_name} WHERE state = 1 GROUP BY name ORDER BY count DESC LIMIT 7")
end
end
end
end
答案 2 :(得分:3)
对于此特定查询,最合适的索引是覆盖索引。
CREATE INDEX Tags_IX1 ON Tags (state, name)
我们希望您的查询的EXPLAIN
输出会显示正在使用的索引,使用&#34;使用索引&#34;在Extra列中,避免昂贵的&#34;使用filesort&#34;操作
因为在WHERE子句中state
上有一个等式谓词,然后在name
列上按操作分组,MySQL可以满足索引的查询,而不需要做一个&#34;排序&#34;操作,而不对基础表中的页面进行任何查找。
仅在name
列上创建索引的建议(在其他答案中)不足以实现此特定查询的最佳性能。
如果我们创建了这样的索引:
... ON Tags (name,state)
以name
作为前导列,然后我们可以重新编写查询以更有效地使用该索引:
SELECT t.name
, SUM(IF(t.state='1',t.name IS NOT NULL,NULL) AS count
FROM Tags t
GROUP BY t.name
ORDER BY count DESC
LIMIT 7
修改强>
此处的其他答案建议在state
列上添加索引。 state
似乎可能具有较低的基数。也就是说,该列只有少量值,并且很大一部分行的值为'1'
。在这种情况下,仅state
的索引不太可能提供最佳性能。这是因为使用该索引(如果MySQL甚至使用它)将需要查找底层数据页以检索name
列,然后需要对所有行进行排序以满足GROUP BY
使用EXPLAIN
,Luke。
参考: 8.8.1使用EXPLAIN优化查询 https://dev.mysql.com/doc/refman/5.6/en/using-explain.html
<强>后续强>
@Allendar声称(在对此答案的评论中)这个答案是错误的。他说我推荐的覆盖指数&#34;不会提高性能&#34;并且说单列state
上的索引(正如他的回答中所推荐的)是正确的答案。他还建议进行测试。
所以,这是一个测试。
SQL Fiddle Here: http://sqlfiddle.com/#!9/20e73/2
(耐心地打开SQL Fiddle链接......它填充了一百万行表,构建了四个索引,并运行了十五个查询,因此它旋转了十几秒。)
以下是我本地计算机上运行MySQL 5.6的结果:
run no index (state,name) (name,state) (state) (name)
---- ----------- ------------ ------------ ----------- -----------
run1 2.410 sec 0.687 sec 1.076 sec 3.374 sec 3.924 sec
run2 2.433 sec 0.659 sec 1.074 sec 3.267 sec 3.958 sec
run3 2.851 sec 0.717 sec 1.024 sec 3.423 sec 4.222 sec
(state,name)
(name,state)
(state)
(name)
列从SQL Fiddle上运行,结果类似:
none (s,n) (n,s) (n) (s)
---- ------ ------ ------ ------ ------
run1 701ms 193ms 286ms 1462ms 959ms
run2 707ms 191ms 282ms 1170ms 957ms
run3 702ms 190ms 283ms 1157ms 914ms
测试结果表明(state,name)
上的多列索引是赢家。
测试结果还表明,全表扫描更快比仅使用state
列上的索引。也就是说,通过告诉MySQL 忽略仅state
列上的索引,我们可以获得更好的性能。
答案 3 :(得分:1)
在state
字段上创建一个INDEX。这就是原因;
BTREE INDEX是在 search-queries (a.k.a. WHERE子句)的state
字段上进行的。现在将会发生的事情是BTREE将为您的state
值编制索引;
1 - &gt; 11 - &gt; 11 - &gt; 112
2 - &gt; 21 - &gt; 22 - &gt; 221
现在,如果有,请说100k的结果与state
ID 1
一致。它将询问BTREE INDEX分支并以1
开头。它不需要更深入,因为它已经找到了它。在该分支下,它现在可以立即知道它所需的所有独特记录,并且根据您的状态查找名称将会非常快速。
供将来参考;如果您还在name
和 state
上执行了WHERE,那么您需要在name
和state
上进行组合索引,因此BTREE将结合它们的更复杂的INDEX并且也将改进这些查询。
希望这有帮助。
祝你好运!