Avançar para a área de conteúdo

Ao clicar em Enviar, você concorda com os termos e condições do developerWorks.

Na primeira vez que você efetua sign in no developerWorks, um perfil é criado para você. Informações selecionadas do seu perfil developerWorks são exibidas ao público, mas você pode editá-las a qualquer momento. Seu primeiro nome, sobrenome (a menos que escolha ocultá-los), e seu nome de exibição acompanharão o conteúdo que postar.

Todas as informações enviadas são seguras.

  • Fechar [x]

Ao se conectar ao developerWorks pela primeira vez, é criado um perfil para você e é necessário selecionar um nome de exibição. O nome de exibição acompanhará o conteúdo que você postar no developerWorks.

Escolha um nome de exibição de 3 - 31 caracteres. Seu nome de exibição deve ser exclusivo na comunidade do developerWorks e não deve ser o seu endereço de email por motivo de privacidade.

Ao clicar em Enviar, você concorda com os termos e condições do developerWorks.

Todas as informações enviadas são seguras.

  • Fechar [x]

Acelerando o Ruby on Rails

Eliminando o problema de consulta N+1

David Berube, Proprietor, Berube Consulting
David Berube
David Berube é um consultor, palestrante e o autor de Practical Rails Plugins, Practical Reporting with Ruby and Rails e Practical Ruby Gems. É possível entrar em contato com ele em info@berubeconsulting.com.

Resumo:  Ruby on Rails, uma estrutura de desenvolvimento Web popular com base na linguagem de programação Ruby, torna fácil acessar seu banco de dados, mas nem sempre o faz de maneira eficiente. Saiba mais sobre problemas comuns de desempenho com o Rails e descubra como corrigi-los.

Data:  28/Set/2010
Nível:  Intermediário
Atividade:  3230 visualizações
Comentários:  


A linguagem Ruby é frequentemente citada por sua flexibilidade. É possível, como disse Dick Sites, "escrever programas para escrever programas". O Ruby on Rails estende a linguagem principal Ruby, mas o Ruby em si torna aquela extensibilidade possível. O Ruby on Rails usa a flexibilidade da linguagem para facilitar escrever programas altamente estruturados sem muito código ou padronização extra: Você obtém uma grande quantidade de comportamento padrão sem trabalho extra. Apesar de este comportamento gratuito nem sempre ser perfeito, obtém-se muita arquitetura boa com seu aplicativo sem muito trabalho.

Por exemplo, o Ruby on Rails é baseado em um padrão Model-View-Controller (MVC), o que significa que a maioria dos aplicativos Rails é dividida de forma limpa em três partes. O modelo contém o comportamento necessário para gerenciar os dados de um aplicativo. Tipicamente, em um aplicativo Ruby on Rails, há um relacionamento 1:1 entre os modelos e as tabelas de banco de dados; ActiveRecord, o object-relation mapping (ORM) que o Ruby on Rails usa por padrão, gerencia a interação do modelo com o banco de dados, o que significa que o programa Ruby on Rails médio tem pouca, se tiver, codificação SQL. A segunda parte, a visualização, consiste no código que cria a saída enviada ao usuário; tipicamente consiste de HTML, JavaScript etc. A parte final, o controlador, transforma a entrada do usuário em chamadas para os modelos corretos e, a seguir, renderiza a resposta usando as visualizações apropriadas.

Proponentes do Rails frequentemente citam este paradigma MVC — juntamente com outros benefícios do Ruby e do Rails — como aumentando sua facilidade de uso, alegando que menos programadores podem produzir mais funcionalidade em menos tempo. Isto, é claro, significa mais valor de negócio para cada dólar de desenvolvimento de software, portanto o desenvolvimento em Ruby on Rails tornou-se significativamente mais popular.

No entanto, o custo inicial de desenvolvimento não é a figura completa. Existem outros custos contínuos, como custos de manutenção e custos de hardware para executar o aplicativo. Desenvolvedores do Ruby on Rails frequentemente usam testes e outras técnicas de desenvolvimento ágil para manter os custos de desenvolvimento baixos, mas pode ser fácil dar muito menos atenção à execução eficiente de seu aplicativo Rails com grandes quantidades de dados. Apesar de o Rails tornar fácil acessar seu banco de dados, nem sempre ele o faz de forma tão eficiente.

Por que os aplicativos Rails executam lentamente?

