Every meta key in a meta_query adds a join to wp_postmeta, and that table has one row per key per post with an index that was designed for lookup by post rather than by value.
// three joins, and a filesort
$q = new WP_Query( array(
'post_type' => 'product',
'meta_query' => array(
array( 'key' => 'in_stock', 'value' => '1' ),
array( 'key' => 'sector', 'value' => 'retail' ),
array( 'key' => 'price', 'value' => 5000, 'compare' => '<', 'type' => 'NUMERIC' ),
),
) );
// what to do instead when this is a listing page:
// a taxonomy for the categorical fields, or a custom table
The meta_value column is a longtext with a partial index, so a numeric comparison casts every row and cannot use it at all. Categorical values almost always want to be a taxonomy, which is indexed for exactly this. For anything genuinely relational the honest answer is a custom table with the columns and indexes the query needs, and accepting that it is outside the WordPress data model.