I used the test code below. I can get an accurate compass reading if I run the compass_calibrate() function first, even with the magnetometer vertical.
After commenting out the compass_calibrate() line, when moving the board around 3 axis in free space, I can see that the z value does not vary as much as x and y. So I got a small magnet. Moving that around the magnetometer makes the x,y,z values appear to change within roughly the same limits - this is a rough eyeball experiment.
Looking at the data sheet for the MAG3110 magnetometer, I can't see any indication that the 3 magnetometer axis are different. So why are the z-readings different without an external field? I hypothesise that there is a ground plane in the PCB. This is common in PCB construction. This could be acting as a shield for the z-axis.
from microbit import *
# compass.calibrate()
while True:
sleep(250)
# c = compass.heading()
x = compass.get_x()
y = compass.get_y()
z = compass.get_z()
print('x:{} y:{} z: {}'.format(x,y,z))