Aplicativos Rails podem executar lentamente por alguns motivos fundamentais. O primeiro é simples: O Rails faz pressuposições para acelerar o desenvolvimento. Normalmente, essas pressuposições são corretas e úteis. Mas elas não são sempre benéficas para o desempenho e podem resultar em um uso ineficiente de recursos — particularmente recursos de banco de dados.

Por exemplo, ActiveRecord seleciona todos os campos em uma consulta por padrão, usando uma instrução SQL equivalente a SELECT *. Em situações com um grande número de colunas — particularmente se algumas forem campos grandes VARCHAR ou BLOB — este comportamento poderá ser um problema significativo em termos de uso de memória e desempenho.

Outro desafio significativo é o problema N+1, que este artigo examina em detalhes. Essencialmente, isto resulta em várias consultas pequenas sendo realizadas, em vez de uma consulta grande. ActiveRecord não tem como saber, por exemplo, que um registro filho está sendo solicitado para cada conjunto de registros pai, portanto ele produzirá uma consulta de registro filho para cada registro pai. Por causa do custo adicional por consulta, este comportamento pode causar problemas significativos de desempenho.

Outros desafios estão relacionados mais intimamente a hábitos e atitudes de desenvolvimento de desenvolvedores de Ruby on Rails. Como o ActiveRecord facilita tantas tarefas, os desenvolvedores Rails podem muitas vezes criar um pensamento de que "SQL é ruim", desprezando o SQL mesmo quando seu uso faz mais sentido. Criar e manipular grandes quantidades de objetos ActiveRecord pode ser lento, portanto, em alguns casos, poderá ser muito mais rápido escrever uma consulta SQL diretamente que não instancie nenhum objeto.

Como o Ruby on Rails é frequentemente usado para reduzir o tamanho de equipes de desenvolvimento, e como os desenvolvedores de Ruby on Rails frequentemente realizam algumas das tarefas de administração de sistemas necessárias para implementar e manter seus aplicativos em produção, o conhecimento limitado sobre seu ambiente poderá causar problemas. Configurações de sistema operacional e banco de dados poderão não ser definidos corretamente. Apesar de não serem ideais, as configurações MySQL my.cnf são frequentemente deixadas como padrão em implementações do Ruby on Rails, por exemplo. Adicionalmente, poderá não haver ferramentas suficientes de monitoração e avaliação de desempenho para desenvolver um panorama de desempenho em longo prazo. Esta não é uma crítica aos desenvolvedores Ruby on Rails, é claro; é simplesmente uma consequência na falta de especialização; em alguns casos, desenvolvedores do Rails podem ser especialistas em ambas as áreas.

Uma questão final é que o Ruby on Rails encoraja programadores a desenvolver em um ambiente local. Fazer isto tem uma série de benefícios — como menor latência de desenvolvimento e maior distribuição — mas isto significa que é possível trabalhar com um conjunto de dados limitado por causa do tamanho menor das estações de trabalho. A diferença entre como eles desenvolvem e onde o código será implementado pode ser um grande problema. Pode ocorrer de trabalhar por muito tempo com um tamanho de dados pequeno em um servidor local sem carga com bom desempenho para, no final, descobrir que o aplicativo tem problemas significativos de desempenho com um tamanho maior de dados em um servidor congestionado.

É claro, existem muitos outros motivos possível para explicar porque um aplicativo Rails tem problemas de desempenho. A melhor forma de descobrir que problemas potenciais de desempenho seu aplicativo Rails tem é verificando as ferramentas de diagnóstico que podem oferecer medições precisas e repetíveis.


Detectando problemas de desempenho

Uma das melhores ferramentas é o registro de desenvolvimento do Rails, que reside em cada máquina de desenvolvimento no arquivo de log development/log. Ela tem várias métricas brutas disponíveis: tempo total gasto para responder à solicitação, porcentagem de tempo gasto no banco de dados, porcentagem do tempo gasto gerando a visualização etc. Estão disponíveis ferramentas para analisar o arquivo de log, como o development-log-analyzer.

Durante a produção, é possível encontrar informações valiosas examinando o mysql_slow_log. Os detalhes completos estão fora do escopo desta discussão, mas é possível saber mais na seção Recursos.

Uma das ferramentas mais poderosas e úteis é o plug-in query_reviewer (consulte Recursos). Este plug-in mostra quantas consultas estão sendo executadas na página e quanto tempo a página leva para ser gerada. E analisa automaticamente código SQL que o ActiveRecord gera para problemas potenciais. Por exemplo, ele encontra consultas que não usam um índice MySQL, portanto, se você tiver esquecido de indexar uma coluna importante e isto estiver causando problemas de desempenho, é possível encontrá-lo facilmente (consulte Recursos para obter mais informações sobre índices MySQL). O plug-in exibe todas essas informações em um pop-up <div>, que é visível somente durante o modo de desenvolvimento.

Finalmente, não se esqueça de usar ferramentas como Firebug, yslow, Ping e tracert para detectar se seus problemas de desempenho vêm de problemas de carga de ativos ou de rede.

A seguir, lidaremos com alguns problemas específicos do Rails e suas soluções.


O problema da consulta N+1

O problema da consulta N+1 é um dos maiores problemas com aplicativos Rails. Por exemplo, quantas consultas o código na Listagem 1 produz? Este código é um ciclo simples através de todos os tópicos em uma tabela hipotética de tópicos, exibindo a categoria e o corpo do tópico.


Listagem 1. Código Post.all não otimizado

<%@posts = Post.all(@posts).each do |p|%> 
	<h1><%=p.category.name%></h1>
	<p><%=p.body%></p>
<%end%>
 

Resposta: O código gera uma consulta mais uma consulta por linha em @posts. Por causa do custo adicional por consulta, este pode ser um desafio significativo. A culpada é a chamada para p.category.name. Esta chamada se aplica somente àquele objeto particular do tópico, não à array @posts inteira. Felizmente, é possível corrigir isto usando carga antecipada.

Carga antecipada significa que o Rails realizará automaticamente as consultas necessárias para carregar o objeto com quaisquer objetos filhos especificados. O Rails usará uma instrução SQL JOIN ou uma estratégia em que várias consultas são realizadas. No entanto, presumindo que todos os filhos que forem usados sejam especificados, isto nunca resultará em uma situação N+1, onde cada iteração em um ciclo produz uma consulta adicional. A Listagem 2 é uma versão do código da Listagem 1 que usa carga antecipada para evitar o problema N+1.


Listagem 2. Código Post.all otimizado com carga antecipada
	
<%@posts = Post.find(:all, :include=>[:category]
	@posts.each do |p|%> 
	<h1><%=p.category.name%></h1>
	<p><%=p.body%></p>
<%end%>

Aquele código gera no máximo duas consultas, não importa quantas colunas existam na tabela tópicos.

É claro, nem todos os casos são tão simples. É mais trabalhoso lidar com situações de consulta N+1 mais complicadas. Vale a pena o esforço? Vamos fazer um rápido teste.


Testando N+1

Usando o script na Listagem 3, é possível descobrir o quão lentas — ou rápidas — as consultas podem ser. A Listagem 3 demonstra como usar o ActiveRecord em um script independente para estabelecer uma conexão com o banco de dados, definir suas tabelas e carregar dados. A seguir, a biblioteca integrada de avaliação de desempenho do Ruby é usada para ver qual abordagem é mais rápida e o quanto mais rápida ela é.


Listagem 3. Script de referência de carga antecipada

require 'rubygems'
require 'faker'
require 'active_record'
require 'benchmark'

# This call creates a connection to our database.

ActiveRecord::Base.establish_connection(
	:adapter  => "mysql",
	:host     => "127.0.0.1",
	:username => "root", # Note that while this is the default setting for MySQL,
	:password => "",     # a properly secured system will have a different MySQL
                            # username and password, and if so, you'll need to
                            # change these settings.
	:database => "test")

# First, set up our database...	
class Category <  ActiveRecord::Base
end

unless Category.table_exists?
	ActiveRecord::Schema.define do
		create_table :categories do |t|
				t.column :name, :string
		end
	end	
end

Category.create(:name=>'Sara Campbell\'s Stuff')
Category.create(:name=>'Jake Moran\'s Possessions')
Category.create(:name=>'Josh\'s Items')
number_of_categories = Category.count

class Item <  ActiveRecord::Base	
	belongs_to :category	
end

# If the table doesn't exist, we'll create it.

unless Item.table_exists?
	ActiveRecord::Schema.define do
		create_table :items do |t|
				t.column :name, :string
				t.column :category_id, :integer	
		end
	end	
end

puts "Loading data..."

item_count = Item.count
item_table_size = 10000

if item_count < item_table_size
	(item_table_size - item_count).times do
		Item.create!(:name=>Faker.name, 
                 :category_id=>(1+rand(number_of_categories.to_i)))
	end
end

puts "Running tests..."

Benchmark.bm do |x| 
	[100,1000,10000].each do |size|	
		x.report "size:#{size}, with n+1 problem" do 
			@items=Item.find(:all, :limit=>size)
			@items.each do |i| 
				i.category
			end	
		end	
		x.report "size:#{size}, with :include" do 
			@items=Item.find(:all, :include=>:category, :limit=>size)
			@items.each do |i| 
				i.category
			end	
		end	
	end	
end

Este script esta a velocidade de realização de ciclos em 100, 1.000 e 10.000 objetos com e sem carga antecipada usando a cláusula :include. Para executar este script, poderá ser preciso substituir os parâmetros apropriados de conexão com o banco de dados, próximos à parte superior do script, por parâmetros adequados a seu ambiente local. Também será preciso criar um banco de dados MySQL chamado test. Finalmente, será preciso os gems ActiveRecord e faker, que podem ser obtidos executando gem install activerecord faker.

Executar o script em minha máquina produziu os resultados mostrados na Listagem 4.


Listagem 4. Saída do script de referência de carga antecipada

-- create_table(:categories)
	 -> 0.1327s
-- create_table(:items)
	 -> 0.1215s
Loading data...
Running tests...
			user     system      total        real
size:100, with n+1 problem  0.030000   0.000000   0.030000 (  0.045996)
size:100, with :include  0.010000   0.000000   0.010000 (  0.009164)
size:1000, with n+1 problem  0.260000   0.040000   0.300000 (  0.346721)
size:1000, with :include  0.060000   0.010000   0.070000 (  0.076739)
size:10000, with n+1 problem  3.110000   0.380000   3.490000 (  3.935518)
size:10000, with :include  0.470000   0.080000   0.550000 (  0.573861)

Em todos os casos, o teste usando :include foi mais rápido — especificamente, 5,02, 4,52 e 6,86 vezes mais rápido, respectivamente. É claro, o resultado exato depende de sua situação particular, mas a carga antecipada pode claramente levar a ganhos de desempenho significativos.


Carga antecipada aninhada

E se você quiser referenciar uma relação aninhada — uma relação de uma relação? A Listagem 5 demonstra uma situação comum onde tal coisa poderá acontecer: o ciclo por todos os tópicos com exibição da imagem do autor, onde o Author tem um relacionamento belongs_to com Image.


Listagem 5. Caso de uso de carga antecipada aninhada

@posts = Post.all	
@posts.each do |p|  
	<h1><%=p.category.name%></h1>
	<%=image_tag p.author.image.public_filename %> 
	<p><%=p.body%> 
 <%end%> 
 

Este código sofre do mesmo problema N+1 que antes, mas a sintaxe para correção não é imediatamente aparente, pois estão sendo usados relacionamentos de relacionamentos. Como, então, são feitas cargas antecipadas de relacionamentos aninhados?

A resposta correta é usar uma sintaxe de hash para a cláusula :include. A Listagem 6 fornece um exemplo de tal carga antecipada aninhada usando hashes.


Listagem 6. Solução de carga antecipada aninhada
	
@posts = Post.find(:all, :include=>{ :category=>[],
                                       :author=>{ :image=>[]}} )
@posts.each do |p|  
	<h1><%=p.category.name%></h1>
	<%=image_tag p.author.image.public_filename %> 
	<p><%=p.body%> 
 <%end%> 

Como pode-se ver, é possível aninhar literais de hash e array. Note que a única diferença entre um hash e uma array, neste caso, é que o hash pode ter subitens aninhados, e a matriz não. Com exceção disso, eles são equivalentes.


Carga antecipada indireta

Nem todas as instâncias do problema N+1 são tão facilmente percebidas. Por exemplo, quantas consultas a Listagem 7 produz?


Listagem 7. Caso de uso de carga antecipada indireta

  <%@user = User.find(5)
    @user.posts.each do |p|%>   
      <%=render :partial=>'posts/summary',  :locals=>:post=>p
     %> <%end%>
 

É claro, determinar o número de consultas requer conhecimento do posts/summary parcial. É possível ver uma parcial na Listagem 8.


Listagem 8. Carga antecipada indireta parcial: posts/_summary.html.erb

  <h1><%=post.user.name%></h1>

Infelizmente, a resposta é que a Listagem 7 e a Listagem 8 geram uma consulta extra por linha em post, buscando o nome do usuário — apesar de o objeto post ter sido gerado automaticamente por ActiveRecord a partir de um objeto User já em memória. Em resumo, o Rails ainda não associa registros filhos a seus pais.

A correção é usar carga antecipada autorreferencial. Essencialmente, como o Rails recarrega registros filhos gerados por registros pais, é preciso carregar antecipadamente os registros pais como se fossem um relacionamento totalmente separado. Ele se parece com o código na Listagem 9.


Listagem 9. Solução de carga antecipada indireta
	
  <%@user = User.find(5, :include=>{:posts=>[:user]})
  ...snip...

Apesar de não ser intuitiva, esta técnica funciona de forma muito parecida com as técnicas acima. Infelizmente, é fácil aninhar de forma excessiva usando esta técnica, particularmente se for uma hierarquia complicada. Casos de uso simples são fáceis, como o mostrado na Listagem 9, mas aninhamentos pesados poderão causar problemas. Em alguns casos, a carga excessiva de objetos Ruby poderá, de fato, ser mais lenta do que lidar com o problema N+1 — particularmente se algum dos objetos não tiver toda a árvore percorrida. Naquele caso, outras soluções para o problema N+1 poderão ser mais adequadas.

Uma forma de fazer isto é usando técnicas de armazenamento em cache. O Rails V2.1 tem acesso a cache simples integrado. Usando o Rails.cache.read, Rails.cache.write e métodos relacionados, é possível criar seu próprio mecanismo de armazenamento em cache facilmente, e o backend poderá ser um backend simples de memória, um backend baseado em cache ou um servidor com cache de memória. Saiba mais sobre o suporte a armazenamento de cache integrado do Rails na seção Recursos. No entanto, não é preciso criar sua própria solução de armazenamento em cache; é possível usar um plug-in pré-construído do Rails, como o plug-in cache money de Nick Kallen. Este plug-in fornece armazenamento de cache com gravação e é baseado no código em uso pelo Twitter. Consulte Recursos para mais informações.

É claro, nem todos os problemas do Rails são relacionados ao número de consultas.


Cálculos agregados e agrupamento no Rails

Um problema que poderá ser encontrado envolve trabalho no Ruby que deveria ser feito pelo banco de dados. Esta é uma prova do quão poderoso o Ruby é. É difícil imaginar pessoal reimplementando voluntariamente partes de seus códigos de banco de dados em C sem um incentivo significativo, mas é fácil fazer cálculos similares em grupos de objetos ActiveRecord no Rails. Infelizmente, o Ruby é invariavelmente mais lento que o código de seu banco de dados. Não realize cálculos usando uma abordagem de Ruby puro, como mostrado na Listagem 10.


Listagem 10. Forma incorreta de realizar cálculos de agrupamento

  all_ages = Person.find(:all).group_by(&:age).keys.uniq
  oldest_age = Person.find(:all).max

Em vez disso, o Rails fornece uma série de funções agregadas e de agrupamento. Use-as como mostrado na Listagem 11.


Listagem 11. Forma correta de realizar cálculos de agrupamento

  all_ages = Person.find(:all, :group=>[:age])  
  oldest_age = Person.calcuate(:max, :age)

Há várias opções para ActiveRecord::Base#find que podem ser usadas para imitar o SQL. Saiba mais na documentação do Rails. Note que o método calculate funciona com qualquer função agregada válida que seu banco de dados suporta, como :min, :sum e :avg. Adicionalmente, calculate pode usar uma série de argumentos, como :conditions. Consulte a documentação do Rails para obter detalhes.

Mas nem tudo o que é possível fazer no SQL pode ser feito no Rails. Se os elementos integrados não forem suficientes, use SQL personalizado.


SQL personalizado com Rails

Suponha que haja uma tabela com uma lista de pessoas, suas profissões, idades e o número de acidentes nos quais estiveram envolvidas no último ano. É possível usar uma instrução SQL personalizada para recuperar as informações, como mostrado na Listagem 12.


Listagem 12. SQL personalizado com ActiveRecord exemplo

sql = "SELECT profession,
              AVG(age) as average_age,  
              AVG(accident_count) 
         FROM persons 
        GROUP 
           BY profession"

Person.find_by_sql(sql).each do |row|   
  puts "#{row.profession}, " <<
       "avg. age: #{row.average_age}, " <<
       "avg. accidents: #{row.average_accident_count}"
end

Este script produziria resultados como os da Listagem 13.


Listagem 13. SQL personalizado com ActiveRecord saída

  Programmer, avg. age: 18.010, avg. accidents: 9
  System Administrator, avg. age: 22.720, avg. accidents: 8

É claro, este é um caso simples. Pode-se imaginar, no entanto, como é possível estender este exemplo para instruções SQL de qualquer complexidade. É possível executar também outros tipos de instruções SQL, como instruções ALTER TABLE, usando o método ActiveRecord::Base.connection.execute, como mostra a Listagem 14.


Listagem 14. SQL personalizado não buscador com ActiveRecord

  ActiveRecord::Base.connection.execute "ALTER TABLE some_table CHANGE COLUMN..."

A maioria das manipulações de esquema, como adicionar e remover colunas, pode ser feita usando os métodos integrados do Rails. Mas a habilidade de executar código SQL arbitrário está disponível, se necessário.


Conclusão

Como todas as estruturas de trabalho, o Ruby on Rails pode sofrer com problemas de desempenho sem o cuidado e a atenção adequados. Felizmente, as técnicas corretas para monitorar e corrigir estes desafios são relativamente simples e fáceis de aprender, e mesmo problemas complexos podem ser solucionados com alguma paciência e o conhecimento da origem dos problemas de desempenho.


Recursos

Aprender

Obter produtos e tecnologias

Discutir

Sobre o autor

David Berube

David Berube é um consultor, palestrante e o autor de Practical Rails Plugins, Practical Reporting with Ruby and Rails e Practical Ruby Gems. É possível entrar em contato com ele em info@berubeconsulting.com.

Ajuda para Relatar Abuso

Relatar abuso

Obrigado. Esta entrada foi sinalizada para atenção do moderador.


Ajuda para Relatar Abuso

Relatar abuso

Falha no envio do Relatório de abuso. Tente novamente mais tarde.


developerWorks: Registre-se


Precisa de um ID IBM?
Esqueceu seu ID IBM?


Esqueceu sua senha?
Alterar sua senha

Ao clicar em Enviar, você concorda com os termos de uso do developerWorks.

 


Na primeira vez que você efetua sign in no developerWorks, um perfil é criado para você. Informações selecionadas do seu perfil developerWorks são exibidas ao público, mas você pode editá-las a qualquer momento. Seu primeiro nome, sobrenome (a menos que escolha ocultá-los), e seu nome de exibição acompanharão o conteúdo que postar.

Selecione seu nome de exibição

Ao se conectar ao developerWorks pela primeira vez, é criado um perfil para você e é necessário selecionar um nome de exibição. O nome de exibição acompanhará o conteúdo que você postar no developerWorks.

Escolha um nome de exibição de 3 - 31 caracteres. Seu nome de exibição deve ser exclusivo na comunidade do developerWorks e não deve ser o seu endereço de email por motivo de privacidade.

(Deve possuir de 3 a 31 caracteres.)


Ao clicar em Enviar, você concorda com os termos de uso do developerWorks.

 


Classificar este artigo

Comentários

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=80
Zone=Software livre
ArticleID=507689
ArticleTitle=Acelerando o Ruby on Rails
publish-date=09282010
author1-email=info@berubeconsulting.com
author1-email-cc=

Conheça a IBM da sua cidade

Virtual Branch Office Brasil

A IBM está mais perto do que você imagina!


Tags

Help
Use o campo de pesquisa para encontrar todos os tipos de conteúdo no My developerWorks com essa tag.

Use a barra de rolagem para ver mais ou menos tags.

Tags populares mostra as principais tags para esta zona de conteúdo em particular (por exemplo, Java technology, Linux, WebSphere).

Minhas tags mostra suas tags para esta zona de conteúdo em particular (por exemplo, Java technology, Linux, WebSphere).

Use o campo de pesquisa para localizar todos os tipos de conteúdo no Meu developerWorks com essa tag. Tags populares mostra as tags principais para essa zona de conteúdo particular (por exemplo, tecnologia Java, Linux, WebSphere). My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere). Minhas tags mostra as suas tags para essa zona de conteúdo em particular (por exemplo, tecnologia Java, Linux, WebSphere).